From d4eff081774475a80b140dc554bb3ae6ebf121b3 Mon Sep 17 00:00:00 2001 From: kylezs Date: Tue, 29 Oct 2024 12:22:51 +0100 Subject: [PATCH 1/3] Revert "fix: expire all previous epochs (#5279)" (#5362) This reverts commit 5baa3dd3edd280893967c8e051efec16c7c6e1e7. --- .../cf-integration-tests/src/authorities.rs | 22 +++---- .../cf-integration-tests/src/broadcasting.rs | 4 +- .../cf-integration-tests/src/funding.rs | 16 ++--- .../cf-integration-tests/src/genesis.rs | 8 +-- .../cf-integration-tests/src/mock_runtime.rs | 10 +-- .../cf-integration-tests/src/network.rs | 2 +- .../cf-integration-tests/src/new_epoch.rs | 8 +-- .../cf-integration-tests/src/solana.rs | 16 ++--- .../cf-integration-tests/src/swapping.rs | 6 +- .../cf-integration-tests/src/witnessing.rs | 6 +- state-chain/custom-rpc/src/lib.rs | 29 ++++++-- state-chain/custom-rpc/src/monitoring.rs | 44 +------------ state-chain/node/src/chain_spec.rs | 4 +- state-chain/pallets/cf-validator/src/lib.rs | 45 +++++-------- .../pallets/cf-validator/src/migrations.rs | 4 +- .../src/migrations/rename_blocks_per_epoch.rs | 66 ------------------- state-chain/pallets/cf-validator/src/mock.rs | 2 +- state-chain/pallets/cf-validator/src/tests.rs | 59 ++++------------- state-chain/runtime/src/lib.rs | 8 +-- state-chain/runtime/src/monitoring_apis.rs | 2 +- state-chain/runtime/src/runtime_apis.rs | 2 +- 21 files changed, 116 insertions(+), 247 deletions(-) delete mode 100644 state-chain/pallets/cf-validator/src/migrations/rename_blocks_per_epoch.rs diff --git a/state-chain/cf-integration-tests/src/authorities.rs b/state-chain/cf-integration-tests/src/authorities.rs index e27af1d5cf..0307f015dd 100644 --- a/state-chain/cf-integration-tests/src/authorities.rs +++ b/state-chain/cf-integration-tests/src/authorities.rs @@ -59,10 +59,10 @@ pub fn fund_authorities_and_join_auction( /// by going through the correct sequence in sync. #[test] fn authority_rotates_with_correct_sequence() { - const EPOCH_DURATION_BLOCKS: u32 = 1000; + const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -162,7 +162,7 @@ fn authorities_earn_rewards_for_authoring_blocks() { // set const MAX_AUTHORITIES: AuthorityCount = 3; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -206,7 +206,7 @@ fn genesis_nodes_rotated_out_accumulate_rewards_correctly() { // set const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -273,7 +273,7 @@ fn authority_rotation_can_succeed_after_aborted_by_safe_mode() { const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -328,7 +328,7 @@ fn authority_rotation_cannot_be_aborted_after_key_handover_and_completes_even_on const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -371,7 +371,7 @@ fn authority_rotation_can_recover_after_keygen_fails() { const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -424,7 +424,7 @@ fn authority_rotation_can_recover_after_key_handover_fails() { const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -502,7 +502,7 @@ fn can_move_through_multiple_epochs() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -524,7 +524,7 @@ fn cant_rotate_if_previous_rotation_is_pending() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -559,7 +559,7 @@ fn waits_for_governance_when_apicall_fails() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { diff --git a/state-chain/cf-integration-tests/src/broadcasting.rs b/state-chain/cf-integration-tests/src/broadcasting.rs index 5b4754ba64..4f1b61210f 100644 --- a/state-chain/cf-integration-tests/src/broadcasting.rs +++ b/state-chain/cf-integration-tests/src/broadcasting.rs @@ -12,10 +12,10 @@ use state_chain_runtime::{ #[test] fn bitcoin_broadcast_delay_works() { - const EPOCH_DURATION_BLOCKS: u32 = 200; + const EPOCH_BLOCKS: u32 = 200; const MAX_AUTHORITIES: AuthorityCount = 150; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { diff --git a/state-chain/cf-integration-tests/src/funding.rs b/state-chain/cf-integration-tests/src/funding.rs index 68e793d5a6..00a8997198 100644 --- a/state-chain/cf-integration-tests/src/funding.rs +++ b/state-chain/cf-integration-tests/src/funding.rs @@ -22,10 +22,10 @@ use state_chain_runtime::{ // We have a set of nodes that are funded and can redeem in the redeeming period and // not redeem when out of the period fn cannot_redeem_funds_out_of_redemption_period() { - const EPOCH_DURATION_BLOCKS: u32 = 100; + const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 3; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -68,7 +68,7 @@ fn cannot_redeem_funds_out_of_redemption_period() { } let end_of_redemption_period = - EPOCH_DURATION_BLOCKS * REDEMPTION_PERIOD_AS_PERCENTAGE as u32 / 100; + EPOCH_BLOCKS * REDEMPTION_PERIOD_AS_PERCENTAGE as u32 / 100; // Move to end of the redemption period System::set_block_number(end_of_redemption_period + 1); // We will try to redeem @@ -125,7 +125,7 @@ fn cannot_redeem_funds_out_of_redemption_period() { fn funded_node_is_added_to_backups() { const EPOCH_BLOCKS: u32 = 10_000_000; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) // As we run a rotation at genesis we will need accounts to support // having 5 authorities as the default is 3 (Alice, Bob and Charlie) .accounts(vec![ @@ -149,7 +149,7 @@ fn backup_reward_is_calculated_linearly() { const MAX_AUTHORITIES: u32 = 10; const NUM_BACKUPS: u32 = 20; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -194,7 +194,7 @@ fn can_calculate_account_apy() { const MAX_AUTHORITIES: u32 = 10; const NUM_BACKUPS: u32 = 20; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -246,7 +246,7 @@ fn apy_can_be_above_100_percent() { const MAX_AUTHORITIES: u32 = 2; const NUM_BACKUPS: u32 = 2; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -282,7 +282,7 @@ fn backup_rewards_event_gets_emitted_on_heartbeat_interval() { const NUM_BACKUPS: u32 = 20; const MAX_AUTHORITIES: u32 = 100; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .accounts( (0..MAX_AUTHORITIES as u8) .map(|i| (AccountId32::from([i; 32]), AccountRole::Validator, GENESIS_BALANCE)) diff --git a/state-chain/cf-integration-tests/src/genesis.rs b/state-chain/cf-integration-tests/src/genesis.rs index 231e3b88c3..94ed0b3828 100644 --- a/state-chain/cf-integration-tests/src/genesis.rs +++ b/state-chain/cf-integration-tests/src/genesis.rs @@ -14,7 +14,7 @@ use state_chain_runtime::{ }; pub const GENESIS_BALANCE: FlipBalance = TOTAL_ISSUANCE / 100; -const EPOCH_DURATION: u32 = 1000; +const BLOCKS_PER_EPOCH: u32 = 1000; pub fn with_test_defaults() -> ExtBuilder { ExtBuilder::default() @@ -26,7 +26,7 @@ pub fn with_test_defaults() -> ExtBuilder { (AccountId::from(LIQUIDITY_PROVIDER), AccountRole::LiquidityProvider, GENESIS_BALANCE), ]) .root(AccountId::from(ERIN)) - .epoch_duration(EPOCH_DURATION) + .blocks_per_epoch(BLOCKS_PER_EPOCH) } #[test] @@ -62,8 +62,8 @@ fn state_of_genesis_is_as_expected() { } assert_eq!( - Validator::epoch_duration(), - EPOCH_DURATION, + Validator::blocks_per_epoch(), + BLOCKS_PER_EPOCH, "epochs will not rotate automatically from genesis" ); diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 41a70ad56d..56251afa19 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -67,7 +67,7 @@ use cf_primitives::{ pub struct ExtBuilder { pub genesis_accounts: Vec<(AccountId, AccountRole, FlipBalance)>, root: Option, - epoch_duration: BlockNumber, + blocks_per_epoch: BlockNumber, max_authorities: AuthorityCount, min_authorities: AuthorityCount, } @@ -79,7 +79,7 @@ impl Default for ExtBuilder { min_authorities: 1, genesis_accounts: Default::default(), root: Default::default(), - epoch_duration: Default::default(), + blocks_per_epoch: Default::default(), } } } @@ -103,8 +103,8 @@ impl ExtBuilder { self } - pub fn epoch_duration(mut self, epoch_duration: BlockNumber) -> Self { - self.epoch_duration = epoch_duration; + pub fn blocks_per_epoch(mut self, blocks_per_epoch: BlockNumber) -> Self { + self.blocks_per_epoch = blocks_per_epoch; self } @@ -184,7 +184,7 @@ impl ExtBuilder { }) .collect(), genesis_backups: Default::default(), - epoch_duration: self.epoch_duration, + blocks_per_epoch: self.blocks_per_epoch, bond: self .genesis_accounts .iter() diff --git a/state-chain/cf-integration-tests/src/network.rs b/state-chain/cf-integration-tests/src/network.rs index 8ac59a2d6b..8b03627874 100644 --- a/state-chain/cf-integration-tests/src/network.rs +++ b/state-chain/cf-integration-tests/src/network.rs @@ -691,7 +691,7 @@ impl Network { /// Move to the last block of the epoch - next block will start Authority rotation pub fn move_to_the_end_of_epoch(&mut self) { let current_block = System::block_number(); - let target = Validator::current_epoch_started_at() + Validator::epoch_duration(); + let target = Validator::current_epoch_started_at() + Validator::blocks_per_epoch(); if target > current_block { self.move_forward_blocks(target - current_block - 1) } diff --git a/state-chain/cf-integration-tests/src/new_epoch.rs b/state-chain/cf-integration-tests/src/new_epoch.rs index f5161b16d5..932bc06447 100644 --- a/state-chain/cf-integration-tests/src/new_epoch.rs +++ b/state-chain/cf-integration-tests/src/new_epoch.rs @@ -17,9 +17,9 @@ use state_chain_runtime::{ #[test] fn auction_repeats_after_failure_because_of_liveness() { - const EPOCH_DURATION_BLOCKS: BlockNumber = 1000; + const EPOCH_BLOCKS: BlockNumber = 1000; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) // As we run a rotation at genesis we will need accounts to support // having 5 authorities as the default is 3 (Alice, Bob and Charlie) .accounts(vec![ @@ -107,7 +107,7 @@ fn epoch_rotates() { const EPOCH_BLOCKS: BlockNumber = 1000; const MAX_SET_SIZE: AuthorityCount = 5; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .min_authorities(MAX_SET_SIZE) .build() .execute_with(|| { @@ -247,7 +247,7 @@ fn can_consolidate_bitcoin_utxos() { const MAX_AUTHORITIES: AuthorityCount = 5; const CONSOLIDATION_SIZE: u32 = 2; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .build() .execute_with(|| { let (mut testnet, _, _) = diff --git a/state-chain/cf-integration-tests/src/solana.rs b/state-chain/cf-integration-tests/src/solana.rs index b1b521c7be..e4aa87e81b 100644 --- a/state-chain/cf-integration-tests/src/solana.rs +++ b/state-chain/cf-integration-tests/src/solana.rs @@ -149,10 +149,10 @@ fn schedule_deposit_to_swap( #[test] fn can_build_solana_batch_all() { - const EPOCH_DURATION_BLOCKS: u32 = 100; + const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), @@ -225,7 +225,7 @@ fn can_rotate_solana_vault() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -284,7 +284,7 @@ fn can_send_solana_ccm() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), @@ -363,7 +363,7 @@ fn solana_ccm_fails_with_invalid_input() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), @@ -528,7 +528,7 @@ fn failed_ccm_does_not_consume_durable_nonce() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), @@ -590,7 +590,7 @@ fn solana_resigning() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), @@ -666,7 +666,7 @@ fn solana_ccm_execution_error_can_trigger_fallback() { const EPOCH_BLOCKS: u32 = 100; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .with_additional_accounts(&[ (DORIS, AccountRole::LiquidityProvider, 5 * FLIPPERINOS_PER_FLIP), diff --git a/state-chain/cf-integration-tests/src/swapping.rs b/state-chain/cf-integration-tests/src/swapping.rs index f9cc1e5310..180b5b798d 100644 --- a/state-chain/cf-integration-tests/src/swapping.rs +++ b/state-chain/cf-integration-tests/src/swapping.rs @@ -767,10 +767,10 @@ fn ethereum_ccm_can_calculate_gas_limits() { #[test] fn can_resign_failed_ccm() { - const EPOCH_DURATION_BLOCKS: u32 = 1000; + const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -882,7 +882,7 @@ fn can_handle_failed_vault_transfer() { const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 10; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { diff --git a/state-chain/cf-integration-tests/src/witnessing.rs b/state-chain/cf-integration-tests/src/witnessing.rs index 38cb8884d6..c29545c429 100644 --- a/state-chain/cf-integration-tests/src/witnessing.rs +++ b/state-chain/cf-integration-tests/src/witnessing.rs @@ -16,10 +16,10 @@ use pallet_cf_witnesser::{CallHash, CallHashExecuted, WitnessDeadline}; #[test] fn can_punish_failed_witnesser() { - const EPOCH_DURATION_BLOCKS: u32 = 1000; + const EPOCH_BLOCKS: u32 = 1000; const MAX_AUTHORITIES: AuthorityCount = 50; super::genesis::with_test_defaults() - .epoch_duration(EPOCH_DURATION_BLOCKS) + .blocks_per_epoch(EPOCH_BLOCKS) .max_authorities(MAX_AUTHORITIES) .build() .execute_with(|| { @@ -46,7 +46,7 @@ fn can_punish_failed_witnesser() { assert_ok!(Reputation::set_penalty( pallet_cf_governance::RawOrigin::GovernanceApproval.into(), Offence::FailedToWitnessInTime, - Penalty { reputation: -100, suspension: EPOCH_DURATION_BLOCKS }, + Penalty { reputation: -100, suspension: EPOCH_BLOCKS }, )); // Before the deadline is set, no one has been reported. diff --git a/state-chain/custom-rpc/src/lib.rs b/state-chain/custom-rpc/src/lib.rs index da9083e12a..c0f6fe8305 100644 --- a/state-chain/custom-rpc/src/lib.rs +++ b/state-chain/custom-rpc/src/lib.rs @@ -1,4 +1,4 @@ -use crate::{boost_pool_rpc::BoostPoolFeesRpc, monitoring::RpcEpochStateV2}; +use crate::boost_pool_rpc::BoostPoolFeesRpc; use boost_pool_rpc::BoostPoolDetailsRpc; use cf_amm::{ common::{Amount as AmmAmount, PoolPairsMap, Side, Tick}, @@ -46,7 +46,7 @@ use state_chain_runtime::{ chainflip::{BlockUpdate, Offence}, constants::common::TX_FEE_MULTIPLIER, monitoring_apis::{ - ActivateKeysBroadcastIds, AuthoritiesInfo, BtcUtxos, ExternalChainsBlockHeight, + ActivateKeysBroadcastIds, AuthoritiesInfo, BtcUtxos, EpochState, ExternalChainsBlockHeight, FeeImbalance, FlipSupply, LastRuntimeUpgradeInfo, MonitoringData, OpenDepositChannels, PendingBroadcasts, PendingTssCeremonies, RedemptionsInfo, SolanaNonces, }, @@ -67,6 +67,25 @@ use std::{ pub mod monitoring; pub mod order_fills; +#[derive(Serialize, Deserialize, Clone)] +pub struct RpcEpochState { + pub blocks_per_epoch: u32, + pub current_epoch_started_at: u32, + pub current_epoch_index: u32, + pub min_active_bid: Option, + pub rotation_phase: String, +} +impl From for RpcEpochState { + fn from(rotation_state: EpochState) -> Self { + Self { + blocks_per_epoch: rotation_state.blocks_per_epoch, + current_epoch_started_at: rotation_state.current_epoch_started_at, + current_epoch_index: rotation_state.current_epoch_index, + rotation_phase: rotation_state.rotation_phase, + min_active_bid: rotation_state.min_active_bid.map(Into::into), + } + } +} #[derive(Serialize, Deserialize, Clone)] pub struct RpcRedemptionsInfo { pub total_balance: NumberOrHex, @@ -98,7 +117,7 @@ impl From for RpcFlipSupply { pub struct RpcMonitoringData { pub external_chains_height: ExternalChainsBlockHeight, pub btc_utxos: BtcUtxos, - pub epoch: RpcEpochStateV2, + pub epoch: RpcEpochState, pub pending_redemptions: RpcRedemptionsInfo, pub pending_broadcasts: PendingBroadcasts, pub pending_tss: PendingTssCeremonies, @@ -345,7 +364,7 @@ type RpcSuspensions = Vec<(Offence, Vec<(u32, state_chain_runtime::AccountId)>)> #[derive(Serialize, Deserialize, Clone)] pub struct RpcAuctionState { - epoch_duration: u32, + blocks_per_epoch: u32, current_epoch_started_at: u32, redemption_period_as_percentage: u8, min_funding: NumberOrHex, @@ -356,7 +375,7 @@ pub struct RpcAuctionState { impl From for RpcAuctionState { fn from(auction_state: AuctionState) -> Self { Self { - epoch_duration: auction_state.epoch_duration, + blocks_per_epoch: auction_state.blocks_per_epoch, current_epoch_started_at: auction_state.current_epoch_started_at, redemption_period_as_percentage: auction_state.redemption_period_as_percentage, min_funding: auction_state.min_funding.into(), diff --git a/state-chain/custom-rpc/src/monitoring.rs b/state-chain/custom-rpc/src/monitoring.rs index cdcf430f51..8973af24f8 100644 --- a/state-chain/custom-rpc/src/monitoring.rs +++ b/state-chain/custom-rpc/src/monitoring.rs @@ -1,10 +1,8 @@ use super::pass_through; use crate::{BlockT, CustomRpc, RpcAccountInfoV2, RpcFeeImbalance, RpcMonitoringData, RpcResult}; use cf_chains::{dot::PolkadotAccountId, sol::SolAddress}; -use cf_utilities::rpc::NumberOrHex; use jsonrpsee::proc_macros::rpc; use sc_client_api::{BlockchainEvents, HeaderBackend}; -use serde::{Deserialize, Serialize}; use sp_core::{bounded_vec::BoundedVec, ConstU32}; use state_chain_runtime::{ chainflip::Offence, @@ -14,44 +12,6 @@ use state_chain_runtime::{ PendingTssCeremonies, RedemptionsInfo, SolanaNonces, }, }; -impl From for RpcEpochState { - fn from(rotation_state: EpochState) -> Self { - Self { - epoch_duration: rotation_state.epoch_duration, - current_epoch_started_at: rotation_state.current_epoch_started_at, - current_epoch_index: rotation_state.current_epoch_index, - rotation_phase: rotation_state.rotation_phase, - min_active_bid: rotation_state.min_active_bid.map(Into::into), - } - } -} -#[derive(Serialize, Deserialize, Clone)] -pub struct RpcEpochState { - pub epoch_duration: u32, - pub current_epoch_started_at: u32, - pub current_epoch_index: u32, - pub min_active_bid: Option, - pub rotation_phase: String, -} - -// Temporary struct to hold the deprecated blocks_per_epoch field. -// Can be deleted after v1.7 is released (meaning: after the version is bumped to 1.8). -#[derive(Serialize, Deserialize, Clone)] -pub struct RpcEpochStateV2 { - #[deprecated( - since = "1.8.0", - note = "This field is deprecated and will be removed in v1.8. Use blocks_per_epoch instead." - )] - blocks_per_epoch: u32, - #[serde(flatten)] - epoch_state: RpcEpochState, -} - -impl From for RpcEpochStateV2 { - fn from(epoch_state: EpochState) -> Self { - Self { blocks_per_epoch: epoch_state.epoch_duration, epoch_state: epoch_state.into() } - } -} #[rpc(server, client, namespace = "cf_monitoring")] pub trait MonitoringApi { @@ -72,7 +32,7 @@ pub trait MonitoringApi { at: Option, ) -> RpcResult>; #[method(name = "epoch_state")] - fn cf_epoch_state(&self, at: Option) -> RpcResult; + fn cf_epoch_state(&self, at: Option) -> RpcResult; #[method(name = "redemptions")] fn cf_redemptions(&self, at: Option) -> RpcResult; #[method(name = "pending_broadcasts")] @@ -141,7 +101,7 @@ where cf_btc_utxos() -> BtcUtxos, cf_dot_aggkey() -> PolkadotAccountId, cf_suspended_validators() -> Vec<(Offence, u32)>, - cf_epoch_state() -> RpcEpochStateV2 [map: RpcEpochStateV2::from], + cf_epoch_state() -> EpochState, cf_redemptions() -> RedemptionsInfo, cf_pending_broadcasts_count() -> PendingBroadcasts, cf_pending_tss_ceremonies_count() -> PendingTssCeremonies, diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index cfc0c2247a..126ed02d02 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -547,7 +547,7 @@ fn testnet_genesis( genesis_funding_amount: u128, minimum_funding: u128, redemption_tax: u128, - epoch_duration: BlockNumber, + blocks_per_epoch: BlockNumber, redemption_ttl_secs: u64, current_authority_emission_inflation_perbill: u32, backup_node_emission_inflation_perbill: u32, @@ -659,7 +659,7 @@ fn testnet_genesis( } }) .collect::<_>(), - epoch_duration, + blocks_per_epoch, redemption_period_as_percentage, backup_reward_node_percentage: Percent::from_percent(33), bond: all_accounts diff --git a/state-chain/pallets/cf-validator/src/lib.rs b/state-chain/pallets/cf-validator/src/lib.rs index 368899ef06..405c9ad786 100644 --- a/state-chain/pallets/cf-validator/src/lib.rs +++ b/state-chain/pallets/cf-validator/src/lib.rs @@ -69,7 +69,7 @@ pub enum PalletConfigUpdate { type RuntimeRotationState = RotationState<::ValidatorId, ::Amount>; -pub const PALLET_VERSION: StorageVersion = StorageVersion::new(5); +pub const PALLET_VERSION: StorageVersion = StorageVersion::new(4); // Might be better to add the enum inside a struct rather than struct inside enum #[derive(Clone, PartialEq, Eq, Default, Encode, Decode, TypeInfo, RuntimeDebugNoBound)] @@ -174,10 +174,10 @@ pub mod pallet { #[pallet::getter(fn current_epoch_started_at)] pub type CurrentEpochStartedAt = StorageValue<_, BlockNumberFor, ValueQuery>; - /// The amount of blocks in an epoch. + /// The duration of an epoch in blocks. #[pallet::storage] - #[pallet::getter(fn epoch_duration)] - pub type EpochDuration = StorageValue<_, BlockNumberFor, ValueQuery>; + #[pallet::getter(fn blocks_per_epoch)] + pub type BlocksPerEpoch = StorageValue<_, BlockNumberFor, ValueQuery>; /// Current epoch index. #[pallet::storage] @@ -379,8 +379,8 @@ pub mod pallet { let mut weight = Weight::zero(); // Check expiry of epoch and store last expired. - if let Some(epoch_to_expire) = EpochExpiries::::take(block_number) { - Self::expire_epochs_up_to(epoch_to_expire); + if let Some(epoch_index) = EpochExpiries::::take(block_number) { + weight.saturating_accrue(Self::expire_epoch(epoch_index)); } weight.saturating_accrue(Self::punish_missed_authorship_slots()); @@ -389,7 +389,7 @@ pub mod pallet { weight.saturating_accrue(match CurrentRotationPhase::::get() { RotationPhase::Idle => { if block_number.saturating_sub(CurrentEpochStartedAt::::get()) >= - EpochDuration::::get() { + BlocksPerEpoch::::get() { if T::RotationBroadcastsPending::rotation_broadcasts_pending() { Self::deposit_event(Event::PreviousRotationStillPending); T::ValidatorWeightInfo::rotation_phase_idle() @@ -536,7 +536,7 @@ pub mod pallet { }, PalletConfigUpdate::EpochDuration { blocks } => { ensure!(blocks > 0, Error::::InvalidEpochDuration); - EpochDuration::::set(blocks.into()); + BlocksPerEpoch::::set(blocks.into()); }, PalletConfigUpdate::AuctionParameters { parameters } => { Self::try_update_auction_parameters(parameters)?; @@ -820,7 +820,7 @@ pub mod pallet { pub struct GenesisConfig { pub genesis_authorities: BTreeSet>, pub genesis_backups: BackupMap, - pub epoch_duration: BlockNumberFor, + pub blocks_per_epoch: BlockNumberFor, pub bond: T::Amount, pub redemption_period_as_percentage: Percent, pub backup_reward_node_percentage: Percent, @@ -835,7 +835,7 @@ pub mod pallet { Self { genesis_authorities: Default::default(), genesis_backups: Default::default(), - epoch_duration: Zero::zero(), + blocks_per_epoch: Zero::zero(), bond: Default::default(), redemption_period_as_percentage: Zero::zero(), backup_reward_node_percentage: Zero::zero(), @@ -856,7 +856,7 @@ pub mod pallet { fn build(&self) { use cf_primitives::GENESIS_EPOCH; LastExpiredEpoch::::set(Default::default()); - EpochDuration::::set(self.epoch_duration); + BlocksPerEpoch::::set(self.blocks_per_epoch); CurrentRotationPhase::::set(RotationPhase::Idle); RedemptionPeriodAsPercentage::::set(self.redemption_period_as_percentage); BackupRewardNodePercentage::::set(self.backup_reward_node_percentage); @@ -988,7 +988,7 @@ impl Pallet { // Set the expiry block number for the old epoch. EpochExpiries::::insert( - frame_system::Pallet::::current_block_number() + EpochDuration::::get(), + frame_system::Pallet::::current_block_number() + BlocksPerEpoch::::get(), old_epoch, ); @@ -1013,6 +1013,7 @@ impl Pallet { } fn expire_epoch(epoch: EpochIndex) -> Weight { + LastExpiredEpoch::::set(epoch); let mut num_expired_authorities = 0; for authority in EpochHistory::::epoch_authorities(epoch).iter() { num_expired_authorities += 1; @@ -1033,18 +1034,6 @@ impl Pallet { T::ValidatorWeightInfo::expire_epoch(num_expired_authorities) } - fn expire_epochs_up_to(latest_epoch_to_expire: EpochIndex) -> Weight { - let mut weight = Weight::zero(); - LastExpiredEpoch::::mutate(|last_expired_epoch| { - let first_unexpired_epoch = *last_expired_epoch + 1; - for epoch in first_unexpired_epoch..=latest_epoch_to_expire { - weight.saturating_accrue(Self::expire_epoch(epoch)); - } - *last_expired_epoch = latest_epoch_to_expire; - }); - weight - } - /// Does all state updates related to the *new* epoch. Is also called at genesis to initialise /// pallet state. Should not update any external state that is not managed by the validator /// pallet, ie. should not call `on_new_epoch`. Also does not need to concern itself with @@ -1329,7 +1318,7 @@ impl Pallet { // current_block > start + ((epoch * epoch%_can_redeem)) CurrentEpochStartedAt::::get() - .saturating_add(RedemptionPeriodAsPercentage::::get() * EpochDuration::::get()) <= + .saturating_add(RedemptionPeriodAsPercentage::::get() * BlocksPerEpoch::::get()) <= frame_system::Pallet::::current_block_number() } } @@ -1434,14 +1423,14 @@ impl pallet_session::SessionManager> for Pallet { impl EstimateNextSessionRotation> for Pallet { fn average_session_length() -> BlockNumberFor { - Self::epoch_duration() + Self::blocks_per_epoch() } fn estimate_current_session_progress(now: BlockNumberFor) -> (Option, Weight) { ( Some(Permill::from_rational( now.saturating_sub(CurrentEpochStartedAt::::get()), - EpochDuration::::get(), + BlocksPerEpoch::::get(), )), T::DbWeight::get().reads(2), ) @@ -1451,7 +1440,7 @@ impl EstimateNextSessionRotation> for Pallet { _now: BlockNumberFor, ) -> (Option>, Weight) { ( - Some(CurrentEpochStartedAt::::get() + EpochDuration::::get()), + Some(CurrentEpochStartedAt::::get() + BlocksPerEpoch::::get()), T::DbWeight::get().reads(2), ) } diff --git a/state-chain/pallets/cf-validator/src/migrations.rs b/state-chain/pallets/cf-validator/src/migrations.rs index 505e9353a8..ea45a36cbf 100644 --- a/state-chain/pallets/cf-validator/src/migrations.rs +++ b/state-chain/pallets/cf-validator/src/migrations.rs @@ -2,10 +2,8 @@ use crate::Pallet; use cf_runtime_upgrade_utilities::{PlaceholderMigration, VersionedMigration}; mod delete_old_epoch_data; -mod rename_blocks_per_epoch; pub type PalletMigration = ( VersionedMigration, delete_old_epoch_data::Migration, 3, 4>, - VersionedMigration, rename_blocks_per_epoch::BlocksPerEpochMigration, 4, 5>, - PlaceholderMigration, 5>, + PlaceholderMigration, 4>, ); diff --git a/state-chain/pallets/cf-validator/src/migrations/rename_blocks_per_epoch.rs b/state-chain/pallets/cf-validator/src/migrations/rename_blocks_per_epoch.rs deleted file mode 100644 index a34cab7424..0000000000 --- a/state-chain/pallets/cf-validator/src/migrations/rename_blocks_per_epoch.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::*; -use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; - -#[cfg(feature = "try-runtime")] -use codec::{Decode, Encode}; -#[cfg(feature = "try-runtime")] -use frame_support::sp_runtime::DispatchError; - -pub mod old { - use super::*; - - #[frame_support::storage_alias] - pub type BlocksPerEpoch = StorageValue, ValueQuery>; -} - -pub struct BlocksPerEpochMigration(sp_std::marker::PhantomData); - -// Rename BlocksPerEpoch -> EpochDuration -impl OnRuntimeUpgrade for BlocksPerEpochMigration { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, DispatchError> { - Ok(old::BlocksPerEpoch::::get().encode()) - } - - fn on_runtime_upgrade() -> Weight { - EpochDuration::::put(old::BlocksPerEpoch::::take()); - Weight::zero() - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), DispatchError> { - assert_eq!( - EpochDuration::::get(), - BlockNumberFor::::decode(&mut &state[..]).unwrap() - ); - assert!(!old::BlocksPerEpoch::::exists()); - Ok(()) - } -} - -#[cfg(test)] -mod migration_tests { - use crate::mock::Test; - - use self::mock::new_test_ext; - - use super::*; - - #[test] - fn test_migration() { - new_test_ext().execute_with(|| { - old::BlocksPerEpoch::::put(100); - assert_ne!(EpochDuration::::get(), 100); - - #[cfg(feature = "try-runtime")] - let state: Vec = BlocksPerEpochMigration::::pre_upgrade().unwrap(); - - BlocksPerEpochMigration::::on_runtime_upgrade(); - - #[cfg(feature = "try-runtime")] - BlocksPerEpochMigration::::post_upgrade(state).unwrap(); - - assert_eq!(EpochDuration::::get(), 100); - }); - } -} diff --git a/state-chain/pallets/cf-validator/src/mock.rs b/state-chain/pallets/cf-validator/src/mock.rs index d7aae8c343..b79d0b2a34 100644 --- a/state-chain/pallets/cf-validator/src/mock.rs +++ b/state-chain/pallets/cf-validator/src/mock.rs @@ -190,7 +190,7 @@ cf_test_utilities::impl_test_helpers! { validator_pallet: ValidatorPalletConfig { genesis_authorities: BTreeSet::from(GENESIS_AUTHORITIES), genesis_backups: Default::default(), - epoch_duration: EPOCH_DURATION, + blocks_per_epoch: EPOCH_DURATION, bond: GENESIS_BOND, redemption_period_as_percentage: REDEMPTION_PERCENTAGE_AT_GENESIS, backup_reward_node_percentage: Percent::from_percent(34), diff --git a/state-chain/pallets/cf-validator/src/tests.rs b/state-chain/pallets/cf-validator/src/tests.rs index 47c52750aa..9b6e9e794d 100644 --- a/state-chain/pallets/cf-validator/src/tests.rs +++ b/state-chain/pallets/cf-validator/src/tests.rs @@ -1263,33 +1263,25 @@ fn validator_registration_and_deregistration() { #[test] fn validator_deregistration_after_expired_epoch() { new_test_ext().execute_with(|| { - const RETIRING_VALIDATOR: u64 = GENESIS_AUTHORITIES[0]; - const REMAINING_AUTHORITIES: [u64; 2] = [GENESIS_AUTHORITIES[1], GENESIS_AUTHORITIES[2]]; - const BOND: u128 = 100; - - ValidatorPallet::transition_to_next_epoch(REMAINING_AUTHORITIES.to_vec(), BOND); - - assert_noop!( - ValidatorPallet::deregister_as_validator(RuntimeOrigin::signed(RETIRING_VALIDATOR),), - Error::::StillBidding - ); - - assert_ok!(ValidatorPallet::stop_bidding(RuntimeOrigin::signed(RETIRING_VALIDATOR))); + assert_ok!(ValidatorPallet::register_as_validator(RuntimeOrigin::signed(ALICE),)); + ValidatorPallet::transition_to_next_epoch(vec![1, ALICE], 100); + let first_epoch_to_expire = ValidatorPallet::current_epoch(); + // Can't deregister assert_noop!( - ValidatorPallet::deregister_as_validator(RuntimeOrigin::signed(RETIRING_VALIDATOR),), + ValidatorPallet::deregister_as_validator(RuntimeOrigin::signed(ALICE),), Error::::StillKeyHolder ); - ValidatorPallet::transition_to_next_epoch(REMAINING_AUTHORITIES.to_vec(), BOND); - ValidatorPallet::transition_to_next_epoch(REMAINING_AUTHORITIES.to_vec(), BOND); + ValidatorPallet::transition_to_next_epoch(vec![1, ALICE], 100); + let second_epoch_to_expire = ValidatorPallet::current_epoch(); + ValidatorPallet::transition_to_next_epoch(vec![1, 2], 100); - ValidatorPallet::expire_epochs_up_to(ValidatorPallet::current_epoch() - 1); + ValidatorPallet::expire_epoch(second_epoch_to_expire); + ValidatorPallet::expire_epoch(first_epoch_to_expire); // Now you can deregister - assert_ok!(ValidatorPallet::deregister_as_validator(RuntimeOrigin::signed( - RETIRING_VALIDATOR - ),)); + assert_ok!(ValidatorPallet::deregister_as_validator(RuntimeOrigin::signed(ALICE),)); }); } @@ -1371,7 +1363,7 @@ fn can_determine_is_auction_phase() { // In Idle phase, must be within certain % of epoch progress. CurrentEpochStartedAt::::set(1_000); - EpochDuration::::set(100); + BlocksPerEpoch::::set(100); RedemptionPeriodAsPercentage::::set(Percent::from_percent(85)); // First block of auction phase = 1_000 + 100 * 85% = 1085 @@ -1454,7 +1446,7 @@ fn can_update_all_config_items() { assert_ne!(RegistrationBondPercentage::::get(), NEW_REGISTRATION_BOND_PERCENTAGE); assert_ne!(AuthoritySetMinSize::::get(), NEW_AUTHORITY_SET_MIN_SIZE); assert_ne!(BackupRewardNodePercentage::::get(), NEW_BACKUP_REWARD_NODE_PERCENTAGE); - assert_ne!(EpochDuration::::get(), NEW_EPOCH_DURATION as u64); + assert_ne!(BlocksPerEpoch::::get(), NEW_EPOCH_DURATION as u64); assert_ne!(AuctionParameters::::get(), NEW_AUCTION_PARAMETERS); assert_ne!(MinimumReportedCfeVersion::::get(), NEW_MINIMUM_REPORTED_CFE_VERSION); assert_ne!( @@ -1503,7 +1495,7 @@ fn can_update_all_config_items() { assert_eq!(RegistrationBondPercentage::::get(), NEW_REGISTRATION_BOND_PERCENTAGE); assert_eq!(AuthoritySetMinSize::::get(), NEW_AUTHORITY_SET_MIN_SIZE); assert_eq!(BackupRewardNodePercentage::::get(), NEW_BACKUP_REWARD_NODE_PERCENTAGE); - assert_eq!(EpochDuration::::get(), NEW_EPOCH_DURATION as u64); + assert_eq!(BlocksPerEpoch::::get(), NEW_EPOCH_DURATION as u64); assert_eq!(AuctionParameters::::get(), NEW_AUCTION_PARAMETERS); assert_eq!(MinimumReportedCfeVersion::::get(), NEW_MINIMUM_REPORTED_CFE_VERSION); assert_eq!( @@ -1523,26 +1515,3 @@ fn can_update_all_config_items() { ); }); } - -#[test] -fn should_expire_all_previous_epochs() { - new_test_ext().execute_with(|| { - const ID: u64 = 1; - const BOND: u128 = 100; - ValidatorPallet::transition_to_next_epoch(vec![ID], BOND); - let first_epoch = ValidatorPallet::current_epoch(); - ValidatorPallet::transition_to_next_epoch(vec![ID], BOND); - let second_epoch = ValidatorPallet::current_epoch(); - ValidatorPallet::transition_to_next_epoch(vec![ID], BOND); - let third_epoch = ValidatorPallet::current_epoch(); - - assert_eq!( - HistoricalActiveEpochs::::get(ID), - vec![first_epoch, second_epoch, third_epoch] - ); - - ValidatorPallet::expire_epochs_up_to(second_epoch); - - assert_eq!(HistoricalActiveEpochs::::get(ID), vec![third_epoch]); - }); -} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index e885b094f0..f4a85ef508 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -616,7 +616,7 @@ impl pallet_aura::Config for Runtime { } parameter_types! { - pub storage BlocksPerEpoch: u64 = Validator::epoch_duration().into(); + pub storage BlocksPerEpoch: u64 = Validator::blocks_per_epoch().into(); } type KeyOwnerIdentification = @@ -1389,7 +1389,7 @@ impl_runtime_apis! { Environment::current_release_version() } fn cf_epoch_duration() -> u32 { - Validator::epoch_duration() + Validator::blocks_per_epoch() } fn cf_current_epoch_started_at() -> u32 { Validator::current_epoch_started_at() @@ -1492,7 +1492,7 @@ impl_runtime_apis! { .ok() .map(|auction_outcome| auction_outcome.bond); AuctionState { - epoch_duration: Validator::epoch_duration(), + blocks_per_epoch: Validator::blocks_per_epoch(), current_epoch_started_at: Validator::current_epoch_started_at(), redemption_period_as_percentage: Validator::redemption_period_as_percentage().deconstruct(), min_funding: MinimumFunding::::get().unique_saturated_into(), @@ -2164,7 +2164,7 @@ impl_runtime_apis! { .ok() .map(|auction_outcome| auction_outcome.bond); EpochState { - epoch_duration: Validator::epoch_duration(), + blocks_per_epoch: Validator::blocks_per_epoch(), current_epoch_started_at: Validator::current_epoch_started_at(), current_epoch_index: Validator::current_epoch(), min_active_bid, diff --git a/state-chain/runtime/src/monitoring_apis.rs b/state-chain/runtime/src/monitoring_apis.rs index 921ec02836..9ee775c74a 100644 --- a/state-chain/runtime/src/monitoring_apis.rs +++ b/state-chain/runtime/src/monitoring_apis.rs @@ -30,7 +30,7 @@ pub struct BtcUtxos { #[derive(Serialize, Deserialize, Encode, Decode, Eq, PartialEq, TypeInfo, Debug, Clone)] pub struct EpochState { - pub epoch_duration: u32, + pub blocks_per_epoch: u32, pub current_epoch_started_at: u32, pub current_epoch_index: u32, pub min_active_bid: Option, diff --git a/state-chain/runtime/src/runtime_apis.rs b/state-chain/runtime/src/runtime_apis.rs index 9ea27cb05c..536ebc0bfd 100644 --- a/state-chain/runtime/src/runtime_apis.rs +++ b/state-chain/runtime/src/runtime_apis.rs @@ -107,7 +107,7 @@ pub struct RuntimeApiPenalty { #[derive(Encode, Decode, Eq, PartialEq, TypeInfo)] pub struct AuctionState { - pub epoch_duration: u32, + pub blocks_per_epoch: u32, pub current_epoch_started_at: u32, pub redemption_period_as_percentage: u8, pub min_funding: u128, From 92436ffd23a12f400ee958a67a788edf2987a7bb Mon Sep 17 00:00:00 2001 From: Albert Llimos <53186777+albert-llimos@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:54:16 +0100 Subject: [PATCH 2/3] Feat: Extend Evm Vault swaps functionality (#5344) * feat: witnessing btc smart contract swaps * chore: address minor review comments * chore: start implementation * test: fix deposit witnessing tests * chore: add intial scale encoding for cfParameters * chore: improve logic * fix: address RuntimeCall size limit * chore: address clippy * chore: more cleanup and refactoring * chore: add contract swaps to dca test * chore: engine refactor * chore: refactor createEvmWallet * chore: cleanup * chore: add MAX_VAULT_SWAP_ATTRIBUTES_LENGTH * chore: refactor bouncer * chore: refactor into common for reusal * chore: rename attributes to parameters * chore: pass extra parameters to sdk * chore: fix issues * chore: engine renaming * chore: update SDK with new encoding logic * chore: remove unnecessary bouncer ts-scale * chore: lint * chore: update to right name * chore: fix missing rename * chore: add beneficiares and make FoK mandatory * chore: lint * chore: update broker_fees and cli * chore: update with hardcoded cfParameters * chore: bump sdk * fix: simplify cf params decoding * chore: downgrade error -> warning * chore: bump sdk with new broker_fees type * chore: fix failing test * chore: bump sdk * chore: lint --------- Co-authored-by: Maxim Shishmarev Co-authored-by: Daniel --- bouncer/package.json | 2 +- bouncer/pnpm-lock.yaml | 18 +-- bouncer/shared/contract_swap.ts | 110 ++++++++++-------- bouncer/shared/new_swap.ts | 2 +- bouncer/shared/perform_swap.ts | 2 +- bouncer/shared/send.ts | 4 +- bouncer/shared/swap_context.ts | 11 +- bouncer/shared/swapping.ts | 16 +-- bouncer/shared/utils.ts | 30 ++++- bouncer/tests/DCA_test.ts | 99 +++++++++++----- bouncer/tests/evm_deposits.ts | 3 +- bouncer/tests/fill_or_kill.ts | 99 +++++++++++----- ...st_swap_deposit_address_with_affiliates.ts | 6 +- engine/src/witness/arb.rs | 21 ++-- engine/src/witness/common.rs | 1 + engine/src/witness/common/cf_parameters.rs | 46 ++++++++ engine/src/witness/eth.rs | 24 ++-- engine/src/witness/evm/vault.rs | 105 +++++++++++------ .../cf-integration-tests/src/solana.rs | 8 +- .../cf-integration-tests/src/swapping.rs | 4 +- state-chain/chains/src/arb/api.rs | 2 +- state-chain/chains/src/btc/api.rs | 2 +- state-chain/chains/src/ccm_checker.rs | 58 ++++----- state-chain/chains/src/dot/api.rs | 2 +- state-chain/chains/src/eth/api.rs | 2 +- state-chain/chains/src/lib.rs | 8 +- state-chain/chains/src/sol/api.rs | 20 ++-- state-chain/chains/src/sol/sol_tx_core.rs | 2 +- .../cf-ingress-egress/src/benchmarking.rs | 2 +- .../pallets/cf-ingress-egress/src/lib.rs | 10 +- .../pallets/cf-ingress-egress/src/tests.rs | 14 +-- state-chain/pallets/cf-swapping/src/tests.rs | 2 +- .../pallets/cf-swapping/src/tests/ccm.rs | 2 +- state-chain/primitives/src/lib.rs | 17 ++- state-chain/traits/src/mocks/api_call.rs | 4 +- .../traits/src/mocks/egress_handler.rs | 6 +- 36 files changed, 487 insertions(+), 277 deletions(-) create mode 100644 engine/src/witness/common/cf_parameters.rs diff --git a/bouncer/package.json b/bouncer/package.json index d41a0d3f9c..0a0ff85f2b 100644 --- a/bouncer/package.json +++ b/bouncer/package.json @@ -6,7 +6,7 @@ "prettier:write": "prettier --write ." }, "dependencies": { - "@chainflip/cli": "1.6.3", + "@chainflip/cli": "1.8.0-cf-parameters-rename.5", "@chainflip/utils": "^0.4.0", "@coral-xyz/anchor": "^0.30.1", "@iarna/toml": "^2.2.5", diff --git a/bouncer/pnpm-lock.yaml b/bouncer/pnpm-lock.yaml index 1b3b56eaa3..1cdd1b8811 100644 --- a/bouncer/pnpm-lock.yaml +++ b/bouncer/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@chainflip/cli': - specifier: 1.6.3 - version: 1.6.3(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10) + specifier: 1.8.0-cf-parameters-rename.5 + version: 1.8.0-cf-parameters-rename.5(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10) '@chainflip/utils': specifier: ^0.4.0 version: 0.4.0 @@ -145,8 +145,8 @@ packages: '@chainflip/bitcoin@1.1.1': resolution: {integrity: sha512-Jr6X/0QTSFYpTp23ZPhyioL6wL9x3Xpj6OGjadQKZyhobRN4BZkhLogv20HDYTJcRzVphzrTJArbEaNie04XVA==} - '@chainflip/cli@1.6.3': - resolution: {integrity: sha512-isbPdFbO0tmQ7+7OjWSyzKtuog7FOgvkeb7fIini88LHsTjTXf8UvoKGYncOgwvLVQIY+J7NDnSQQmKdBg7PUw==} + '@chainflip/cli@1.8.0-cf-parameters-rename.5': + resolution: {integrity: sha512-+/1U/8IMuLZcRMCekq76heU0gptGvWho7joJ/I3Gbd/4SNI/K1pLp5wtATtE+bcELZ1QrtvAt+bK8yM9WNrCYg==} hasBin: true peerDependencies: axios: ^1.x @@ -155,8 +155,8 @@ packages: '@chainflip/extrinsics@1.6.1': resolution: {integrity: sha512-sm3v2QguNW4/RiIZYHd+VRSxup2q4cVBY0VjbQJw6xYqhNCzZlt+Hl38Kni+TCHQu3hP/WgIF72yvvSs7/PBdg==} - '@chainflip/rpc@1.6.7': - resolution: {integrity: sha512-OIE2cqzDy9Oq9sHuxJKOtbjP438QwMFdc6tuWdAmjjPcHGATI0Ft/lIKeCutD/8uH/CumOzaOoBaT2pI6G78Gg==} + '@chainflip/rpc@1.6.9': + resolution: {integrity: sha512-FtaoZBey4kIFhWQY5mMMAGXIgzSpZCX0DTnXQqPEn9q5qvt6FoQLDQk9k2IYKOTiIP+XsLy9w/WPPiH7+3BpnQ==} '@chainflip/solana@1.0.2': resolution: {integrity: sha512-8M+z0yjVCZBMpdCTGuBRt/Ns9NBJCNFvnSOjDu/kyaBRvxdUvECJOIseL0ENLBd0J/YmO9TcrZ30M0po3a96BA==} @@ -3169,11 +3169,11 @@ snapshots: transitivePeerDependencies: - typescript - '@chainflip/cli@1.6.3(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10)': + '@chainflip/cli@1.8.0-cf-parameters-rename.5(axios@1.7.2)(bufferutil@4.0.8)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.3)(utf-8-validate@5.0.10)': dependencies: '@chainflip/bitcoin': 1.1.1(typescript@5.5.3) '@chainflip/extrinsics': 1.6.1 - '@chainflip/rpc': 1.6.7 + '@chainflip/rpc': 1.6.9 '@chainflip/solana': 1.0.2 '@chainflip/utils': 0.4.0 axios: 1.7.2 @@ -3189,7 +3189,7 @@ snapshots: '@chainflip/extrinsics@1.6.1': {} - '@chainflip/rpc@1.6.7': + '@chainflip/rpc@1.6.9': dependencies: '@chainflip/utils': 0.3.0 zod: 3.23.8 diff --git a/bouncer/shared/contract_swap.ts b/bouncer/shared/contract_swap.ts index fa3f70c94c..d3a0d80eb4 100644 --- a/bouncer/shared/contract_swap.ts +++ b/bouncer/shared/contract_swap.ts @@ -5,10 +5,10 @@ import { approveVault, Asset as SCAsset, Chains, - InternalAsset, Chain, } from '@chainflip/cli'; -import { HDNodeWallet, Wallet, getDefaultProvider } from 'ethers'; +import { HDNodeWallet } from 'ethers'; +import { randomBytes } from 'crypto'; import { observeBalanceIncrease, getContractAddress, @@ -16,33 +16,56 @@ import { amountToFineAmount, defaultAssetAmounts, chainFromAsset, - getEvmEndpoint, assetDecimals, stateChainAssetFromAsset, - chainGasAsset, + createEvmWalletAndFund, + newAddress, } from './utils'; import { getBalance } from './get_balance'; -import { CcmDepositMetadata } from '../shared/new_swap'; -import { send } from './send'; +import { CcmDepositMetadata, DcaParams, FillOrKillParamsX128 } from '../shared/new_swap'; import { SwapContext, SwapStatus } from './swap_context'; const erc20Assets: Asset[] = ['Flip', 'Usdc', 'Usdt', 'ArbUsdc']; export async function executeContractSwap( - srcAsset: Asset, + sourceAsset: Asset, destAsset: Asset, destAddress: string, - wallet: HDNodeWallet, messageMetadata?: CcmDepositMetadata, + amount?: string, + boostFeeBps?: number, + fillOrKillParams?: FillOrKillParamsX128, + dcaParams?: DcaParams, + wallet?: HDNodeWallet, ): ReturnType { - const srcChain = chainFromAsset(srcAsset); + const srcChain = chainFromAsset(sourceAsset); const destChain = chainFromAsset(destAsset); + const amountToSwap = amount ?? defaultAssetAmounts(sourceAsset); + + const refundAddress = await newAddress(sourceAsset, randomBytes(32).toString('hex')); + const fokParams = fillOrKillParams ?? { + retryDurationBlocks: 0, + refundAddress, + minPriceX128: '0', + }; + + const evmWallet = wallet ?? (await createEvmWalletAndFund(sourceAsset)); + + if (erc20Assets.includes(sourceAsset)) { + // Doing effectively infinite approvals to make sure it doesn't fail. + // eslint-disable-next-line @typescript-eslint/no-use-before-define + await approveTokenVault( + sourceAsset, + (BigInt(amountToFineAmount(amountToSwap, assetDecimals(sourceAsset))) * 100n).toString(), + evmWallet, + ); + } const networkOptions = { - signer: wallet, + signer: evmWallet, network: 'localnet', vaultContractAddress: getContractAddress(srcChain, 'VAULT'), - srcTokenContractAddress: getContractAddress(srcChain, srcAsset), + srcTokenContractAddress: getContractAddress(srcChain, sourceAsset), } as const; const txOptions = { // This is run with fresh addresses to prevent nonce issues. Will be 1 for ERC20s. @@ -55,15 +78,21 @@ export async function executeContractSwap( destAsset: stateChainAssetFromAsset(destAsset), // It is important that this is large enough to result in // an amount larger than existential (e.g. on Polkadot): - amount: amountToFineAmount(defaultAssetAmounts(srcAsset), assetDecimals(srcAsset)), + amount: amountToFineAmount(amountToSwap, assetDecimals(sourceAsset)), destAddress, - srcAsset: stateChainAssetFromAsset(srcAsset), + srcAsset: stateChainAssetFromAsset(sourceAsset), srcChain, ccmParams: messageMetadata && { gasBudget: messageMetadata.gasBudget.toString(), message: messageMetadata.message, - cfParameters: messageMetadata.cfParameters, + ccmAdditionalData: messageMetadata.ccmAdditionalData, }, + // The SDK will encode these parameters and the ccmAdditionalData + // into the `cfParameters` field for the vault swap. + boostFeeBps, + fillOrKillParams: fokParams, + dcaParams, + beneficiaries: undefined, } as ExecuteSwapParams, networkOptions, txOptions, @@ -86,37 +115,18 @@ export async function performSwapViaContract( messageMetadata?: CcmDepositMetadata, swapContext?: SwapContext, log = true, + amount?: string, + boostFeeBps?: number, + fillOrKillParams?: FillOrKillParamsX128, + dcaParams?: DcaParams, ): Promise { const tag = swapTag ?? ''; - - const srcChain = chainFromAsset(sourceAsset); - - // Generate a new wallet for each contract swap to prevent nonce issues when running in parallel - // with other swaps via deposit channels. - const mnemonic = Wallet.createRandom().mnemonic?.phrase ?? ''; - if (mnemonic === '') { - throw new Error('Failed to create random mnemonic'); - } - const wallet = Wallet.fromPhrase(mnemonic).connect(getDefaultProvider(getEvmEndpoint(srcChain))); + const amountToSwap = amount ?? defaultAssetAmounts(sourceAsset); try { - // Fund new key with native asset and asset to swap. - await send(chainGasAsset(srcChain) as InternalAsset, wallet.address); - await send(sourceAsset, wallet.address); - - if (erc20Assets.includes(sourceAsset)) { - // Doing effectively infinite approvals to make sure it doesn't fail. - // eslint-disable-next-line @typescript-eslint/no-use-before-define - await approveTokenVault( - sourceAsset, - ( - BigInt(amountToFineAmount(defaultAssetAmounts(sourceAsset), assetDecimals(sourceAsset))) * - 100n - ).toString(), - wallet, - ); - } - swapContext?.updateStatus(swapTag, SwapStatus.ContractApproved); + // Generate a new wallet for each contract swap to prevent nonce issues when running in parallel + // with other swaps via deposit channels. + const wallet = await createEvmWalletAndFund(sourceAsset); const oldBalance = await getBalance(destAsset, destAddress); if (log) { @@ -133,8 +143,12 @@ export async function performSwapViaContract( sourceAsset, destAsset, destAddress, - wallet, messageMetadata, + amountToSwap, + boostFeeBps, + fillOrKillParams, + dcaParams, + wallet, ); swapContext?.updateStatus(swapTag, SwapStatus.ContractExecuted); @@ -171,24 +185,24 @@ export async function performSwapViaContract( throw new Error(`${tag} ${err}`); } } -export async function approveTokenVault(srcAsset: Asset, amount: string, wallet: HDNodeWallet) { - if (!erc20Assets.includes(srcAsset)) { - throw new Error(`Unsupported asset, not an ERC20: ${srcAsset}`); +export async function approveTokenVault(sourceAsset: Asset, amount: string, wallet: HDNodeWallet) { + if (!erc20Assets.includes(sourceAsset)) { + throw new Error(`Unsupported asset, not an ERC20: ${sourceAsset}`); } - const chain = chainFromAsset(srcAsset as Asset); + const chain = chainFromAsset(sourceAsset as Asset); await approveVault( { amount, srcChain: chain as Chain, - srcAsset: stateChainAssetFromAsset(srcAsset) as SCAsset, + srcAsset: stateChainAssetFromAsset(sourceAsset) as SCAsset, }, { signer: wallet, network: 'localnet', vaultContractAddress: getContractAddress(chain, 'VAULT'), - srcTokenContractAddress: getContractAddress(chain, srcAsset), + srcTokenContractAddress: getContractAddress(chain, sourceAsset), }, // This is run with fresh addresses to prevent nonce issues { diff --git a/bouncer/shared/new_swap.ts b/bouncer/shared/new_swap.ts index 69453842c2..0919ddd0a4 100644 --- a/bouncer/shared/new_swap.ts +++ b/bouncer/shared/new_swap.ts @@ -38,7 +38,7 @@ export async function newSwap( ccmParams: messageMetadata && { message: messageMetadata.message as `0x${string}`, gasBudget: messageMetadata.gasBudget.toString(), - cfParameters: messageMetadata.cfParameters as `0x${string}`, + ccmAdditionalData: messageMetadata.ccmAdditionalData as `0x${string}`, }, commissionBps: brokerCommissionBps, maxBoostFeeBps: boostFeeBps, diff --git a/bouncer/shared/perform_swap.ts b/bouncer/shared/perform_swap.ts index 45814b70f2..465b84805e 100644 --- a/bouncer/shared/perform_swap.ts +++ b/bouncer/shared/perform_swap.ts @@ -70,7 +70,7 @@ export async function requestNewSwap( ? event.data.channelMetadata !== null && event.data.channelMetadata.message === messageMetadata.message && event.data.channelMetadata.gasBudget.replace(/,/g, '') === messageMetadata.gasBudget && - event.data.channelMetadata.cfParameters === messageMetadata.cfParameters + event.data.channelMetadata.ccmAdditionalData === messageMetadata.ccmAdditionalData : event.data.channelMetadata === null; return destAddressMatches && destAssetMatches && sourceAssetMatches && ccmMetadataMatches; diff --git a/bouncer/shared/send.ts b/bouncer/shared/send.ts index 984806351f..521f66fc34 100644 --- a/bouncer/shared/send.ts +++ b/bouncer/shared/send.ts @@ -20,9 +20,7 @@ import { sendSolUsdc } from './send_solusdc'; const cfTesterAbi = await getCFTesterAbi(); export async function send(asset: Asset, address: string, amount?: string, log = true) { - // TODO: Remove this any when we have Sol assets in the Asset type. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - switch (asset as any) { + switch (asset) { case 'Btc': await sendBtc(address, amount ?? defaultAssetAmounts(asset)); break; diff --git a/bouncer/shared/swap_context.ts b/bouncer/shared/swap_context.ts index 6c9ff44286..2d2a6bc681 100644 --- a/bouncer/shared/swap_context.ts +++ b/bouncer/shared/swap_context.ts @@ -3,8 +3,6 @@ import assert from 'assert'; export enum SwapStatus { Initiated, Funded, - // Contract swap specific statuses - ContractApproved, ContractExecuted, SwapScheduled, Success, @@ -37,16 +35,9 @@ export class SwapContext { ); break; } - case SwapStatus.ContractApproved: { - assert( - currentStatus === SwapStatus.Initiated, - `Unexpected status transition for ${tag}. Transitioning from ${currentStatus} to ${status}`, - ); - break; - } case SwapStatus.ContractExecuted: { assert( - currentStatus === SwapStatus.ContractApproved, + currentStatus === SwapStatus.Initiated, `Unexpected status transition for ${tag}. Transitioning from ${currentStatus} to ${status}`, ); break; diff --git a/bouncer/shared/swapping.ts b/bouncer/shared/swapping.ts index 5642c4ea69..0bde37e88d 100644 --- a/bouncer/shared/swapping.ts +++ b/bouncer/shared/swapping.ts @@ -12,7 +12,7 @@ import { defaultAssetAmounts, ccmSupportedChains, assetDecimals, - solCfParamsCodec, + solCcmAdditionalDataCodec, } from '../shared/utils'; import { BtcAddressType } from '../shared/new_btc_address'; import { CcmDepositMetadata } from '../shared/new_swap'; @@ -69,7 +69,7 @@ function newAbiEncodedMessage(types?: SolidityType[]): string { return web3.eth.abi.encodeParameters(typesArray, variables); } -export function newSolanaCfParameters(maxAccounts: number) { +export function newSolanaCcmAdditionalData(maxAccounts: number) { const cfReceiverAddress = getContractAddress('Solana', 'CFTESTER'); const fallbackAddress = Keypair.generate().publicKey.toBytes(); @@ -93,7 +93,7 @@ export function newSolanaCfParameters(maxAccounts: number) { fallback_address: fallbackAddress, }; - return u8aToHex(solCfParamsCodec.enc(cfParameters)); + return u8aToHex(solCcmAdditionalDataCodec.enc(cfParameters)); } // Solana CCM-related parameters. These are values in the protocol. @@ -107,7 +107,7 @@ function newCcmArbitraryBytes(maxLength: number): string { return randomAsHex(Math.floor(Math.random() * Math.max(0, maxLength - 10)) + 10); } -function newCfParameters(destAsset: Asset, message?: string): string { +function newCcmAdditionalData(destAsset: Asset, message?: string): string { const destChain = chainFromAsset(destAsset); switch (destChain) { case 'Ethereum': @@ -123,7 +123,7 @@ function newCfParameters(destAsset: Asset, message?: string): string { // The maximum number of extra accounts that can be passed is limited by the tx size // and therefore also depends on the message length. - return newSolanaCfParameters(maxAccounts); + return newSolanaCcmAdditionalData(maxAccounts); } default: throw new Error(`Unsupported chain: ${destChain}`); @@ -151,7 +151,7 @@ export function newCcmMetadata( cfParamsArray?: string, ): CcmDepositMetadata { const message = ccmMessage ?? newCcmMessage(destAsset); - const cfParameters = cfParamsArray ?? newCfParameters(destAsset, message); + const ccmAdditionalData = cfParamsArray ?? newCcmAdditionalData(destAsset, message); const gasDiv = gasBudgetFraction ?? 2; const gasBudget = Math.floor( @@ -162,7 +162,7 @@ export function newCcmMetadata( return { message, gasBudget, - cfParameters, + ccmAdditionalData, }; } @@ -250,7 +250,7 @@ export async function testSwapViaContract( destAsset, addressType, messageMetadata, - (tagSuffix ?? '') + ' Contract', + (tagSuffix ?? '') + 'Contract', log, swapContext, ); diff --git a/bouncer/shared/utils.ts b/bouncer/shared/utils.ts index 759c2744b8..073626d3af 100644 --- a/bouncer/shared/utils.ts +++ b/bouncer/shared/utils.ts @@ -1,5 +1,6 @@ import { execSync } from 'child_process'; import * as crypto from 'crypto'; +import { HDNodeWallet, Wallet, getDefaultProvider } from 'ethers'; import { setTimeout as sleep } from 'timers/promises'; import Client from 'bitcoin-core'; import { ApiPromise, Keyring } from '@polkadot/api'; @@ -31,6 +32,7 @@ import { SwapParams } from './perform_swap'; import { newSolAddress } from './new_sol_address'; import { getChainflipApi, observeBadEvent, observeEvent } from './utils/substrate'; import { execWithLog } from './utils/exec_with_log'; +import { send } from './send'; const cfTesterAbi = await getCFTesterAbi(); const cfTesterIdl = await getCfTesterIdl(); @@ -52,7 +54,7 @@ const isSDKChain = (chain: Chain): chain is SDKChain => chain in chainConstants; export const solanaNumberOfNonces = 10; -export const solCfParamsCodec = Struct({ +export const solCcmAdditionalDataCodec = Struct({ cf_receiver: Struct({ pubkey: TsBytes(32), is_writable: bool, @@ -484,7 +486,7 @@ function checkRequestTypeMatches(actual: object | string, expected: SwapRequestT export async function observeSwapRequested( sourceAsset: Asset, destAsset: Asset, - channelId: number, + id: number | string, swapRequestType: SwapRequestType, ) { // need to await this to prevent the chainflip api from being disposed prematurely @@ -492,9 +494,12 @@ export async function observeSwapRequested( test: (event) => { const data = event.data; - if (typeof data.origin === 'object' && 'DepositChannel' in data.origin) { + if (typeof data.origin === 'object') { const channelMatches = - Number(data.origin.DepositChannel.channelId.replaceAll(',', '')) === channelId; + (typeof id === 'number' && + 'DepositChannel' in data.origin && + Number(data.origin.DepositChannel.channelId.replaceAll(',', '')) === id) || + (typeof id === 'string' && 'Vault' in data.origin && data.origin.Vault.txHash === id); const sourceAssetMatches = sourceAsset === (data.inputAsset as Asset); const destAssetMatches = destAsset === (data.outputAsset as Asset); const requestTypeMatches = checkRequestTypeMatches(data.requestType, swapRequestType); @@ -746,8 +751,8 @@ export async function observeSolanaCcmEvent( // The message is being used as the main discriminator if (matchEventName && matchSourceChain && matchMessage) { - const { remaining_accounts: expectedRemainingAccounts } = solCfParamsCodec.dec( - messageMetadata.cfParameters!, + const { remaining_accounts: expectedRemainingAccounts } = solCcmAdditionalDataCodec.dec( + messageMetadata.ccmAdditionalData!, ); if ( @@ -1152,3 +1157,16 @@ export function getTimeStamp(): string { const seconds = now.getSeconds().toString().padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; } + +export async function createEvmWalletAndFund(asset: Asset): Promise { + const chain = chainFromAsset(asset); + + const mnemonic = Wallet.createRandom().mnemonic?.phrase ?? ''; + if (mnemonic === '') { + throw new Error('Failed to create random mnemonic'); + } + const wallet = Wallet.fromPhrase(mnemonic).connect(getDefaultProvider(getEvmEndpoint(chain))); + await send(chainGasAsset(chain) as SDKAsset, wallet.address, undefined, false); + await send(asset, wallet.address, undefined, false); + return wallet; +} diff --git a/bouncer/tests/DCA_test.ts b/bouncer/tests/DCA_test.ts index 43e14aad59..725305d906 100644 --- a/bouncer/tests/DCA_test.ts +++ b/bouncer/tests/DCA_test.ts @@ -12,7 +12,8 @@ import { observeEvent, observeEvents } from '../shared/utils/substrate'; import { getBalance } from '../shared/get_balance'; import { ExecutableTest } from '../shared/executable_test'; import { requestNewSwap } from '../shared/perform_swap'; -import { DcaParams } from '../shared/new_swap'; +import { DcaParams, FillOrKillParamsX128 } from '../shared/new_swap'; +import { executeContractSwap } from '../shared/contract_swap'; /* eslint-disable @typescript-eslint/no-use-before-define */ export const testDCASwaps = new ExecutableTest('DCA-Swaps', main, 150); @@ -20,50 +21,85 @@ export const testDCASwaps = new ExecutableTest('DCA-Swaps', main, 150); // Requested number of blocks between each chunk const CHUNK_INTERVAL = 2; -async function testDCASwap(inputAsset: Asset, amount: number, numberOfChunks: number) { +async function testDCASwap( + inputAsset: Asset, + amount: number, + numberOfChunks: number, + swapviaContract = false, +) { assert(numberOfChunks > 1, 'Number of chunks must be greater than 1'); - const dcaParameters: DcaParams = { + const dcaParams: DcaParams = { numberOfChunks, chunkIntervalBlocks: CHUNK_INTERVAL, }; + const fillOrKillParams: FillOrKillParamsX128 = { + refundAddress: '0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF', + minPriceX128: '1', + retryDurationBlocks: 100, + }; const destAsset = inputAsset === Assets.Usdc ? Assets.Flip : Assets.Usdc; + const destAddress = await newAddress(destAsset, randomBytes(32).toString('hex')); + const destBalanceBefore = await getBalance(inputAsset, destAddress); testDCASwaps.debugLog(`DCA destination address: ${destAddress}`); - const swapRequest = await requestNewSwap( - inputAsset, - destAsset, - destAddress, - 'DCA_Test', - undefined, // messageMetadata - 0, // brokerCommissionBps - false, // log - 0, // boostFeeBps - { - refundAddress: '0xa56A6be23b6Cf39D9448FF6e897C29c41c8fbDFF', - minPriceX128: '1', - retryDurationBlocks: 100, - }, // FoK parameters - dcaParameters, - ); + let swapRequestedHandle; - const depositChannelId = swapRequest.channelId; - const swapRequestedHandle = observeSwapRequested( - inputAsset, - destAsset, - depositChannelId, - SwapRequestType.Regular, - ); + if (!swapviaContract) { + const swapRequest = await requestNewSwap( + inputAsset, + destAsset, + destAddress, + 'DCA_Test', + undefined, // messageMetadata + 0, // brokerCommissionBps + false, // log + 0, // boostFeeBps + fillOrKillParams, + dcaParams, + ); - // Deposit the asset - await send(inputAsset, swapRequest.depositAddress, amount.toString()); - testDCASwaps.log(`Sent ${amount} ${inputAsset} to ${swapRequest.depositAddress}`); + const depositChannelId = swapRequest.channelId; + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + depositChannelId, + SwapRequestType.Regular, + ); + + // Deposit the asset + await send(inputAsset, swapRequest.depositAddress, amount.toString()); + testDCASwaps.log(`Sent ${amount} ${inputAsset} to ${swapRequest.depositAddress}`); + } else { + const receipt = await executeContractSwap( + inputAsset, + destAsset, + destAddress, + undefined, + amount.toString(), + undefined, + fillOrKillParams, + dcaParams, + ); + + testDCASwaps.log(`Contract swap executed, tx hash: ${receipt.hash}`); + + // Look after Swap Requested of data.origin.Vault.tx_hash + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + receipt.hash, + SwapRequestType.Regular, + ); + } const swapRequestId = Number((await swapRequestedHandle).data.swapRequestId.replaceAll(',', '')); - testDCASwaps.debugLog(`${inputAsset} swap requested, swapRequestId: ${swapRequestId}`); + testDCASwaps.debugLog( + `${inputAsset} swap ${swapviaContract ? 'via contract' : ''}, swapRequestId: ${swapRequestId}`, + ); // Wait for the swap to complete await observeEvent(`swapping:SwapRequestCompleted`, { @@ -92,11 +128,12 @@ async function testDCASwap(inputAsset: Asset, amount: number, numberOfChunks: nu `Unexpected chunk interval between chunk ${i - 1} & ${i}`, ); } + testDCASwaps.log(`Chunk interval of ${CHUNK_INTERVAL} verified for all ${numberOfChunks} chunks`); await observeBalanceIncrease(destAsset, destAddress, destBalanceBefore); } export async function main() { - await testDCASwap(Assets.Eth, 1, 2); + await Promise.all([testDCASwap(Assets.Eth, 1, 2), testDCASwap(Assets.ArbEth, 1, 2, true)]); } diff --git a/bouncer/tests/evm_deposits.ts b/bouncer/tests/evm_deposits.ts index 73638b5f7a..02a293e5d1 100644 --- a/bouncer/tests/evm_deposits.ts +++ b/bouncer/tests/evm_deposits.ts @@ -97,7 +97,8 @@ async function testTxMultipleContractSwaps(sourceAsset: Asset, destAsset: Asset) assetContractId(destAsset), getContractAddress(chainFromAsset(sourceAsset), sourceAsset), amount, - '0x', + // Encoded EVM refund address and no other swap parameters. + '0x0000000000000e879c89cad7076b347bde13c99cf2c33e7299b60000000000000000000000000000000000000000000000000000000000000000000000', numSwaps, ) .encodeABI(); diff --git a/bouncer/tests/fill_or_kill.ts b/bouncer/tests/fill_or_kill.ts index bbdf7a6e3e..591cba52ca 100644 --- a/bouncer/tests/fill_or_kill.ts +++ b/bouncer/tests/fill_or_kill.ts @@ -13,18 +13,21 @@ import { requestNewSwap } from '../shared/perform_swap'; import { send } from '../shared/send'; import { getBalance } from '../shared/get_balance'; import { observeEvent } from '../shared/utils/substrate'; -import { FillOrKillParamsX128 } from '../shared/new_swap'; +import { CcmDepositMetadata, FillOrKillParamsX128 } from '../shared/new_swap'; import { ExecutableTest } from '../shared/executable_test'; +import { executeContractSwap } from '../shared/contract_swap'; +import { newCcmMetadata } from '../shared/swapping'; /* eslint-disable @typescript-eslint/no-use-before-define */ export const testFillOrKill = new ExecutableTest('FoK', main, 600); /// Do a swap with unrealistic minimum price so it gets refunded. -async function testMinPriceRefund(inputAsset: Asset, amount: number) { +async function testMinPriceRefund(inputAsset: Asset, amount: number, swapviaContract = false) { const destAsset = inputAsset === Assets.Usdc ? Assets.Flip : Assets.Usdc; const refundAddress = await newAddress(inputAsset, randomBytes(32).toString('hex')); const destAddress = await newAddress(destAsset, randomBytes(32).toString('hex')); testFillOrKill.debugLog(`Swap destination address: ${destAddress}`); + testFillOrKill.debugLog(`Refund address: ${refundAddress}`); const refundBalanceBefore = await getBalance(inputAsset, refundAddress); @@ -39,33 +42,66 @@ async function testMinPriceRefund(inputAsset: Asset, amount: number) { ), }; - testFillOrKill.log( - `Requesting swap from ${inputAsset} to ${destAsset} with unrealistic min price`, - ); - const swapRequest = await requestNewSwap( - inputAsset, - destAsset, - destAddress, - 'FoK_Test', - undefined, // messageMetadata - 0, // brokerCommissionBps - false, // log - 0, // boostFeeBps - refundParameters, - ); - const depositAddress = swapRequest.depositAddress; - const depositChannelId = swapRequest.channelId; - - const swapRequestedHandle = observeSwapRequested( - inputAsset, - destAsset, - depositChannelId, - SwapRequestType.Regular, - ); - - // Deposit the asset - await send(inputAsset, depositAddress, amount.toString()); - testFillOrKill.log(`Sent ${amount} ${inputAsset} to ${depositAddress}`); + let swapRequestedHandle; + + if (!swapviaContract) { + testFillOrKill.log( + `Requesting swap from ${inputAsset} to ${destAsset} with unrealistic min price`, + ); + const swapRequest = await requestNewSwap( + inputAsset, + destAsset, + destAddress, + 'FoK_Test', + undefined, // messageMetadata + 0, // brokerCommissionBps + false, // log + 0, // boostFeeBps + refundParameters, + ); + const depositAddress = swapRequest.depositAddress; + const depositChannelId = swapRequest.channelId; + + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + depositChannelId, + SwapRequestType.Regular, + ); + + // Deposit the asset + await send(inputAsset, depositAddress, amount.toString()); + testFillOrKill.log(`Sent ${amount} ${inputAsset} to ${depositAddress}`); + } else { + testFillOrKill.log( + `Swapping via contract from ${inputAsset} to ${destAsset} with unrealistic min price`, + ); + + // Randomly use CCM to test different encodings + let ccmMetadata: CcmDepositMetadata | undefined; + if (Math.random() < 0.5) { + ccmMetadata = newCcmMetadata(inputAsset, destAsset, undefined, 100); + ccmMetadata.ccmAdditionalData = + Math.random() < 0.5 ? ccmMetadata.ccmAdditionalData : undefined; + } + + const receipt = await executeContractSwap( + inputAsset, + destAsset, + destAddress, + undefined, + amount.toString(), + undefined, + refundParameters, + ); + + swapRequestedHandle = observeSwapRequested( + inputAsset, + destAsset, + receipt.hash, + SwapRequestType.Regular, + ); + } const swapRequestedEvent = await swapRequestedHandle; const swapRequestId = Number(swapRequestedEvent.data.swapRequestId.replaceAll(',', '')); @@ -87,8 +123,6 @@ async function testMinPriceRefund(inputAsset: Asset, amount: number) { `${inputAsset} swap ${swapRequestId} was executed instead of failing and being refunded`, ); } - - testFillOrKill.log(`FoK ${inputAsset} swap refunded`); } async function main() { @@ -98,5 +132,8 @@ async function main() { testMinPriceRefund(Assets.Dot, 100), testMinPriceRefund(Assets.Btc, 0.1), testMinPriceRefund(Assets.Usdc, 1000), + testMinPriceRefund(Assets.Flip, 500, true), + testMinPriceRefund(Assets.Eth, 1, true), + testMinPriceRefund(Assets.ArbEth, 5, true), ]); } diff --git a/bouncer/tests/request_swap_deposit_address_with_affiliates.ts b/bouncer/tests/request_swap_deposit_address_with_affiliates.ts index 794468dacc..d90a905803 100644 --- a/bouncer/tests/request_swap_deposit_address_with_affiliates.ts +++ b/bouncer/tests/request_swap_deposit_address_with_affiliates.ts @@ -40,7 +40,7 @@ const eventSchema = z destinationAsset: z.string(), brokerCommissionRate: numberSchema, channelMetadata: z - .object({ message: z.string(), gasBudget: bigintSchema, cfParameters: z.string() }) + .object({ message: z.string(), gasBudget: bigintSchema, ccmAdditionalData: z.string() }) .nullable(), boostFee: numberSchema, affiliateFees: z.array(z.object({ account: z.string(), bps: numberSchema })), @@ -141,7 +141,7 @@ const requestSwapDepositAddress = async ( if (params.ccmParams) { assert.strictEqual(event.channelMetadata?.message, params.ccmParams.message); assert.strictEqual(event.channelMetadata.gasBudget, BigInt(params.ccmParams.gasBudget)); - assert.strictEqual(event.channelMetadata.cfParameters, params.ccmParams.cfParameters); + assert.strictEqual(event.channelMetadata.ccmAdditionalData, params.ccmParams.ccmAdditionalData); } }; @@ -215,7 +215,7 @@ const withCcm: NewSwapRequest = { ccmParams: { message: '0xcafebabe', gasBudget: '1000000', - cfParameters: '0xdeadbeef', + ccmAdditionalData: '0xdeadbeef', }, }; diff --git a/engine/src/witness/arb.rs b/engine/src/witness/arb.rs index d38886b7b6..1fe39cf897 100644 --- a/engine/src/witness/arb.rs +++ b/engine/src/witness/arb.rs @@ -18,7 +18,7 @@ use crate::{ stream_api::{StreamApi, FINALIZED}, STATE_CHAIN_CONNECTION, }, - witness::evm::erc20_deposits::usdc::UsdcEvents, + witness::{common::cf_parameters::ShortId, evm::erc20_deposits::usdc::UsdcEvents}, }; use super::{ @@ -175,8 +175,10 @@ where struct ArbCallBuilder {} -use cf_chains::{address::EncodedAddress, CcmDepositMetadata}; -use cf_primitives::{Asset, AssetAmount, TransactionHash}; +use cf_chains::{address::EncodedAddress, CcmDepositMetadata, ChannelRefundParameters}; +use cf_primitives::{ + Asset, AssetAmount, BasisPoints, Beneficiaries, DcaParameters, TransactionHash, +}; impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { type Chain = Arbitrum; @@ -188,6 +190,11 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: TransactionHash, + _broker_fees: Beneficiaries, + refund_params: Option, + dca_params: Option, + // This is only to be checked in the pre-witnessed version + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall { state_chain_runtime::RuntimeCall::ArbitrumIngressEgress( pallet_cf_ingress_egress::Call::contract_swap_request { @@ -198,11 +205,11 @@ impl super::evm::vault::IngressCallBuilder for ArbCallBuilder { deposit_metadata, tx_hash, deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_hash.into()]) }), + // Defaulting to no broker fees until PRO-1743 is completed. broker_fees: Default::default(), - // TODO: use real parameters when we can decode them - boost_fee: 0, - dca_params: None, - refund_params: None, + boost_fee: boost_fee.unwrap_or_default(), + dca_params, + refund_params: refund_params.map(Box::new), }, ) } diff --git a/engine/src/witness/common.rs b/engine/src/witness/common.rs index 60690923d6..56b2ba4857 100644 --- a/engine/src/witness/common.rs +++ b/engine/src/witness/common.rs @@ -1,3 +1,4 @@ +pub mod cf_parameters; pub mod chain_source; pub mod chunked_chain_source; pub mod epoch_source; diff --git a/engine/src/witness/common/cf_parameters.rs b/engine/src/witness/common/cf_parameters.rs new file mode 100644 index 0000000000..e84d88812f --- /dev/null +++ b/engine/src/witness/common/cf_parameters.rs @@ -0,0 +1,46 @@ +use cf_chains::{CcmAdditionalData, ChannelRefundParameters}; +use cf_primitives::{BasisPoints, Beneficiaries, DcaParameters}; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug)] +pub struct CfParameters { + /// CCMs may require additional data (for example CCMs to Solana require adding a list of + /// addresses). + pub ccm_additional_data: CcmData, + pub vault_swap_parameters: VaultSwapParameters, +} + +pub type CcmCfParameters = CfParameters; + +// TODO: Define this / implement it on the SC - PRO-1743. +pub type ShortId = u8; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, PartialEq, Debug)] +pub struct VaultSwapParameters { + pub refund_params: ChannelRefundParameters, + pub dca_params: Option, + pub boost_fee: Option, + pub broker_fees: Beneficiaries, +} + +#[cfg(test)] +mod tests { + use super::*; + use cf_chains::MAX_CCM_ADDITIONAL_DATA_LENGTH; + + const MAX_VAULT_SWAP_PARAMETERS_LENGTH: u32 = 1_000; + const MAX_CF_PARAM_LENGTH: u32 = + MAX_CCM_ADDITIONAL_DATA_LENGTH + MAX_VAULT_SWAP_PARAMETERS_LENGTH; + + #[test] + fn test_cf_parameters_max_length() { + assert!( + MAX_VAULT_SWAP_PARAMETERS_LENGTH as usize >= VaultSwapParameters::max_encoded_len() + ); + assert!(MAX_CF_PARAM_LENGTH as usize >= CfParameters::<()>::max_encoded_len()); + assert!( + MAX_VAULT_SWAP_PARAMETERS_LENGTH as usize >= VaultSwapParameters::max_encoded_len() + ); + } +} diff --git a/engine/src/witness/eth.rs b/engine/src/witness/eth.rs index f61b6c2642..d8e8c044ff 100644 --- a/engine/src/witness/eth.rs +++ b/engine/src/witness/eth.rs @@ -19,7 +19,10 @@ use crate::{ stream_api::{StreamApi, FINALIZED}, STATE_CHAIN_CONNECTION, }, - witness::evm::erc20_deposits::{flip::FlipEvents, usdc::UsdcEvents, usdt::UsdtEvents}, + witness::{ + common::cf_parameters::ShortId, + evm::erc20_deposits::{flip::FlipEvents, usdc::UsdcEvents, usdt::UsdtEvents}, + }, }; use super::{common::epoch_source::EpochSourceBuilder, evm::source::EvmSource}; @@ -218,8 +221,10 @@ where Ok(()) } -use cf_chains::{address::EncodedAddress, CcmDepositMetadata}; -use cf_primitives::{Asset, AssetAmount, TransactionHash}; +use cf_chains::{address::EncodedAddress, CcmDepositMetadata, ChannelRefundParameters}; +use cf_primitives::{ + Asset, AssetAmount, BasisPoints, Beneficiaries, DcaParameters, TransactionHash, +}; pub struct EthCallBuilder {} @@ -233,6 +238,11 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: TransactionHash, + _broker_fees: Beneficiaries, + refund_params: Option, + dca_params: Option, + // This is only to be checked in the pre-witnessed version + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall { state_chain_runtime::RuntimeCall::EthereumIngressEgress( pallet_cf_ingress_egress::Call::contract_swap_request { @@ -243,11 +253,11 @@ impl super::evm::vault::IngressCallBuilder for EthCallBuilder { deposit_metadata, tx_hash, deposit_details: Box::new(DepositDetails { tx_hashes: Some(vec![tx_hash.into()]) }), + // Defaulting to no broker fees until PRO-1743 is completed. broker_fees: Default::default(), - // TODO: use real parameters when we can decode them - boost_fee: 0, - dca_params: None, - refund_params: None, + boost_fee: boost_fee.unwrap_or_default(), + dca_params, + refund_params: refund_params.map(Box::new), }, ) } diff --git a/engine/src/witness/evm/vault.rs b/engine/src/witness/evm/vault.rs index 206679638d..543c138ccd 100644 --- a/engine/src/witness/evm/vault.rs +++ b/engine/src/witness/evm/vault.rs @@ -1,11 +1,12 @@ +use crate::evm::retry_rpc::EvmRetryRpcApi; +use codec::Decode; use ethers::types::Bloom; use sp_core::H256; use std::collections::HashMap; -use crate::evm::retry_rpc::EvmRetryRpcApi; - use super::{ super::common::{ + cf_parameters::*, chain_source::ChainClient, chunked_chain_source::chunked_by_vault::{builder::ChunkedByVaultBuilder, ChunkedByVault}, }, @@ -19,9 +20,9 @@ use cf_chains::{ address::{EncodedAddress, IntoForeignChainAddress}, eth::Address as EthereumAddress, evm::DepositDetails, - CcmChannelMetadata, CcmDepositMetadata, Chain, + CcmChannelMetadata, CcmDepositMetadata, Chain, ChannelRefundParameters, }; -use cf_primitives::{Asset, ForeignChain}; +use cf_primitives::{Asset, BasisPoints, DcaParameters, ForeignChain}; use ethers::prelude::*; use state_chain_runtime::{EthereumInstance, Runtime, RuntimeCall}; @@ -63,15 +64,24 @@ where dst_token, amount, sender: _, - cf_parameters: _, - }) => Some(CallBuilder::contract_swap_request( - native_asset, - try_into_primitive(amount)?, - try_into_primitive(dst_token)?, - try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, - None, - event.tx_hash.into(), - )), + cf_parameters, + }) => { + let CfParameters { ccm_additional_data: (), vault_swap_parameters } = + CfParameters::decode(&mut &cf_parameters[..])?; + + Some(CallBuilder::contract_swap_request( + native_asset, + try_into_primitive(amount)?, + try_into_primitive(dst_token)?, + try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, + None, + event.tx_hash.into(), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::SwapTokenFilter(SwapTokenFilter { dst_chain, dst_address, @@ -79,17 +89,26 @@ where src_token, amount, sender: _, - cf_parameters: _, - }) => Some(CallBuilder::contract_swap_request( - *(supported_assets - .get(&src_token) - .ok_or(anyhow!("Source token {src_token:?} not found"))?), - try_into_primitive(amount)?, - try_into_primitive(dst_token)?, - try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, - None, - event.tx_hash.into(), - )), + cf_parameters, + }) => { + let CfParameters { ccm_additional_data: (), vault_swap_parameters } = + CfParameters::decode(&mut &cf_parameters[..])?; + + Some(CallBuilder::contract_swap_request( + *(supported_assets + .get(&src_token) + .ok_or(anyhow!("Source token {src_token:?} not found"))?), + try_into_primitive(amount)?, + try_into_primitive(dst_token)?, + try_into_encoded_address(try_into_primitive(dst_chain)?, dst_address.to_vec())?, + None, + event.tx_hash.into(), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::XcallNativeFilter(XcallNativeFilter { dst_chain, dst_address, @@ -99,7 +118,10 @@ where message, gas_amount, cf_parameters, - }) => + }) => { + let CfParameters { ccm_additional_data, vault_swap_parameters } = + CcmCfParameters::decode(&mut &cf_parameters[..])?; + Some(CallBuilder::contract_swap_request( native_asset, try_into_primitive(amount)?, @@ -118,13 +140,16 @@ where .try_into() .map_err(|_| anyhow!("Failed to deposit CCM: `message` too long."))?, gas_budget: try_into_primitive(gas_amount)?, - cf_parameters: cf_parameters.0.to_vec().try_into().map_err(|_| { - anyhow!("Failed to deposit CCM: `cf_parameters` too long.") - })?, + ccm_additional_data, }, }), event.tx_hash.into(), - )), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::XcallTokenFilter(XcallTokenFilter { dst_chain, dst_address, @@ -135,7 +160,10 @@ where message, gas_amount, cf_parameters, - }) => + }) => { + let CfParameters { ccm_additional_data, vault_swap_parameters } = + CcmCfParameters::decode(&mut &cf_parameters[..])?; + Some(CallBuilder::contract_swap_request( *(supported_assets .get(&src_token) @@ -156,13 +184,16 @@ where .try_into() .map_err(|_| anyhow!("Failed to deposit CCM. Message too long."))?, gas_budget: try_into_primitive(gas_amount)?, - cf_parameters: cf_parameters.0.to_vec().try_into().map_err(|_| { - anyhow!("Failed to deposit CCM. cf_parameters too long.") - })?, + ccm_additional_data, }, }), event.tx_hash.into(), - )), + vault_swap_parameters.broker_fees, + Some(vault_swap_parameters.refund_params), + vault_swap_parameters.dca_params, + vault_swap_parameters.boost_fee, + )) + }, VaultEvents::TransferNativeFailedFilter(TransferNativeFailedFilter { recipient, amount, @@ -204,6 +235,10 @@ pub trait IngressCallBuilder { destination_address: EncodedAddress, deposit_metadata: Option, tx_hash: cf_primitives::TransactionHash, + broker_fees: cf_primitives::Beneficiaries, + refund_params: Option, + dca_params: Option, + boost_fee: Option, ) -> state_chain_runtime::RuntimeCall; fn vault_transfer_failed( @@ -268,7 +303,7 @@ impl ChunkedByVaultBuilder { process_call(call, epoch.index).await; }, Err(message) => { - tracing::error!("Ignoring vault contract event: {message}"); + tracing::warn!("Ignoring vault contract event: {message}"); }, } } diff --git a/state-chain/cf-integration-tests/src/solana.rs b/state-chain/cf-integration-tests/src/solana.rs index e4aa87e81b..7a9e02e8de 100644 --- a/state-chain/cf-integration-tests/src/solana.rs +++ b/state-chain/cf-integration-tests/src/solana.rs @@ -392,7 +392,7 @@ fn solana_ccm_fails_with_invalid_input() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 0u128, - cf_parameters: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), + ccm_additional_data: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), }, }; @@ -466,7 +466,7 @@ fn solana_ccm_fails_with_invalid_input() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 0u128, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: receiver.into(), is_writable: true }, remaining_accounts: vec![ SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: false }, @@ -515,7 +515,7 @@ fn solana_ccm_fails_with_invalid_input() { egress_id: (ForeignChain::Solana, 1u64), error: ExecutexSwapAndCallError::FailedToBuildCcmForSolana( SolanaTransactionBuildingError::InvalidCcm( - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ) ), }), @@ -691,7 +691,7 @@ fn solana_ccm_execution_error_can_trigger_fallback() { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8].try_into().unwrap(), gas_budget: 1_000_000_000u128, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x10; 32]), is_writable: true }, remaining_accounts: vec![ SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: false }, diff --git a/state-chain/cf-integration-tests/src/swapping.rs b/state-chain/cf-integration-tests/src/swapping.rs index 180b5b798d..e8f7f13e9c 100644 --- a/state-chain/cf-integration-tests/src/swapping.rs +++ b/state-chain/cf-integration-tests/src/swapping.rs @@ -374,7 +374,7 @@ fn can_process_ccm_via_swap_deposit_address() { let message = CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }; assert_ok!(Swapping::request_swap_deposit_address_with_affiliates( @@ -559,7 +559,7 @@ fn ccm_deposit_metadata_mock() -> CcmDepositMetadata { channel_metadata: CcmChannelMetadata { message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), gas_budget: 100_000_000, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, } } diff --git a/state-chain/chains/src/arb/api.rs b/state-chain/chains/src/arb/api.rs index 053827d9ee..e6a177a44d 100644 --- a/state-chain/chains/src/arb/api.rs +++ b/state-chain/chains/src/arb/api.rs @@ -70,7 +70,7 @@ where source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { let transfer_param = EncodableTransferAssetParams { asset: E::token_address(transfer_param.asset) diff --git a/state-chain/chains/src/btc/api.rs b/state-chain/chains/src/btc/api.rs index c60401de79..97a1f3d2d9 100644 --- a/state-chain/chains/src/btc/api.rs +++ b/state-chain/chains/src/btc/api.rs @@ -140,7 +140,7 @@ impl> ExecutexSwapAndCall for Bitc _source_address: Option, _gas_budget: ::ChainAmount, _message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { Err(ExecutexSwapAndCallError::Unsupported) } diff --git a/state-chain/chains/src/ccm_checker.rs b/state-chain/chains/src/ccm_checker.rs index b6a659c89e..9f0f617259 100644 --- a/state-chain/chains/src/ccm_checker.rs +++ b/state-chain/chains/src/ccm_checker.rs @@ -12,22 +12,22 @@ use sp_std::vec::Vec; #[derive(Copy, Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub enum CcmValidityError { - CannotDecodeCfParameters, + CannotDecodeCcmAdditionalData, CcmIsTooLong, - CfParametersContainsInvalidAccounts, + CcmAdditionalDataContainsInvalidAccounts, } pub trait CcmValidityCheck { fn check_and_decode( _ccm: &CcmChannelMetadata, _egress_asset: cf_primitives::Asset, - ) -> Result { - Ok(DecodedCfParameters::NotRequired) + ) -> Result { + Ok(DecodedCcmAdditionalData::NotRequired) } } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum DecodedCfParameters { +pub enum DecodedCcmAdditionalData { Solana(SolCcmAccounts), NotRequired, } @@ -41,11 +41,11 @@ impl CcmValidityCheck for CcmValidityChecker { fn check_and_decode( ccm: &CcmChannelMetadata, egress_asset: Asset, - ) -> Result { + ) -> Result { if ForeignChain::from(egress_asset) == ForeignChain::Solana { // Check if the cf_parameter can be decoded - let ccm_accounts = SolCcmAccounts::decode(&mut &ccm.cf_parameters.clone()[..]) - .map_err(|_| CcmValidityError::CannotDecodeCfParameters)?; + let ccm_accounts = SolCcmAccounts::decode(&mut &ccm.ccm_additional_data.clone()[..]) + .map_err(|_| CcmValidityError::CannotDecodeCcmAdditionalData)?; let asset: SolAsset = egress_asset .try_into() .expect("Only Solana chain's asset will be checked. This conversion must succeed."); @@ -61,9 +61,9 @@ impl CcmValidityCheck for CcmValidityChecker { return Err(CcmValidityError::CcmIsTooLong) } - Ok(DecodedCfParameters::Solana(ccm_accounts)) + Ok(DecodedCcmAdditionalData::Solana(ccm_accounts)) } else { - Ok(DecodedCfParameters::NotRequired) + Ok(DecodedCcmAdditionalData::NotRequired) } } } @@ -80,7 +80,7 @@ pub fn check_ccm_for_blacklisted_accounts( .iter() .any(|acc| acc.pubkey == blacklisted_account)) .then_some(()) - .ok_or(CcmValidityError::CfParametersContainsInvalidAccounts) + .ok_or(CcmValidityError::CcmAdditionalDataContainsInvalidAccounts) }) } @@ -98,7 +98,7 @@ mod test { let ccm = sol_test_values::ccm_parameter().channel_metadata; assert_eq!( CcmValidityChecker::check_and_decode(&ccm, Asset::Sol), - Ok(DecodedCfParameters::Solana(sol_test_values::ccm_accounts())) + Ok(DecodedCcmAdditionalData::Solana(sol_test_values::ccm_accounts())) ); } @@ -107,12 +107,12 @@ mod test { let ccm = CcmChannelMetadata { message: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), gas_budget: 1, - cf_parameters: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), + ccm_additional_data: vec![0x01, 0x02, 0x03, 0x04, 0x05].try_into().unwrap(), }; assert_err!( CcmValidityChecker::check_and_decode(&ccm, Asset::Sol), - CcmValidityError::CannotDecodeCfParameters + CcmValidityError::CannotDecodeCcmAdditionalData ); } @@ -121,7 +121,7 @@ mod test { let ccm = || CcmChannelMetadata { message: vec![0x01; MAX_CCM_BYTES_SOL].try_into().unwrap(), gas_budget: 0, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![], fallback_address: SolPubkey([0xf0; 32]), @@ -141,7 +141,7 @@ mod test { ); let mut invalid_ccm = ccm(); - invalid_ccm.cf_parameters = SolCcmAccounts { + invalid_ccm.ccm_additional_data = SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![SolCcmAddress { pubkey: SolPubkey([0x01; 32]), @@ -163,7 +163,7 @@ mod test { let ccm = || CcmChannelMetadata { message: vec![0x01; MAX_CCM_BYTES_USDC].try_into().unwrap(), gas_budget: 0, - cf_parameters: SolCcmAccounts { + ccm_additional_data: SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, fallback_address: SolPubkey([0xf0; 32]), remaining_accounts: vec![], @@ -183,7 +183,7 @@ mod test { ); let mut invalid_ccm = ccm(); - invalid_ccm.cf_parameters = SolCcmAccounts { + invalid_ccm.ccm_additional_data = SolCcmAccounts { cf_receiver: SolCcmAddress { pubkey: SolPubkey([0x01; 32]), is_writable: true }, remaining_accounts: vec![SolCcmAddress { pubkey: SolPubkey([0x01; 32]), @@ -219,31 +219,31 @@ mod test { // Always valid on other chains. assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Eth), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Btc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Flip), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Usdt), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::Usdc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::ArbUsdc), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); assert_ok!( CcmValidityChecker::check_and_decode(&ccm, Asset::ArbEth), - DecodedCfParameters::NotRequired + DecodedCcmAdditionalData::NotRequired ); } @@ -267,7 +267,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); let ccm_accounts = SolCcmAccounts { @@ -286,7 +286,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); // Agg key is blacklisted @@ -303,7 +303,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); let ccm_accounts = SolCcmAccounts { @@ -319,7 +319,7 @@ mod test { }; assert_err!( check_ccm_for_blacklisted_accounts(&ccm_accounts, blacklisted_accounts()), - CcmValidityError::CfParametersContainsInvalidAccounts + CcmValidityError::CcmAdditionalDataContainsInvalidAccounts ); } } diff --git a/state-chain/chains/src/dot/api.rs b/state-chain/chains/src/dot/api.rs index d42186f812..5cd8443f2e 100644 --- a/state-chain/chains/src/dot/api.rs +++ b/state-chain/chains/src/dot/api.rs @@ -128,7 +128,7 @@ where _source_address: Option, _gas_budget: ::ChainAmount, _message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { Err(ExecutexSwapAndCallError::Unsupported) } diff --git a/state-chain/chains/src/eth/api.rs b/state-chain/chains/src/eth/api.rs index 1244ac31b2..9eb4121f57 100644 --- a/state-chain/chains/src/eth/api.rs +++ b/state-chain/chains/src/eth/api.rs @@ -194,7 +194,7 @@ where source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { let transfer_param = EncodableTransferAssetParams { asset: E::token_address(transfer_param.asset) diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 8eb73ad7fd..2b284c2b56 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -550,7 +550,7 @@ pub trait ExecutexSwapAndCall: ApiCall { source_address: Option, gas_budget: C::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result; } @@ -589,10 +589,10 @@ pub enum SwapOrigin { } pub const MAX_CCM_MSG_LENGTH: u32 = 10_000; -pub const MAX_CCM_CF_PARAM_LENGTH: u32 = 1_000; +pub const MAX_CCM_ADDITIONAL_DATA_LENGTH: u32 = 1_000; pub type CcmMessage = BoundedVec>; -pub type CcmCfParameters = BoundedVec>; +pub type CcmAdditionalData = BoundedVec>; #[cfg(feature = "std")] mod bounded_hex { @@ -637,7 +637,7 @@ pub struct CcmChannelMetadata { feature = "std", serde(with = "bounded_hex", default, skip_serializing_if = "Vec::is_empty") )] - pub cf_parameters: CcmCfParameters, + pub ccm_additional_data: CcmAdditionalData, } #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 1b330d9ff9..b86ead8290 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -10,7 +10,7 @@ use sp_std::{vec, vec::Vec}; use crate::{ ccm_checker::{ check_ccm_for_blacklisted_accounts, CcmValidityCheck, CcmValidityChecker, CcmValidityError, - DecodedCfParameters, + DecodedCcmAdditionalData, }, sol::{ transaction_builder::SolanaTransactionBuilder, SolAddress, SolAmount, SolApiEnvironment, @@ -264,10 +264,10 @@ impl SolanaApi { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result { // For extra safety, re-verify the validity of the CCM message here - // and extract the decoded `ccm_accounts` from `cf_parameters`. + // and extract the decoded `ccm_accounts` from `ccm_additional_data`. let decoded_cf_params = CcmValidityChecker::check_and_decode( &CcmChannelMetadata { message: message @@ -275,7 +275,7 @@ impl SolanaApi { .try_into() .expect("This is parsed from bounded vec, therefore the size must fit"), gas_budget: 0, // This value is un-used by Solana - cf_parameters: cf_parameters + ccm_additional_data: ccm_additional_data .clone() .try_into() .expect("This is parsed from bounded vec, therefore the size must fit"), @@ -284,12 +284,14 @@ impl SolanaApi { ) .map_err(SolanaTransactionBuildingError::InvalidCcm)?; - // Always expects the `DecodedCfParameters::Solana(..)` variant of the decoded cf params. - let ccm_accounts = if let DecodedCfParameters::Solana(ccm_accounts) = decoded_cf_params { + // Always expects the `DecodedCcmAdditionalData::Solana(..)` variant of the decoded cf + // params. + let ccm_accounts = if let DecodedCcmAdditionalData::Solana(ccm_accounts) = decoded_cf_params + { Ok(ccm_accounts) } else { Err(SolanaTransactionBuildingError::InvalidCcm( - CcmValidityError::CannotDecodeCfParameters, + CcmValidityError::CannotDecodeCcmAdditionalData, )) }?; @@ -449,7 +451,7 @@ impl ExecutexSwapAndCall for SolanaApi source_address: Option, gas_budget: ::ChainAmount, message: Vec, - cf_parameters: Vec, + ccm_additional_data: Vec, ) -> Result { Self::ccm_transfer( transfer_param, @@ -457,7 +459,7 @@ impl ExecutexSwapAndCall for SolanaApi source_address, gas_budget, message, - cf_parameters, + ccm_additional_data, ) .map_err(|e| { log::error!("Failed to construct Solana CCM transfer transaction! \nError: {:?}", e); diff --git a/state-chain/chains/src/sol/sol_tx_core.rs b/state-chain/chains/src/sol/sol_tx_core.rs index 7a29fad8e3..14bddb13a8 100644 --- a/state-chain/chains/src/sol/sol_tx_core.rs +++ b/state-chain/chains/src/sol/sol_tx_core.rs @@ -950,7 +950,7 @@ pub mod sol_test_values { channel_metadata: CcmChannelMetadata { message: vec![124u8, 29u8, 15u8, 7u8].try_into().unwrap(), // CCM message gas_budget: 0u128, // unused - cf_parameters: codec::Encode::encode(&ccm_accounts()) + ccm_additional_data: codec::Encode::encode(&ccm_accounts()) .try_into() .expect("Test data cannot be too long"), // Extra addresses }, diff --git a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs index 22b95c7ec6..e5dc6a8942 100644 --- a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs +++ b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs @@ -328,7 +328,7 @@ mod benchmarks { channel_metadata: CcmChannelMetadata { message: vec![0x00].try_into().unwrap(), gas_budget: 1, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; let call = Call::::contract_swap_request { diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 4e1c786628..7b8388bb35 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -27,7 +27,7 @@ use cf_chains::{ }, assets::any::GetChainAssetMap, ccm_checker::CcmValidityCheck, - AllBatch, AllBatchError, CcmCfParameters, CcmChannelMetadata, CcmDepositMetadata, + AllBatch, AllBatchError, CcmAdditionalData, CcmChannelMetadata, CcmDepositMetadata, CcmFailReason, CcmMessage, Chain, ChannelLifecycleHooks, ChannelRefundParameters, ConsolidateCall, DepositChannel, ExecutexSwapAndCall, FetchAssetParams, ForeignChainAddress, SwapOrigin, TransferAssetParams, @@ -160,7 +160,7 @@ pub(crate) struct CrossChainMessage { pub source_chain: ForeignChain, pub source_address: Option, // Where funds might be returned to if the message fails. - pub cf_parameters: CcmCfParameters, + pub ccm_additional_data: CcmAdditionalData, pub gas_budget: C::ChainAmount, } @@ -1538,7 +1538,7 @@ impl, I: 'static> Pallet { ccm.source_address, ccm.gas_budget, ccm.message.to_vec(), - ccm.cf_parameters.to_vec(), + ccm.ccm_additional_data.to_vec(), ) { Ok(api_call) => { let broadcast_id = T::Broadcaster::threshold_sign_and_broadcast_with_callback( @@ -2383,7 +2383,7 @@ impl, I: 'static> EgressApi for Pallet { match maybe_ccm_with_gas_budget { Some(( CcmDepositMetadata { - channel_metadata: CcmChannelMetadata { message, cf_parameters, .. }, + channel_metadata: CcmChannelMetadata { message, ccm_additional_data, .. }, source_chain, source_address, .. @@ -2396,7 +2396,7 @@ impl, I: 'static> EgressApi for Pallet { amount, destination_address: destination_address.clone(), message, - cf_parameters, + ccm_additional_data, source_chain, source_address, gas_budget, diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index 30dde61e93..8269f302b3 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -117,7 +117,7 @@ fn blacklisted_asset_will_not_egress_via_ccm() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), }, }; @@ -151,7 +151,7 @@ fn blacklisted_asset_will_not_egress_via_ccm() { message: ccm.channel_metadata.message.clone(), source_chain: ForeignChain::Ethereum, source_address: ccm.source_address.clone(), - cf_parameters: ccm.channel_metadata.cf_parameters, + ccm_additional_data: ccm.channel_metadata.ccm_additional_data, gas_budget, }] ); @@ -487,7 +487,7 @@ fn can_egress_ccm() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), } }; @@ -507,7 +507,7 @@ fn can_egress_ccm() { amount, destination_address, message: ccm.channel_metadata.message.clone(), - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), source_chain: ForeignChain::Ethereum, source_address: Some(ForeignChainAddress::Eth([0xcf; 20].into())), gas_budget: GAS_BUDGET, @@ -1736,7 +1736,7 @@ fn do_not_process_more_ccm_swaps_than_allowed_by_limit() { channel_metadata: CcmChannelMetadata { message: vec![0x00, 0x01, 0x02].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), }, }; @@ -1824,7 +1824,7 @@ fn can_request_ccm_swap_via_extrinsic() { channel_metadata: CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: 1_000, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; @@ -1925,7 +1925,7 @@ fn failed_ccm_deposit_can_deposit_event() { channel_metadata: CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), }, }; diff --git a/state-chain/pallets/cf-swapping/src/tests.rs b/state-chain/pallets/cf-swapping/src/tests.rs index deaa23c184..ac79413503 100644 --- a/state-chain/pallets/cf-swapping/src/tests.rs +++ b/state-chain/pallets/cf-swapping/src/tests.rs @@ -216,7 +216,7 @@ fn generate_ccm_channel() -> CcmChannelMetadata { CcmChannelMetadata { message: vec![0x01].try_into().unwrap(), gas_budget: GAS_BUDGET, - cf_parameters: Default::default(), + ccm_additional_data: Default::default(), } } fn generate_ccm_deposit() -> CcmDepositMetadata { diff --git a/state-chain/pallets/cf-swapping/src/tests/ccm.rs b/state-chain/pallets/cf-swapping/src/tests/ccm.rs index 7b2c9712d8..f06002224a 100644 --- a/state-chain/pallets/cf-swapping/src/tests/ccm.rs +++ b/state-chain/pallets/cf-swapping/src/tests/ccm.rs @@ -66,7 +66,7 @@ pub(super) fn assert_ccm_egressed( amount: principal_amount, destination_address: (*EVM_OUTPUT_ADDRESS).clone(), message: vec![0x01].try_into().unwrap(), - cf_parameters: vec![].try_into().unwrap(), + ccm_additional_data: vec![].try_into().unwrap(), gas_budget, }, ); diff --git a/state-chain/primitives/src/lib.rs b/state-chain/primitives/src/lib.rs index 6daa6b210e..b5c584df1a 100644 --- a/state-chain/primitives/src/lib.rs +++ b/state-chain/primitives/src/lib.rs @@ -399,13 +399,26 @@ pub type Affiliates = BoundedVec, ConstU32>; pub type Beneficiaries = BoundedVec, ConstU32<{ MAX_AFFILIATES + 1 }>>; -#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, Debug, PartialEq, Eq, MaxEncodedLen, Encode, Decode, TypeInfo, Serialize, Deserialize, +)] pub struct Beneficiary { pub account: Id, pub bps: BasisPoints, } -#[derive(Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, Serialize, Deserialize)] +#[derive( + Clone, + RuntimeDebug, + PartialEq, + Eq, + Encode, + Decode, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, +)] pub struct DcaParameters { /// The number of individual swaps to be executed pub number_of_chunks: u32, diff --git a/state-chain/traits/src/mocks/api_call.rs b/state-chain/traits/src/mocks/api_call.rs index 095b58c28f..05ee49ecdc 100644 --- a/state-chain/traits/src/mocks/api_call.rs +++ b/state-chain/traits/src/mocks/api_call.rs @@ -149,7 +149,7 @@ impl ExecutexSwapAndCall for MockEthereumApiCall { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { if MockEvmEnvironment::lookup(transfer_param.asset).is_none() { Err(ExecutexSwapAndCallError::DispatchError(DispatchError::CannotLookup)) @@ -285,7 +285,7 @@ impl ExecutexSwapAndCall for MockBitcoinApiCall { source_address: Option, gas_budget: ::ChainAmount, message: Vec, - _cf_parameters: Vec, + _ccm_additional_data: Vec, ) -> Result { if MockBtcEnvironment::lookup(transfer_param.asset).is_none() { Err(ExecutexSwapAndCallError::DispatchError(DispatchError::CannotLookup)) diff --git a/state-chain/traits/src/mocks/egress_handler.rs b/state-chain/traits/src/mocks/egress_handler.rs index b150ef96e4..d6fdc50160 100644 --- a/state-chain/traits/src/mocks/egress_handler.rs +++ b/state-chain/traits/src/mocks/egress_handler.rs @@ -1,6 +1,6 @@ use super::{MockPallet, MockPalletStorage}; use crate::{EgressApi, ScheduledEgressDetails}; -use cf_chains::{CcmCfParameters, CcmDepositMetadata, CcmMessage, Chain}; +use cf_chains::{CcmAdditionalData, CcmDepositMetadata, CcmMessage, Chain}; use cf_primitives::{AssetAmount, EgressCounter}; use codec::{Decode, Encode}; use frame_support::sp_runtime::{ @@ -29,7 +29,7 @@ pub enum MockEgressParameter { amount: C::ChainAmount, destination_address: C::ChainAccount, message: CcmMessage, - cf_parameters: CcmCfParameters, + ccm_additional_data: CcmAdditionalData, gas_budget: C::ChainAmount, }, } @@ -95,7 +95,7 @@ impl EgressApi for MockEgressHandler { amount, destination_address, message: message.channel_metadata.message.clone(), - cf_parameters: message.channel_metadata.cf_parameters.clone(), + ccm_additional_data: message.channel_metadata.ccm_additional_data.clone(), gas_budget: *gas_budget, }, None => MockEgressParameter::::Swap { From 079a14965d5b1e9cbd0afb0e461fc8b3a2c02e61 Mon Sep 17 00:00:00 2001 From: Janislav Date: Tue, 29 Oct 2024 16:50:11 +0100 Subject: [PATCH 3/3] feature/PRO-1712/reject-tx (#5332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Handling of tainted transactions - Extended DepositChannelDetails with owner field - Added extrinsic to mark transaction as tainted - Handling deposit and save details for a refund if tx is tainted * tests: Added tests - Added tests to verify that tainted transactions can get detected for all possible swap types - Added tests to check that txs marked by other brokers are getting ignored * chore: Extended LpRegistration trait + Added to Ingress/Egress config * feat: Getting LP refund address * feature: ensure by lp and broker + added more tests * refactor: Don't error if tx is tainted * refactor: Using DoubleMap instead of Map * refactor: Using BadOrigin + Added unit test * refactor: Inline code + Add Deposit Witness to struct * refactor: Extended lp deposit with refund address - Taking refund address if we open a deposit channel as lp - Extended ChannelAction to take an optional refund address - Removed all dependencies regarding the LpRegistration trait (added last commit) - Refactored tests, benchmarks, etc * feature: Added benchmark * chore: Changes to benchmark * chore: generated mock weights * chore: Added POC for BTC swap rejecting. * chore: only allow mark transactions for BTC * feature: expire tainted transaction * test: refactored tests * chore: Extended RejectCall with DepositWitness * Revert "chore: Extended RejectCall with DepositWitness" This reverts commit 67873ff0cecabb6ff99b103eb41eef1b4df6586e. * chore: added draft for eth refund implementation * chore: Removed unused events * chore: Ensure only by broker * chore: removed broker from tainted tx struct * chore: Only clone owner * chore: Moved tx tainted check * feature: Added migration for DepositChannelLookup * refactor: Changed data structure + fixed migrations * chore: Handle LP refund address as requirement * chore: Made clippy happy 🙂 * chore: don't manipulate storage in place in iteration 🙅‍♂️ * test: Added migration test 🧪 * chore: changed pallet storage version 📀 * chore: bumped pallet storage version (again) * chore: using ScheduledTxForReject for refunding * refactor: Changed accounting of expired transactions * refactor: using translate for migration * refactor: using append, refactored test * feature: Added handling of boost channels * feature: Marking txs when prewitness and reject when we process the depo * feat: pre-witnessed rejection handling * chore: Fixed logic + added tests * tests: Refactor/Rearranged tests * chore: Using SECONDS_PER_BLOCK instead of static block seconds * chore: Addressed comments * chore: Fixed clippy in CI * chore: update comments * fix: don't allow report overwrite * chore: Renamed event * feat: improvements: - mark boosted transactions as boosted instead of using channel status - allow pallet config instead of relying on chain - add event for tx reports - only allow reporting of unseen transactions - add doc comments - renaming of types/events - remove unused error * chore: removed not needed RefundParameters, added test * refactor: Rejecting - Changed trait - Refactor AllBatch build - LogOrPanic if reject failed and safe details * chore: fix compiler errors * chore: subtract fees * fix: using deposit address * fix: addressed problems - Introduced NoChangeTransfer ApiCall - Fire event when tx was broadcasted - Handle on_broadcast_ready with no-op * chore: small improvements - Removed not needed derives - Clippy * wip: change depositDetails for btc to utxo * Revert "wip: change depositDetails for btc to utxo" This reverts commit 55de221263192eb97f9d2ab05925d6a6dd33e2a9. * refactor: added current status of tx_id refactor * tests: Moved screening feature tests to own file and test against btc * refactor: - Fire event if we can not reject tx - Split amount and fees in trait - Fix UTXO construct * chore: rearranged imports * refactors: - use Utxo and remove BtcDepositDetails - use saturating_sub for egress fee subtraction - use ChainAccount instead of ForeignChainAddress in RejectCall - simplify definitions of RejectCall using default method --------- Co-authored-by: Daniel Co-authored-by: Maxim Shishmarev --- Cargo.lock | 351 ++++++++-------- .../src/witnessing/state_chain.rs | 4 +- engine/src/witness/btc/deposits.rs | 10 +- engine/src/witness/btc/smart_contract.rs | 14 +- .../cf-integration-tests/src/broadcasting.rs | 12 +- .../cf-integration-tests/src/new_epoch.rs | 2 +- state-chain/chains/src/any.rs | 4 +- state-chain/chains/src/arb/api.rs | 5 + state-chain/chains/src/benchmarking_value.rs | 9 +- state-chain/chains/src/btc.rs | 18 +- state-chain/chains/src/btc/api.rs | 35 +- state-chain/chains/src/dot.rs | 2 + state-chain/chains/src/dot/api.rs | 5 + state-chain/chains/src/eth/api.rs | 5 + state-chain/chains/src/evm.rs | 8 + state-chain/chains/src/lib.rs | 27 +- state-chain/chains/src/mocks.rs | 6 + state-chain/chains/src/sol.rs | 7 +- state-chain/chains/src/sol/api.rs | 3 + state-chain/pallets/cf-environment/src/lib.rs | 8 +- .../pallets/cf-environment/src/tests.rs | 8 +- .../cf-ingress-egress/src/benchmarking.rs | 2 +- .../pallets/cf-ingress-egress/src/lib.rs | 163 +++++--- .../pallets/cf-ingress-egress/src/tests.rs | 306 +------------- .../cf-ingress-egress/src/tests/screening.rs | 375 ++++++++++++++++++ state-chain/runtime/src/chainflip.rs | 54 +-- state-chain/traits/src/lib.rs | 2 +- state-chain/traits/src/mocks/api_call.rs | 26 +- 28 files changed, 865 insertions(+), 606 deletions(-) create mode 100644 state-chain/pallets/cf-ingress-egress/src/tests/screening.rs diff --git a/Cargo.lock b/Cargo.lock index 8bfb53b61c..bbad0f58ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "approx" @@ -209,7 +209,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -435,7 +435,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "synstructure 0.13.1", ] @@ -458,7 +458,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -626,7 +626,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -684,7 +684,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -824,13 +824,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.24", + "prettyplease 0.2.22", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1069,7 +1069,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "syn_derive", ] @@ -1141,9 +1141,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -1153,9 +1153,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -1239,9 +1239,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "jobserver", "libc", @@ -1424,7 +1424,7 @@ name = "cf-runtime-macros" version = "0.1.0" dependencies = [ "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -1989,7 +1989,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2539,7 +2539,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2579,9 +2579,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.129" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdc8cca144dce1c4981b5c9ab748761619979e515c3d53b5df385c677d1d007" +checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" dependencies = [ "cc", "cxxbridge-flags", @@ -2591,9 +2591,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.129" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5764c3142ab44fcf857101d12c0ddf09c34499900557c764f5ad0597159d1fc" +checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1" dependencies = [ "cc", "codespan-reporting", @@ -2601,24 +2601,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "cxxbridge-flags" -version = "1.0.129" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d422aff542b4fa28c2ce8e5cc202d42dbf24702345c1fba3087b2d3f8a1b90ff" +checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" [[package]] name = "cxxbridge-macro" -version = "1.0.129" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1719100f31492cd6adeeab9a0f46cdbc846e615fdb66d7b398aa46ec7fdd06f" +checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2690,7 +2690,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2723,7 +2723,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2832,7 +2832,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2843,7 +2843,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2881,7 +2881,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -2989,7 +2989,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3037,7 +3037,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.82", + "syn 2.0.79", "termcolor", "toml 0.8.19", "walkdir", @@ -3226,7 +3226,7 @@ dependencies = [ "engine-upgrade-utils", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3302,7 +3302,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3526,14 +3526,14 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.24", + "prettyplease 0.2.22", "proc-macro2", "quote", "regex", "reqwest", "serde", "serde_json", - "syn 2.0.82", + "syn 2.0.79", "toml 0.8.19", "walkdir", ] @@ -3551,7 +3551,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -3577,7 +3577,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.82", + "syn 2.0.79", "tempfile", "thiserror", "tiny-keccak", @@ -3770,10 +3770,10 @@ dependencies = [ "blake2 0.10.6", "file-guard", "fs-err", - "prettyplease 0.2.24", + "prettyplease 0.2.22", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4157,7 +4157,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-substrate-1.15.2+2)", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4169,7 +4169,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4179,7 +4179,7 @@ source = "git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-sub dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4358,7 +4358,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -4988,9 +4988,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -5012,9 +5012,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", @@ -5039,7 +5039,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -5055,10 +5055,10 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "log", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -5073,7 +5073,7 @@ checksum = "6eea26c5d0b6ab9d72219f65000af310f042a740926f7b2fa3553e774036e2e7" dependencies = [ "derive_builder", "dns-lookup", - "hyper 0.14.31", + "hyper 0.14.30", "tokio", "tower-service", "tracing", @@ -5086,7 +5086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.31", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", @@ -5103,7 +5103,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.4.1", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -5211,7 +5211,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "log", "rand", "tokio", @@ -5572,7 +5572,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.23.2", "pin-project", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -5595,7 +5595,7 @@ dependencies = [ "beef", "futures-timer", "futures-util", - "hyper 0.14.31", + "hyper 0.14.30", "jsonrpsee-types 0.22.5", "pin-project", "rustc-hash", @@ -5643,7 +5643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ccf93fc4a0bfe05d851d37d7c32b7f370fe94336b52a2f0efc5f1981895c2e5" dependencies = [ "async-trait", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "jsonrpsee-core 0.22.5", "jsonrpsee-types 0.22.5", @@ -5665,12 +5665,12 @@ dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-rustls 0.27.3", "hyper-util", "jsonrpsee-core 0.23.2", "jsonrpsee-types 0.23.2", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-platform-verifier", "serde", "serde_json", @@ -5691,7 +5691,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -5705,7 +5705,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "jsonrpsee-core 0.23.2", "jsonrpsee-types 0.23.2", @@ -5899,9 +5899,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -6250,7 +6250,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6466,9 +6466,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9eda9dcf4f2a99787827661f312ac3219292549c2ee992bf9a6248ffb066bf7" +checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" dependencies = [ "nalgebra", ] @@ -6623,7 +6623,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6637,7 +6637,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6648,7 +6648,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6659,7 +6659,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6919,7 +6919,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -6931,7 +6931,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7134,12 +7134,13 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.33.1" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf139e93ad757869338ad85239cb1d6c067b23b94e5846e637ca6328ee4be60" +checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" dependencies = [ "approx", "matrixmultiply", + "nalgebra-macros", "num-complex", "num-rational", "num-traits", @@ -7147,6 +7148,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "names" version = "0.14.0" @@ -7378,7 +7390,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7449,7 +7461,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7545,9 +7557,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -7566,7 +7578,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -7586,9 +7598,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -8409,9 +8421,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" [[package]] name = "pathdiff" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" @@ -8459,9 +8471,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -8470,9 +8482,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -8480,22 +8492,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -8552,7 +8564,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8590,7 +8602,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8696,7 +8708,7 @@ dependencies = [ "polkavm-common 0.8.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8708,7 +8720,7 @@ dependencies = [ "polkavm-common 0.9.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8718,7 +8730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" dependencies = [ "polkavm-derive-impl 0.8.0", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8728,7 +8740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl 0.9.0", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8873,12 +8885,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8946,7 +8958,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -8957,14 +8969,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -9003,7 +9015,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9077,11 +9089,11 @@ dependencies = [ "multimap 0.10.0", "once_cell", "petgraph", - "prettyplease 0.2.24", + "prettyplease 0.2.22", "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.82", + "syn 2.0.79", "tempfile", ] @@ -9108,7 +9120,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9148,7 +9160,7 @@ dependencies = [ "futures-core", "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-system-resolver", "pin-project-lite", "thiserror", @@ -9478,9 +9490,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.27.5" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cccf17a692ce51b86564334614d72dcae1def0fd5ecebc9f02956da74352b5" +checksum = "dc6baebe319ef5e4b470f248335620098d1c2e9261e995be05f56f719ca4bdb2" dependencies = [ "arc-swap", "async-trait", @@ -9545,7 +9557,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -9631,7 +9643,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "hyper-tls", "ipnet", @@ -9917,9 +9929,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", @@ -9975,9 +9987,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-platform-verifier" @@ -9990,7 +10002,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.15", + "rustls 0.23.14", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", @@ -10029,9 +10041,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ruzstd" @@ -10171,7 +10183,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -10725,7 +10737,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.31", + "hyper 0.14.30", "hyper-rustls 0.24.2", "log", "num_cpus", @@ -10820,7 +10832,7 @@ dependencies = [ "governor", "http 1.1.0", "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "ip_network", "jsonrpsee 0.23.2", "log", @@ -11018,7 +11030,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11143,7 +11155,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11220,7 +11232,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.82", + "syn 2.0.79", "thiserror", ] @@ -11506,14 +11518,14 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -11579,7 +11591,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -11719,9 +11731,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.9.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ "approx", "num-complex", @@ -12041,7 +12053,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12314,7 +12326,7 @@ source = "git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-sub dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-substrate-1.15.2+2)", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12334,7 +12346,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12344,7 +12356,7 @@ source = "git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-sub dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12652,7 +12664,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12665,7 +12677,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -12930,7 +12942,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13239,7 +13251,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13298,7 +13310,7 @@ version = "0.17.0" source = "git+https://github.com/chainflip-io/polkadot-sdk.git?tag=chainflip-substrate-1.15.2+2#d4792faaa7ab3fbb9798dcc629564d182853690e" dependencies = [ "http-body-util", - "hyper 1.5.0", + "hyper 1.4.1", "hyper-util", "log", "prometheus", @@ -13389,16 +13401,16 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.82", + "syn 2.0.79", "thiserror", "tokio", ] [[package]] name = "subxt-core" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f41eb2e2eea6ed45649508cc735f92c27f1fcfb15229e75f8270ea73177345" +checksum = "3af3b36405538a36b424d229dc908d1396ceb0994c90825ce928709eac1a159a" dependencies = [ "base58", "blake2 0.10.6", @@ -13452,7 +13464,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13501,9 +13513,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -13519,7 +13531,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13548,7 +13560,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13650,7 +13662,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13788,7 +13800,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -13828,7 +13840,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -13999,7 +14011,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -14356,9 +14368,12 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.8.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] [[package]] name = "unicode-bidi" @@ -14536,9 +14551,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "w3f-bls" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" +checksum = "9c5da5fa2c6afa2c9158eaa7cd9aee249765eb32b5fb0c63ad8b9e79336a47ec" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -14597,7 +14612,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.30", "log", "mime", "mime_guess", @@ -14649,7 +14664,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -14683,7 +14698,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -15573,7 +15588,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] @@ -15593,7 +15608,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.79", ] [[package]] diff --git a/api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs b/api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs index 20a5829256..fafbfff21b 100644 --- a/api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs +++ b/api/bin/chainflip-ingress-egress-tracker/src/witnessing/state_chain.rs @@ -147,8 +147,8 @@ impl From> for WitnessInformation { amount: value.amount.into(), asset: value.asset.into(), deposit_details: Some(DepositDetails::Bitcoin { - tx_id: value.deposit_details.utxo_id.tx_id, - vout: value.deposit_details.utxo_id.vout, + tx_id: value.deposit_details.id.tx_id, + vout: value.deposit_details.id.vout, }), } } diff --git a/engine/src/witness/btc/deposits.rs b/engine/src/witness/btc/deposits.rs index 881928b29c..8d03c4b6c8 100644 --- a/engine/src/witness/btc/deposits.rs +++ b/engine/src/witness/btc/deposits.rs @@ -19,7 +19,7 @@ use crate::{ use bitcoin::{hashes::Hash, BlockHash}; use cf_chains::{ assets::btc, - btc::{deposit_address::DepositAddress, BtcDepositDetails, UtxoId}, + btc::{deposit_address::DepositAddress, Utxo, UtxoId}, Bitcoin, }; @@ -115,12 +115,14 @@ fn deposit_witnesses( .filter(|(_vout, tx_out)| tx_out.value.to_sat() > 0) .filter_map(|(vout, tx_out)| { deposit_addresses.get(tx_out.script_pubkey.as_bytes()).map(|deposit_address| { + let amount = tx_out.value.to_sat(); DepositWitness:: { deposit_address: deposit_address.script_pubkey(), asset: btc::Asset::Btc, - amount: tx_out.value.to_sat(), - deposit_details: BtcDepositDetails { - utxo_id: UtxoId { tx_id: tx.txid.to_byte_array().into(), vout }, + amount, + deposit_details: Utxo { + id: UtxoId { tx_id: tx.txid.to_byte_array().into(), vout }, + amount, deposit_address: deposit_address.clone(), }, } diff --git a/engine/src/witness/btc/smart_contract.rs b/engine/src/witness/btc/smart_contract.rs index 0f5633c140..3032b875f6 100644 --- a/engine/src/witness/btc/smart_contract.rs +++ b/engine/src/witness/btc/smart_contract.rs @@ -3,8 +3,8 @@ use cf_amm::common::{bounded_sqrt_price, sqrt_price_to_price}; use cf_chains::{ assets::btc::Asset as BtcAsset, btc::{ - deposit_address::DepositAddress, smart_contract_encoding::UtxoEncodedData, - BtcDepositDetails, ScriptPubkey, UtxoId, + deposit_address::DepositAddress, smart_contract_encoding::UtxoEncodedData, ScriptPubkey, + Utxo, UtxoId, }, ChannelRefundParameters, ForeignChainAddress, }; @@ -134,9 +134,10 @@ pub fn try_extract_contract_call( deposit_amount, destination_address: data.output_address, tx_hash: tx_id, - deposit_details: Box::new(BtcDepositDetails { + deposit_details: Box::new(Utxo { // we require the deposit to be the first UTXO - utxo_id: UtxoId { tx_id: tx_id.into(), vout: 0 }, + id: UtxoId { tx_id: tx_id.into(), vout: 0 }, + amount: deposit_amount, deposit_address: vault_address.clone(), }), deposit_metadata: None, // No ccm for BTC (yet?) @@ -274,8 +275,9 @@ mod tests { deposit_amount: DEPOSIT_AMOUNT, destination_address: MOCK_SWAP_PARAMS.output_address.clone(), tx_hash: tx.hash.to_byte_array(), - deposit_details: Box::new(BtcDepositDetails { - utxo_id: UtxoId { tx_id: tx.txid.to_byte_array().into(), vout: 0 }, + deposit_details: Box::new(Utxo { + id: UtxoId { tx_id: tx.txid.to_byte_array().into(), vout: 0 }, + amount: DEPOSIT_AMOUNT, deposit_address: vault_deposit_address, }), broker_fees: Default::default(), diff --git a/state-chain/cf-integration-tests/src/broadcasting.rs b/state-chain/cf-integration-tests/src/broadcasting.rs index 4f1b61210f..e4b739b633 100644 --- a/state-chain/cf-integration-tests/src/broadcasting.rs +++ b/state-chain/cf-integration-tests/src/broadcasting.rs @@ -1,6 +1,6 @@ use super::*; use cf_chains::{ - btc::{deposit_address::DepositAddress, ScriptPubkey}, + btc::{deposit_address::DepositAddress, ScriptPubkey, Utxo}, AllBatch, Bitcoin, ForeignChain, TransferAssetParams, }; use cf_primitives::{chains::assets::btc, AuthorityCount, BroadcastId}; @@ -26,11 +26,11 @@ fn bitcoin_broadcast_delay_works() { let epoch = Validator::epoch_index(); let bitcoin_agg_key = BitcoinThresholdSigner::keys(epoch).unwrap().current; let egress_id = (ForeignChain::Bitcoin, 1u64); - Environment::add_bitcoin_utxo_to_list( - 1_000_000_000_000u64, - Default::default(), - DepositAddress::new(bitcoin_agg_key, 0u32), - ); + Environment::add_bitcoin_utxo_to_list(Utxo { + id: Default::default(), + amount: 1_000_000_000_000u64, + deposit_address: DepositAddress::new(bitcoin_agg_key, 0u32), + }); // Cause bitcoin vault to rotate - but stop the broadcasting. let (bitcoin_call, _egress_ids) = AllBatch::::new_unsigned( diff --git a/state-chain/cf-integration-tests/src/new_epoch.rs b/state-chain/cf-integration-tests/src/new_epoch.rs index 932bc06447..978f667ba3 100644 --- a/state-chain/cf-integration-tests/src/new_epoch.rs +++ b/state-chain/cf-integration-tests/src/new_epoch.rs @@ -238,7 +238,7 @@ fn utxo(amount: BtcAmount, salt: u32, pub_key: Option<[u8; 32]>) -> Utxo { } fn add_utxo_amount(utxo: Utxo) { - Environment::add_bitcoin_utxo_to_list(utxo.amount, utxo.id, utxo.deposit_address); + Environment::add_bitcoin_utxo_to_list(utxo); } #[test] diff --git a/state-chain/chains/src/any.rs b/state-chain/chains/src/any.rs index fea3f39ce7..2371b8264d 100644 --- a/state-chain/chains/src/any.rs +++ b/state-chain/chains/src/any.rs @@ -1,7 +1,7 @@ use crate::{ address::{ForeignChainAddress, IntoForeignChainAddress}, none::NoneChainCrypto, - Chain, FeeRefundCalculator, + Chain, DepositDetailsToTransactionInId, FeeRefundCalculator, }; use codec::{FullCodec, MaxEncodedLen}; use frame_support::Parameter; @@ -52,3 +52,5 @@ impl IntoForeignChainAddress for ForeignChainAddress { address } } + +impl DepositDetailsToTransactionInId for () {} diff --git a/state-chain/chains/src/arb/api.rs b/state-chain/chains/src/arb/api.rs index e6a177a44d..c42dda9396 100644 --- a/state-chain/chains/src/arb/api.rs +++ b/state-chain/chains/src/arb/api.rs @@ -150,6 +150,11 @@ impl From> for Arb } } +impl RejectCall for ArbitrumApi where + E: EvmEnvironmentProvider + ReplayProtectionProvider +{ +} + macro_rules! map_over_api_variants { ( $self:expr, $var:pat_param, $var_method:expr $(,)* ) => { match $self { diff --git a/state-chain/chains/src/benchmarking_value.rs b/state-chain/chains/src/benchmarking_value.rs index 0fb0fabf5f..9af451aaff 100644 --- a/state-chain/chains/src/benchmarking_value.rs +++ b/state-chain/chains/src/benchmarking_value.rs @@ -15,7 +15,7 @@ use sp_std::vec; #[cfg(feature = "runtime-benchmarks")] use crate::{ address::{EncodedAddress, ForeignChainAddress}, - btc::{BtcDepositDetails, UtxoId}, + btc::{Utxo, UtxoId}, dot::PolkadotTransactionId, evm::{DepositDetails, EvmFetchId, EvmTransactionMetadata}, }; @@ -225,10 +225,11 @@ impl BenchmarkValue for DepositDetails { } #[cfg(feature = "runtime-benchmarks")] -impl BenchmarkValue for BtcDepositDetails { +impl BenchmarkValue for Utxo { fn benchmark_value() -> Self { - BtcDepositDetails { - utxo_id: UtxoId::benchmark_value(), + Utxo { + id: UtxoId::benchmark_value(), + amount: 10_000_000, deposit_address: crate::btc::deposit_address::DepositAddress::new([0; 32], 0), } } diff --git a/state-chain/chains/src/btc.rs b/state-chain/chains/src/btc.rs index 437ef73ffd..9789ddd423 100644 --- a/state-chain/chains/src/btc.rs +++ b/state-chain/chains/src/btc.rs @@ -7,8 +7,8 @@ pub mod utxo_selection; extern crate alloc; use self::deposit_address::DepositAddress; use crate::{ - benchmarking_value::BenchmarkValue, Chain, ChainCrypto, DepositChannel, FeeEstimationApi, - FeeRefundCalculator, RetryPolicy, + benchmarking_value::BenchmarkValue, Chain, ChainCrypto, DepositChannel, + DepositDetailsToTransactionInId, FeeEstimationApi, FeeRefundCalculator, RetryPolicy, }; use alloc::{collections::VecDeque, string::String}; use arrayref::array_ref; @@ -227,12 +227,6 @@ impl BitcoinFeeInfo { } } -#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug, PartialEq, Eq, MaxEncodedLen)] -pub struct BtcDepositDetails { - pub utxo_id: UtxoId, - pub deposit_address: DepositAddress, -} - impl Chain for Bitcoin { const NAME: &'static str = "Bitcoin"; const GAS_ASSET: Self::ChainAsset = assets::btc::Asset::Btc; @@ -250,7 +244,7 @@ impl Chain for Bitcoin { type ChainAccount = ScriptPubkey; type DepositFetchId = BitcoinFetchId; type DepositChannelState = DepositAddress; - type DepositDetails = BtcDepositDetails; + type DepositDetails = Utxo; type Transaction = BitcoinTransactionData; type TransactionMetadata = (); type TransactionRef = Hash; @@ -406,6 +400,12 @@ impl Utxo { } } +impl DepositDetailsToTransactionInId for Utxo { + fn deposit_id(&self) -> Option { + Some(self.id.tx_id) + } +} + #[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Clone, RuntimeDebug, PartialEq, Eq)] pub struct BitcoinOutput { pub amount: u64, diff --git a/state-chain/chains/src/btc/api.rs b/state-chain/chains/src/btc/api.rs index 97a1f3d2d9..d45fec6936 100644 --- a/state-chain/chains/src/btc/api.rs +++ b/state-chain/chains/src/btc/api.rs @@ -4,7 +4,7 @@ use super::{ deposit_address::DepositAddress, AggKey, Bitcoin, BitcoinCrypto, BitcoinOutput, BtcAmount, Utxo, BITCOIN_DUST_LIMIT, CHANGE_ADDRESS_SALT, }; -use crate::*; +use crate::{btc::BitcoinTransaction, *}; use frame_support::{CloneNoBound, DebugNoBound, EqNoBound, Never, PartialEqNoBound}; use sp_std::marker::PhantomData; @@ -13,6 +13,7 @@ use sp_std::marker::PhantomData; #[allow(clippy::large_enum_variant)] pub enum BitcoinApi { BatchTransfer(batch_transfer::BatchTransfer), + NoChangeTransfer(BitcoinTransaction), #[doc(hidden)] #[codec(skip)] _Phantom(PhantomData, Never), @@ -146,6 +147,25 @@ impl> ExecutexSwapAndCall for Bitc } } +impl> RejectCall for BitcoinApi +where + E: ChainEnvironment + + ChainEnvironment<(), AggKey>, +{ + fn new_unsigned( + deposit_details: ::DepositDetails, + refund_address: ::ChainAccount, + refund_amount: ::ChainAmount, + ) -> Result { + let agg_key = >::lookup(()).ok_or(RejectError::Other)?; + Ok(Self::NoChangeTransfer(BitcoinTransaction::create_new_unsigned( + &agg_key, + vec![deposit_details], + vec![BitcoinOutput { amount: refund_amount, script_pubkey: refund_address }], + ))) + } +} + // transfer_fallback is unsupported for Bitcoin. impl> TransferFallback for BitcoinApi { fn new_unsigned( @@ -159,7 +179,7 @@ impl ApiCall for BitcoinApi { fn threshold_signature_payload(&self) -> ::Payload { match self { BitcoinApi::BatchTransfer(tx) => tx.threshold_signature_payload(), - + BitcoinApi::NoChangeTransfer(tx) => tx.get_signing_payloads(), BitcoinApi::_Phantom(..) => unreachable!(), } } @@ -171,6 +191,10 @@ impl ApiCall for BitcoinApi { ) -> Self { match self { BitcoinApi::BatchTransfer(call) => call.signed(threshold_signature, signer).into(), + BitcoinApi::NoChangeTransfer(mut tx) => { + tx.add_signer_and_signatures(signer, threshold_signature.clone()); + Self::NoChangeTransfer(tx) + }, BitcoinApi::_Phantom(..) => unreachable!(), } } @@ -178,7 +202,7 @@ impl ApiCall for BitcoinApi { fn chain_encoded(&self) -> Vec { match self { BitcoinApi::BatchTransfer(call) => call.chain_encoded(), - + BitcoinApi::NoChangeTransfer(call) => call.clone().finalize(), BitcoinApi::_Phantom(..) => unreachable!(), } } @@ -186,7 +210,7 @@ impl ApiCall for BitcoinApi { fn is_signed(&self) -> bool { match self { BitcoinApi::BatchTransfer(call) => call.is_signed(), - + BitcoinApi::NoChangeTransfer(call) => call.is_signed(), BitcoinApi::_Phantom(..) => unreachable!(), } } @@ -194,6 +218,7 @@ impl ApiCall for BitcoinApi { fn transaction_out_id(&self) -> ::TransactionOutId { match self { BitcoinApi::BatchTransfer(call) => call.transaction_out_id(), + BitcoinApi::NoChangeTransfer(call) => call.txid(), BitcoinApi::_Phantom(..) => unreachable!(), } } @@ -205,6 +230,8 @@ impl ApiCall for BitcoinApi { fn signer(&self) -> Option<::AggKey> { match self { BitcoinApi::BatchTransfer(call) => call.signer(), + BitcoinApi::NoChangeTransfer(call) => + call.signer_and_signatures.as_ref().map(|(signer, _)| (*signer)), BitcoinApi::_Phantom(..) => unreachable!(), } } diff --git a/state-chain/chains/src/dot.rs b/state-chain/chains/src/dot.rs index 3eb846ab1f..d69f9335cb 100644 --- a/state-chain/chains/src/dot.rs +++ b/state-chain/chains/src/dot.rs @@ -520,6 +520,8 @@ pub enum PolkadotRuntimeCall { #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] pub enum SystemCall {} +impl DepositDetailsToTransactionInId for u32 {} + #[allow(non_camel_case_types)] #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] pub enum BalancesCall { diff --git a/state-chain/chains/src/dot/api.rs b/state-chain/chains/src/dot/api.rs index 5cd8443f2e..c8208a2d72 100644 --- a/state-chain/chains/src/dot/api.rs +++ b/state-chain/chains/src/dot/api.rs @@ -145,6 +145,11 @@ where } } +impl RejectCall for PolkadotApi where + E: PolkadotEnvironment + ReplayProtectionProvider +{ +} + #[macro_export] macro_rules! map_over_api_variants { ( $self:expr, $var:pat_param, $var_method:expr $(,)* ) => { diff --git a/state-chain/chains/src/eth/api.rs b/state-chain/chains/src/eth/api.rs index 9eb4121f57..34d8b47ffe 100644 --- a/state-chain/chains/src/eth/api.rs +++ b/state-chain/chains/src/eth/api.rs @@ -237,6 +237,11 @@ where } } +impl RejectCall for EthereumApi where + E: EvmEnvironmentProvider + ReplayProtectionProvider +{ +} + impl From> for EthereumApi { diff --git a/state-chain/chains/src/evm.rs b/state-chain/chains/src/evm.rs index c157129acc..e2227afd6e 100644 --- a/state-chain/chains/src/evm.rs +++ b/state-chain/chains/src/evm.rs @@ -21,6 +21,8 @@ use serde::{Deserialize, Serialize}; use sp_core::ConstBool; use sp_std::{convert::TryFrom, str, vec}; +use crate::DepositDetailsToTransactionInId; + #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, Default)] pub struct DepositDetails { // In the case of EVM Native Deposits (ETH or arbETH), because we need to detect ingresses by @@ -650,6 +652,12 @@ pub struct TransactionFee { pub gas_used: u128, } +impl DepositDetailsToTransactionInId for DepositDetails { + fn deposit_id(&self) -> Option { + None + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 2b284c2b56..ecf088d655 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -260,7 +260,10 @@ pub trait Chain: Member + Parameter + ChainInstanceAlias { type DepositChannelState: Member + Parameter + ChannelLifecycleHooks + Unpin; /// Extra data associated with a deposit. - type DepositDetails: Member + Parameter + BenchmarkValue; + type DepositDetails: Member + + Parameter + + BenchmarkValue + + DepositDetailsToTransactionInId; type Transaction: Member + Parameter + BenchmarkValue + FeeRefundCalculator; @@ -522,10 +525,26 @@ pub enum ConsolidationError { Other, } +#[derive(Debug)] +pub enum RejectError { + NotSupportedForAsset, + Other, +} + pub trait ConsolidateCall: ApiCall { fn consolidate_utxos() -> Result; } +pub trait RejectCall: ApiCall { + fn new_unsigned( + _deposit_details: C::DepositDetails, + _refund_address: C::ChainAccount, + _refund_amount: C::ChainAmount, + ) -> Result { + Err(RejectError::NotSupportedForAsset) + } +} + pub trait AllBatch: ApiCall { fn new_unsigned( fetch_params: Vec>, @@ -815,3 +834,9 @@ pub enum RequiresSignatureRefresh> { False, _Phantom(PhantomData, Never), } + +pub trait DepositDetailsToTransactionInId { + fn deposit_id(&self) -> Option { + None + } +} diff --git a/state-chain/chains/src/mocks.rs b/state-chain/chains/src/mocks.rs index b51b494cae..2566fc1514 100644 --- a/state-chain/chains/src/mocks.rs +++ b/state-chain/chains/src/mocks.rs @@ -429,3 +429,9 @@ impl, Call: ApiCall> } } } + +impl DepositDetailsToTransactionInId for [u8; 4] { + fn deposit_id(&self) -> Option<[u8; 4]> { + Some(*self) + } +} diff --git a/state-chain/chains/src/sol.rs b/state-chain/chains/src/sol.rs index 351807c50f..fa583e5762 100644 --- a/state-chain/chains/src/sol.rs +++ b/state-chain/chains/src/sol.rs @@ -6,7 +6,10 @@ use sp_std::{vec, vec::Vec}; use sol_prim::{AccountBump, SlotNumber}; -use crate::{address, assets, DepositChannel, FeeEstimationApi, FeeRefundCalculator, TypeInfo}; +use crate::{ + address, assets, DepositChannel, DepositDetailsToTransactionInId, FeeEstimationApi, + FeeRefundCalculator, TypeInfo, +}; use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; use frame_support::{sp_runtime::RuntimeDebug, Parameter}; use serde::{Deserialize, Serialize}; @@ -387,3 +390,5 @@ pub struct SolApiEnvironment { pub usdc_token_mint_pubkey: SolAddress, pub usdc_token_vault_ata: SolAddress, } + +impl DepositDetailsToTransactionInId for () {} diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index b86ead8290..174a959859 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -1,3 +1,4 @@ +use crate::RejectCall; use cf_runtime_utilities::log_or_panic; use codec::{Decode, Encode}; use core::marker::PhantomData; @@ -532,3 +533,5 @@ impl SetGovKeyWithAggKey for Solan }) } } + +impl RejectCall for SolanaApi {} diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 36e279aeae..600f165278 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -585,12 +585,8 @@ impl Pallet { }) } - pub fn add_bitcoin_utxo_to_list( - amount: BtcAmount, - utxo_id: UtxoId, - deposit_address: DepositAddress, - ) { - BitcoinAvailableUtxos::::append(Utxo { amount, id: utxo_id, deposit_address }); + pub fn add_bitcoin_utxo_to_list(utxo: Utxo) { + BitcoinAvailableUtxos::::append(utxo); } pub fn add_bitcoin_change_utxo(amount: BtcAmount, utxo_id: UtxoId, pubkey_x: [u8; 32]) { diff --git a/state-chain/pallets/cf-environment/src/tests.rs b/state-chain/pallets/cf-environment/src/tests.rs index 49b028d80a..1badb80228 100644 --- a/state-chain/pallets/cf-environment/src/tests.rs +++ b/state-chain/pallets/cf-environment/src/tests.rs @@ -28,11 +28,11 @@ fn utxo_with_key(pub_key: [u8; 32]) -> Utxo { } fn add_utxo_amount(amount: BtcAmount, salt: u32) { - Environment::add_bitcoin_utxo_to_list( + Environment::add_bitcoin_utxo_to_list(Utxo { + id: Default::default(), amount, - Default::default(), - DepositAddress::new(Default::default(), salt), - ); + deposit_address: DepositAddress::new(Default::default(), salt), + }); } #[test] diff --git a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs index e5dc6a8942..8f34aaf07d 100644 --- a/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs +++ b/state-chain/pallets/cf-ingress-egress/src/benchmarking.rs @@ -528,7 +528,7 @@ mod benchmarks { fn mark_transaction_as_tainted() { let caller = T::AccountRoleRegistry::whitelisted_caller_with_role(AccountRole::Broker).unwrap(); - let tx_id = <>::TargetChain as Chain>::DepositDetails::benchmark_value(); + let tx_id: TransactionInIdFor = TransactionInIdFor::::benchmark_value(); #[block] { diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 7b8388bb35..c65293795c 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -28,9 +28,9 @@ use cf_chains::{ assets::any::GetChainAssetMap, ccm_checker::CcmValidityCheck, AllBatch, AllBatchError, CcmAdditionalData, CcmChannelMetadata, CcmDepositMetadata, - CcmFailReason, CcmMessage, Chain, ChannelLifecycleHooks, ChannelRefundParameters, - ConsolidateCall, DepositChannel, ExecutexSwapAndCall, FetchAssetParams, ForeignChainAddress, - SwapOrigin, TransferAssetParams, + CcmFailReason, CcmMessage, Chain, ChainCrypto, ChannelLifecycleHooks, ChannelRefundParameters, + ConsolidateCall, DepositChannel, DepositDetailsToTransactionInId, ExecutexSwapAndCall, + FetchAssetParams, ForeignChainAddress, RejectCall, SwapOrigin, TransferAssetParams, }; use cf_primitives::{ Asset, AssetAmount, BasisPoints, Beneficiaries, BoostPoolTier, BroadcastId, ChannelId, @@ -145,7 +145,7 @@ pub struct TaintedTransactionDetails, I: 'static> { pub refund_address: Option, pub asset: TargetChainAsset, pub amount: TargetChainAmount, - pub tx_id: ::DepositDetails, + pub deposit_details: ::DepositDetails, } /// Cross-chain messaging requests. @@ -267,6 +267,9 @@ pub mod pallet { pub(crate) type TargetChainBlockNumber = <>::TargetChain as Chain>::ChainBlockNumber; + pub type TransactionInIdFor = + <<>::TargetChain as Chain>::ChainCrypto as ChainCrypto>::TransactionInId; + #[derive(Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct DepositWitness { pub deposit_address: C::ChainAccount, @@ -408,7 +411,8 @@ pub mod pallet { type ChainApiCall: AllBatch + ExecutexSwapAndCall + TransferFallback - + ConsolidateCall; + + ConsolidateCall + + RejectCall; /// Get the latest chain state of the target chain. type ChainTracking: GetBlockHeight @@ -562,7 +566,7 @@ pub mod pallet { Identity, T::AccountId, Blake2_128Concat, - ::DepositDetails, + TransactionInIdFor, TaintedTransactionStatus, OptionQuery, >; @@ -573,7 +577,7 @@ pub mod pallet { _, Twox64Concat, BlockNumberFor, - Vec<(T::AccountId, ::DepositDetails)>, + Vec<(T::AccountId, TransactionInIdFor)>, ValueQuery, >; @@ -582,6 +586,11 @@ pub mod pallet { pub(crate) type ScheduledTxForReject, I: 'static = ()> = StorageValue<_, Vec>, ValueQuery>; + /// Stores the details of the tainted transactions that failed to be rejected. + #[pallet::storage] + pub(crate) type FailedRejections, I: 'static = ()> = + StorageValue<_, Vec>, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -717,17 +726,24 @@ pub mod pallet { }, TaintedTransactionReportReceived { account_id: T::AccountId, - tx_id: ::DepositDetails, + tx_id: TransactionInIdFor, expires_at: BlockNumberFor, }, TaintedTransactionReportExpired { account_id: T::AccountId, - tx_id: ::DepositDetails, + tx_id: TransactionInIdFor, }, CcmFallbackScheduled { broadcast_id: BroadcastId, egress_details: ScheduledEgressDetails, }, + TaintedTransactionRejected { + broadcast_id: BroadcastId, + tx_id: ::DepositDetails, + }, + FailedToRejectTaintedTransaction { + tx_id: ::DepositDetails, + }, } #[derive(CloneNoBound, PartialEqNoBound, EqNoBound)] @@ -913,6 +929,35 @@ pub mod pallet { }, } } + + for tx in ScheduledTxForReject::::take() { + if let Some(Ok(refund_address)) = tx.refund_address.clone().map(TryInto::try_into) { + if let Ok(api_call) = + >::new_unsigned( + tx.deposit_details.clone(), + refund_address, + tx.amount + .saturating_sub(T::ChainTracking::estimate_egress_fee(tx.asset)), + ) { + let (broadcast_id, _) = + T::Broadcaster::threshold_sign_and_broadcast(api_call); + Self::deposit_event(Event::::TaintedTransactionRejected { + broadcast_id, + tx_id: tx.deposit_details, + }); + } else { + FailedRejections::::append(tx.clone()); + Self::deposit_event(Event::::FailedToRejectTaintedTransaction { + tx_id: tx.deposit_details, + }); + } + } else { + FailedRejections::::append(tx.clone()); + Self::deposit_event(Event::::FailedToRejectTaintedTransaction { + tx_id: tx.deposit_details, + }); + } + } } } @@ -1223,7 +1268,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::mark_transaction_as_tainted())] pub fn mark_transaction_as_tainted( origin: OriginFor, - tx_id: ::DepositDetails, + tx_id: TransactionInIdFor, ) -> DispatchResult { let account_id = T::AccountRoleRegistry::ensure_broker(origin)?; ensure!(T::AllowTransactionReports::get(), Error::::UnsupportedChain); @@ -1306,7 +1351,7 @@ impl, I: 'static> IngressSink for Pallet { impl, I: 'static> Pallet { fn mark_transaction_as_tainted_inner( account_id: T::AccountId, - tx_id: ::DepositDetails, + tx_id: TransactionInIdFor, ) -> DispatchResult { TaintedTransactions::::try_mutate(&account_id, &tx_id, |opt| { const UNSEEN: TaintedTransactionStatus = TaintedTransactionStatus::Unseen; @@ -1672,26 +1717,28 @@ impl, I: 'static> Pallet { } = DepositChannelLookup::::get(&deposit_address) .ok_or(Error::::InvalidDepositAddress)?; - if TaintedTransactions::::mutate(&owner, &deposit_details, |opt| { - match opt.as_mut() { - // Transaction has been reported, mark it as pre-witnessed. - Some(status @ TaintedTransactionStatus::Unseen) => { - *status = TaintedTransactionStatus::Prewitnessed; - true - }, - // Transaction has not been reported, mark it as boosted to prevent further - // reports. - None => { - let _ = opt.insert(TaintedTransactionStatus::Boosted); - false - }, - // Pre-witnessing twice or pre-witnessing after boosting is unlikely but - // possible. Either way we don't want to change the status. - Some(TaintedTransactionStatus::Prewitnessed) | - Some(TaintedTransactionStatus::Boosted) => true, + if let Some(tx_id) = deposit_details.deposit_id() { + if TaintedTransactions::::mutate(&owner, &tx_id, |opt| { + match opt.as_mut() { + // Transaction has been reported, mark it as pre-witnessed. + Some(status @ TaintedTransactionStatus::Unseen) => { + *status = TaintedTransactionStatus::Prewitnessed; + true + }, + // Transaction has not been reported, mark it as boosted to prevent further + // reports. + None => { + let _ = opt.insert(TaintedTransactionStatus::Boosted); + false + }, + // Pre-witnessing twice or pre-witnessing after boosting is unlikely but + // possible. Either way we don't want to change the status. + Some(TaintedTransactionStatus::Prewitnessed) | + Some(TaintedTransactionStatus::Boosted) => true, + } + }) { + continue; } - }) { - continue; } let prewitnessed_deposit_id = @@ -1915,33 +1962,37 @@ impl, I: 'static> Pallet { let channel_owner = deposit_channel_details.owner.clone(); - if matches!(TaintedTransactions::::take(&channel_owner, &deposit_details), + if let Some(tx_id) = deposit_details.deposit_id() { + if matches!(TaintedTransactions::::take(&channel_owner, &tx_id), Some(status) if status != TaintedTransactionStatus::Boosted) - { - let refund_address = match deposit_channel_details.action.clone() { - ChannelAction::Swap { refund_params, .. } => - refund_params.as_ref().map(|refund_params| refund_params.refund_address.clone()), - ChannelAction::CcmTransfer { refund_params, .. } => - refund_params.as_ref().map(|refund_params| refund_params.refund_address.clone()), - ChannelAction::LiquidityProvision { refund_address, .. } => refund_address, - }; + { + let refund_address = match deposit_channel_details.action.clone() { + ChannelAction::Swap { refund_params, .. } => refund_params + .as_ref() + .map(|refund_params| refund_params.refund_address.clone()), + ChannelAction::CcmTransfer { refund_params, .. } => refund_params + .as_ref() + .map(|refund_params| refund_params.refund_address.clone()), + ChannelAction::LiquidityProvision { refund_address, .. } => refund_address, + }; - let tainted_transaction_details = TaintedTransactionDetails { - refund_address, - amount: deposit_amount, - asset, - tx_id: deposit_details.clone(), - }; - ScheduledTxForReject::::append(tainted_transaction_details); + let tainted_transaction_details = TaintedTransactionDetails { + refund_address, + amount: deposit_amount, + asset, + deposit_details: deposit_details.clone(), + }; + ScheduledTxForReject::::append(tainted_transaction_details); - Self::deposit_event(Event::::DepositIgnored { - deposit_address: Some(deposit_address), - asset, - amount: deposit_amount, - deposit_details, - reason: DepositIgnoredReason::TransactionTainted, - }); - return Ok(()) + Self::deposit_event(Event::::DepositIgnored { + deposit_address: Some(deposit_address), + asset, + amount: deposit_amount, + deposit_details, + reason: DepositIgnoredReason::TransactionTainted, + }); + return Ok(()) + } } ScheduledEgressFetchOrTransfer::::append(FetchOrTransfer::::Fetch { @@ -1953,7 +2004,7 @@ impl, I: 'static> Pallet { Self::deposit_event(Event::::DepositFetchesScheduled { channel_id, asset }); // Add the deposit to the balance. - T::DepositHandler::on_deposit_made(deposit_details.clone(), deposit_amount); + T::DepositHandler::on_deposit_made(deposit_details.clone()); // We received a deposit on a channel. If channel has been boosted earlier // (i.e. awaiting finalisation), *and* the boosted amount matches the amount @@ -2074,7 +2125,7 @@ impl, I: 'static> Pallet { return; } - T::DepositHandler::on_deposit_made(deposit_details, deposit_amount); + T::DepositHandler::on_deposit_made(deposit_details); let destination_address_internal = match T::AddressConverter::decode_and_validate_address_for_asset( diff --git a/state-chain/pallets/cf-ingress-egress/src/tests.rs b/state-chain/pallets/cf-ingress-egress/src/tests.rs index 8269f302b3..a28b0afe30 100644 --- a/state-chain/pallets/cf-ingress-egress/src/tests.rs +++ b/state-chain/pallets/cf-ingress-egress/src/tests.rs @@ -1,29 +1,28 @@ mod boost; +mod screening; use crate::{ - mock_eth::*, BoostPoolId, BoostStatus, Call as PalletCall, ChannelAction, ChannelIdCounter, + mock_eth::*, BoostStatus, Call as PalletCall, ChannelAction, ChannelIdCounter, ChannelOpeningFee, CrossChainMessage, DepositAction, DepositChannelLookup, DepositChannelPool, DepositIgnoredReason, DepositWitness, DisabledEgressAssets, EgressDustLimit, Event as PalletEvent, FailedForeignChainCall, FailedForeignChainCalls, FetchOrTransfer, MinimumDeposit, Pallet, PalletConfigUpdate, PalletSafeMode, PrewitnessedDepositIdCounter, - ReportExpiresAt, ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, ScheduledTxForReject, - TaintedTransactionStatus, TaintedTransactions, TAINTED_TX_EXPIRATION_BLOCKS, + ScheduledEgressCcm, ScheduledEgressFetchOrTransfer, }; use cf_chains::{ address::{AddressConverter, EncodedAddress}, assets::eth::Asset as EthAsset, btc::{BitcoinNetwork, ScriptPubkey}, - evm::{self, DepositDetails, EvmFetchId}, + evm::{DepositDetails, EvmFetchId}, mocks::MockEthereum, CcmChannelMetadata, CcmFailReason, DepositChannel, ExecutexSwapAndCall, SwapOrigin, TransferAssetParams, }; -use cf_primitives::{AssetAmount, Beneficiaries, ChannelId, ForeignChain}; +use cf_primitives::{AssetAmount, ChannelId, ForeignChain}; use cf_test_utilities::{assert_events_eq, assert_has_event, assert_has_matching_event}; use cf_traits::{ mocks::{ self, - account_role_registry::MockAccountRoleRegistry, address_converter::MockAddressConverter, api_call::{MockEthAllBatch, MockEthereumApiCall, MockEvmEnvironment}, asset_converter::MockAssetConverter, @@ -35,17 +34,16 @@ use cf_traits::{ funding_info::MockFundingInfo, swap_request_api::{MockSwapRequest, MockSwapRequestHandler}, }, - AccountRoleRegistry, BalanceApi, DepositApi, EgressApi, EpochInfo, - FetchesTransfersLimitProvider, FundingInfo, GetBlockHeight, SafeMode, ScheduledEgressDetails, - SwapRequestType, + BalanceApi, DepositApi, EgressApi, EpochInfo, FetchesTransfersLimitProvider, FundingInfo, + GetBlockHeight, SafeMode, ScheduledEgressDetails, SwapRequestType, }; use frame_support::{ assert_err, assert_noop, assert_ok, traits::{Hooks, OriginTrait}, weights::Weight, }; -use sp_core::{H160, H256}; -use sp_runtime::{DispatchError, DispatchError::BadOrigin}; +use sp_core::H160; +use sp_runtime::DispatchError; const ALICE_ETH_ADDRESS: EthereumAddress = H160([100u8; 20]); const BOB_ETH_ADDRESS: EthereumAddress = H160([101u8; 20]); @@ -1981,289 +1979,3 @@ fn failed_ccm_deposit_can_deposit_event() { ); }); } - -#[test] -fn process_tainted_transaction_and_expect_refund() { - new_test_ext().execute_with(|| { - let (_, address) = request_address_and_deposit(BROKER, EthAsset::Eth); - let _ = DepositChannelLookup::::get(address).unwrap(); - - let deposit_details: cf_chains::evm::DepositDetails = Default::default(); - - assert_ok!(>::register_as_broker( - &BROKER, - )); - - assert_ok!(IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(BROKER), - Default::default(), - )); - - assert_ok!(IngressEgress::process_single_deposit( - address, - EthAsset::Eth, - DEFAULT_DEPOSIT_AMOUNT, - deposit_details, - Default::default() - )); - - assert_has_matching_event!( - Test, - RuntimeEvent::IngressEgress(crate::Event::::DepositIgnored { - deposit_address: _address, - asset: EthAsset::Eth, - amount: DEFAULT_DEPOSIT_AMOUNT, - deposit_details: _, - reason: DepositIgnoredReason::TransactionTainted, - }) - ); - - assert_eq!(ScheduledTxForReject::::decode_len(), Some(1)); - }); -} - -#[test] -fn only_broker_can_mark_transaction_as_tainted() { - new_test_ext().execute_with(|| { - assert_noop!( - IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(ALICE), - Default::default(), - ), - BadOrigin - ); - - assert_ok!(>::register_as_broker( - &BROKER, - )); - - assert_ok!(IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(BROKER), - Default::default(), - )); - }); -} - -#[test] -fn tainted_transactions_expire_if_not_witnessed() { - new_test_ext().execute_with(|| { - let tx_id = DepositDetails::default(); - let expiry_at = System::block_number() + TAINTED_TX_EXPIRATION_BLOCKS as u64; - - let (_, address) = request_address_and_deposit(BROKER, EthAsset::Eth); - let _ = DepositChannelLookup::::get(address).unwrap(); - - assert_ok!(>::register_as_broker( - &BROKER, - )); - - assert_ok!(IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(BROKER), - Default::default(), - )); - - System::set_block_number(expiry_at); - - IngressEgress::on_idle(expiry_at, Weight::MAX); - - assert!(!TaintedTransactions::::contains_key(BROKER, tx_id)); - - assert_has_event::(RuntimeEvent::IngressEgress( - crate::Event::TaintedTransactionReportExpired { - account_id: BROKER, - tx_id: Default::default(), - }, - )); - }); -} - -fn setup_boost_swap() -> ForeignChainAddress { - assert_ok!( - >::register_as_liquidity_provider( - &ALICE, - ) - ); - - assert_ok!(IngressEgress::create_boost_pools( - RuntimeOrigin::root(), - vec![BoostPoolId { asset: EthAsset::Eth, tier: 10 }], - )); - - ::Balance::try_credit_account(&ALICE, EthAsset::Eth.into(), 1000) - .unwrap(); - - let (_, address, _, _) = IngressEgress::request_swap_deposit_address( - EthAsset::Eth, - EthAsset::Eth.into(), - ForeignChainAddress::Eth(Default::default()), - Beneficiaries::new(), - BROKER, - None, - 10, - None, - None, - ) - .unwrap(); - - assert_ok!(IngressEgress::add_boost_funds( - RuntimeOrigin::signed(ALICE), - EthAsset::Eth, - 1000, - 10 - )); - - address -} - -#[test] -fn finalize_boosted_tx_if_tainted_after_prewitness() { - new_test_ext().execute_with(|| { - let tx_id = DepositDetails::default(); - - assert_ok!(>::register_as_broker( - &BROKER, - )); - - let address: ::ChainAccount = setup_boost_swap().try_into().unwrap(); - - let _ = IngressEgress::add_prewitnessed_deposits( - vec![DepositWitness { - deposit_address: address, - asset: EthAsset::Eth, - amount: DEFAULT_DEPOSIT_AMOUNT, - deposit_details: tx_id.clone(), - }], - 10, - ); - - assert_noop!( - IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), tx_id.clone(),), - crate::Error::::TransactionAlreadyPrewitnessed - ); - - assert_ok!(IngressEgress::process_single_deposit( - address, - EthAsset::Eth, - DEFAULT_DEPOSIT_AMOUNT, - tx_id, - Default::default() - )); - - assert_has_matching_event!( - Test, - RuntimeEvent::IngressEgress(crate::Event::DepositFinalised { - deposit_address: _, - asset: EthAsset::Eth, - .. - }) - ); - }); -} - -#[test] -fn reject_tx_if_tainted_before_prewitness() { - new_test_ext().execute_with(|| { - let tx_id = DepositDetails::default(); - - assert_ok!(>::register_as_broker( - &BROKER, - )); - - let address: ::ChainAccount = setup_boost_swap().try_into().unwrap(); - - assert_ok!(IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(BROKER), - tx_id.clone(), - )); - - let _ = IngressEgress::add_prewitnessed_deposits( - vec![DepositWitness { - deposit_address: address, - asset: EthAsset::Eth, - amount: DEFAULT_DEPOSIT_AMOUNT, - deposit_details: tx_id.clone(), - }], - 10, - ); - - assert_ok!(IngressEgress::process_single_deposit( - address, - EthAsset::Eth, - DEFAULT_DEPOSIT_AMOUNT, - tx_id, - Default::default() - )); - - assert_has_matching_event!( - Test, - RuntimeEvent::IngressEgress(crate::Event::DepositIgnored { - deposit_address: _, - asset: EthAsset::Eth, - amount: DEFAULT_DEPOSIT_AMOUNT, - deposit_details: _, - reason: DepositIgnoredReason::TransactionTainted, - }) - ); - }); -} - -#[test] -fn do_not_expire_tainted_transactions_if_prewitnessed() { - new_test_ext().execute_with(|| { - let tx_id = DepositDetails::default(); - let expiry_at = System::block_number() + TAINTED_TX_EXPIRATION_BLOCKS as u64; - - TaintedTransactions::::insert( - BROKER, - &tx_id, - TaintedTransactionStatus::Prewitnessed, - ); - - ReportExpiresAt::::insert(expiry_at, vec![(BROKER, tx_id.clone())]); - - IngressEgress::on_idle(expiry_at, Weight::MAX); - - assert!(TaintedTransactions::::contains_key(BROKER, tx_id)); - }); -} - -#[test] -fn can_not_report_transaction_after_witnessing() { - new_test_ext().execute_with(|| { - assert_ok!(>::register_as_broker( - &BROKER, - )); - let unreported = evm::DepositDetails { tx_hashes: Some(vec![H256::random()]) }; - let unseen = evm::DepositDetails { tx_hashes: Some(vec![H256::random()]) }; - let prewitnessed = evm::DepositDetails { tx_hashes: Some(vec![H256::random()]) }; - let boosted = evm::DepositDetails { tx_hashes: Some(vec![H256::random()]) }; - - TaintedTransactions::::insert(BROKER, &unseen, TaintedTransactionStatus::Unseen); - TaintedTransactions::::insert( - BROKER, - &prewitnessed, - TaintedTransactionStatus::Prewitnessed, - ); - TaintedTransactions::::insert( - BROKER, - &boosted, - TaintedTransactionStatus::Boosted, - ); - - assert_ok!(IngressEgress::mark_transaction_as_tainted( - OriginTrait::signed(BROKER), - unreported, - )); - assert_ok!( - IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), unseen,) - ); - assert_noop!( - IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), prewitnessed,), - crate::Error::::TransactionAlreadyPrewitnessed - ); - assert_noop!( - IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), boosted,), - crate::Error::::TransactionAlreadyPrewitnessed - ); - }); -} diff --git a/state-chain/pallets/cf-ingress-egress/src/tests/screening.rs b/state-chain/pallets/cf-ingress-egress/src/tests/screening.rs new file mode 100644 index 0000000000..4daf026526 --- /dev/null +++ b/state-chain/pallets/cf-ingress-egress/src/tests/screening.rs @@ -0,0 +1,375 @@ +use crate::{ + mock_btc::*, + tests::{ALICE, BROKER}, + BoostPoolId, DepositChannelLookup, DepositIgnoredReason, DepositWitness, ReportExpiresAt, + ScheduledTxForReject, TaintedTransactionDetails, TaintedTransactionStatus, TaintedTransactions, + TAINTED_TX_EXPIRATION_BLOCKS, +}; + +use frame_support::{ + assert_noop, assert_ok, + traits::{Hooks, OriginTrait}, + weights::Weight, +}; + +use cf_chains::{ + btc::{deposit_address::DepositAddress, Hash, ScriptPubkey, UtxoId}, + ForeignChainAddress, +}; + +use cf_traits::{ + mocks::account_role_registry::MockAccountRoleRegistry, AccountRoleRegistry, BalanceApi, + DepositApi, +}; + +use cf_primitives::{chains::assets::btc, Beneficiaries, ChannelId}; +use cf_test_utilities::{assert_has_event, assert_has_matching_event}; +use sp_runtime::DispatchError::BadOrigin; + +const DEFAULT_DEPOSIT_AMOUNT: u64 = 1_000; +const DEFAULT_BTC_ADDRESS: [u8; 20] = [0; 20]; + +mod helpers { + + use super::*; + use cf_chains::btc::Utxo; + + pub fn generate_btc_deposit(tx_id: Hash) -> Utxo { + Utxo { + amount: DEFAULT_DEPOSIT_AMOUNT, + id: UtxoId { tx_id, vout: 0 }, + deposit_address: DepositAddress { pubkey_x: [0; 32], script_path: None }, + } + } + + pub fn request_address_and_deposit( + who: ChannelId, + asset: btc::Asset, + deposit_details: Utxo, + ) -> (ChannelId, ::ChainAccount) { + let (id, address, ..) = IngressEgress::request_liquidity_deposit_address( + who, + asset, + 0, + ForeignChainAddress::Btc(ScriptPubkey::P2SH(DEFAULT_BTC_ADDRESS)), + ) + .unwrap(); + let address: ::ChainAccount = address.try_into().unwrap(); + assert_ok!(IngressEgress::process_single_deposit( + address.clone(), + asset, + DEFAULT_DEPOSIT_AMOUNT, + deposit_details, + Default::default() + )); + (id, address) + } + + pub fn setup_boost_swap() -> ForeignChainAddress { + assert_ok!( + >::register_as_liquidity_provider( + &ALICE, + ) + ); + + assert_ok!(IngressEgress::create_boost_pools( + RuntimeOrigin::root(), + vec![BoostPoolId { asset: btc::Asset::Btc, tier: 10 }], + )); + + ::Balance::try_credit_account(&ALICE, btc::Asset::Btc.into(), 1000) + .unwrap(); + + let (_, address, _, _) = IngressEgress::request_swap_deposit_address( + btc::Asset::Btc, + btc::Asset::Btc.into(), + ForeignChainAddress::Btc(ScriptPubkey::P2SH(DEFAULT_BTC_ADDRESS)), + Beneficiaries::new(), + BROKER, + None, + 10, + None, + None, + ) + .unwrap(); + + assert_ok!(IngressEgress::add_boost_funds( + RuntimeOrigin::signed(ALICE), + btc::Asset::Btc, + 1000, + 10 + )); + + address + } +} + +#[test] +fn process_tainted_transaction_and_expect_refund() { + new_test_ext().execute_with(|| { + let tx_in_id = Hash::random(); + let deposit_details = helpers::generate_btc_deposit(tx_in_id); + let (_, address) = + helpers::request_address_and_deposit(BROKER, btc::Asset::Btc, deposit_details.clone()); + let _ = DepositChannelLookup::::get(address.clone()).unwrap(); + + assert_ok!(>::register_as_broker( + &BROKER, + )); + + assert_ok!(IngressEgress::mark_transaction_as_tainted( + OriginTrait::signed(BROKER), + tx_in_id, + )); + + assert_ok!(IngressEgress::process_single_deposit( + address, + btc::Asset::Btc, + DEFAULT_DEPOSIT_AMOUNT, + deposit_details, + Default::default() + )); + + assert_has_matching_event!( + Test, + RuntimeEvent::IngressEgress(crate::Event::::DepositIgnored { + deposit_address: _address, + asset: btc::Asset::Btc, + amount: DEFAULT_DEPOSIT_AMOUNT, + deposit_details: _, + reason: DepositIgnoredReason::TransactionTainted, + }) + ); + + assert_eq!(ScheduledTxForReject::::decode_len(), Some(1)); + }); +} + +#[test] +fn finalize_boosted_tx_if_tainted_after_prewitness() { + new_test_ext().execute_with(|| { + let tx_id = Hash::random(); + let deposit_details = helpers::generate_btc_deposit(tx_id); + + assert_ok!(>::register_as_broker( + &BROKER, + )); + + let address: ::ChainAccount = + helpers::setup_boost_swap().try_into().unwrap(); + + let _ = IngressEgress::add_prewitnessed_deposits( + vec![DepositWitness { + deposit_address: address.clone(), + asset: btc::Asset::Btc, + amount: DEFAULT_DEPOSIT_AMOUNT, + deposit_details: deposit_details.clone(), + }], + 10, + ); + + assert_noop!( + IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), tx_id,), + crate::Error::::TransactionAlreadyPrewitnessed + ); + + assert_ok!(IngressEgress::process_single_deposit( + address, + btc::Asset::Btc, + DEFAULT_DEPOSIT_AMOUNT, + deposit_details, + Default::default() + )); + + assert_has_matching_event!( + Test, + RuntimeEvent::IngressEgress(crate::Event::DepositFinalised { + deposit_address: _, + asset: btc::Asset::Btc, + .. + }) + ); + }); +} + +#[test] +fn reject_tx_if_tainted_before_prewitness() { + new_test_ext().execute_with(|| { + let tx_id = Hash::random(); + let deposit_details = helpers::generate_btc_deposit(tx_id); + + assert_ok!(>::register_as_broker( + &BROKER, + )); + + let address: ::ChainAccount = + helpers::setup_boost_swap().try_into().unwrap(); + + assert_ok!(IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), tx_id,)); + + let _ = IngressEgress::add_prewitnessed_deposits( + vec![DepositWitness { + deposit_address: address.clone(), + asset: btc::Asset::Btc, + amount: DEFAULT_DEPOSIT_AMOUNT, + deposit_details: deposit_details.clone(), + }], + 10, + ); + + assert_ok!(IngressEgress::process_single_deposit( + address, + btc::Asset::Btc, + DEFAULT_DEPOSIT_AMOUNT, + deposit_details, + Default::default() + )); + + assert_has_matching_event!( + Test, + RuntimeEvent::IngressEgress(crate::Event::DepositIgnored { + deposit_address: _, + asset: btc::Asset::Btc, + amount: DEFAULT_DEPOSIT_AMOUNT, + deposit_details: _, + reason: DepositIgnoredReason::TransactionTainted, + }) + ); + }); +} + +#[test] +fn tainted_transactions_expire_if_not_witnessed() { + new_test_ext().execute_with(|| { + let tx_id = Hash::random(); + let deposit_details = helpers::generate_btc_deposit(tx_id); + let expiry_at = System::block_number() + TAINTED_TX_EXPIRATION_BLOCKS as u64; + + let (_, address) = + helpers::request_address_and_deposit(BROKER, btc::Asset::Btc, deposit_details); + let _ = DepositChannelLookup::::get(address).unwrap(); + + assert_ok!(>::register_as_broker( + &BROKER, + )); + + assert_ok!(IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), tx_id,)); + + System::set_block_number(expiry_at); + + IngressEgress::on_idle(expiry_at, Weight::MAX); + + assert!(!TaintedTransactions::::contains_key(BROKER, tx_id)); + + assert_has_event::(RuntimeEvent::IngressEgress( + crate::Event::TaintedTransactionReportExpired { account_id: BROKER, tx_id }, + )); + }); +} + +#[test] +fn only_broker_can_mark_transaction_as_tainted() { + new_test_ext().execute_with(|| { + assert_noop!( + IngressEgress::mark_transaction_as_tainted( + OriginTrait::signed(ALICE), + Default::default(), + ), + BadOrigin + ); + + assert_ok!(>::register_as_broker( + &BROKER, + )); + + assert_ok!(IngressEgress::mark_transaction_as_tainted( + OriginTrait::signed(BROKER), + Default::default(), + )); + }); +} + +#[test] +fn do_not_expire_tainted_transactions_if_prewitnessed() { + new_test_ext().execute_with(|| { + let tx_id = Hash::random(); + let expiry_at = System::block_number() + TAINTED_TX_EXPIRATION_BLOCKS as u64; + + TaintedTransactions::::insert( + BROKER, + tx_id, + TaintedTransactionStatus::Prewitnessed, + ); + + ReportExpiresAt::::insert(expiry_at, vec![(BROKER, tx_id)]); + + IngressEgress::on_idle(expiry_at, Weight::MAX); + + assert!(TaintedTransactions::::contains_key(BROKER, tx_id)); + }); +} + +#[test] +fn can_not_report_transaction_after_witnessing() { + new_test_ext().execute_with(|| { + assert_ok!(>::register_as_broker( + &BROKER, + )); + + let unreported = Hash::random(); + let unseen = Hash::random(); + let prewitnessed = Hash::random(); + let boosted = Hash::random(); + + TaintedTransactions::::insert(BROKER, unseen, TaintedTransactionStatus::Unseen); + TaintedTransactions::::insert( + BROKER, + prewitnessed, + TaintedTransactionStatus::Prewitnessed, + ); + TaintedTransactions::::insert(BROKER, boosted, TaintedTransactionStatus::Boosted); + + assert_ok!(IngressEgress::mark_transaction_as_tainted( + OriginTrait::signed(BROKER), + unreported, + )); + assert_ok!( + IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), unseen,) + ); + assert_noop!( + IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), prewitnessed,), + crate::Error::::TransactionAlreadyPrewitnessed + ); + assert_noop!( + IngressEgress::mark_transaction_as_tainted(OriginTrait::signed(BROKER), boosted,), + crate::Error::::TransactionAlreadyPrewitnessed + ); + }); +} + +#[test] +fn send_funds_back_after_they_have_been_rejected() { + new_test_ext().execute_with(|| { + let deposit_details = helpers::generate_btc_deposit(Hash::random()); + let tainted_tx_details = TaintedTransactionDetails { + refund_address: Some(ForeignChainAddress::Btc(ScriptPubkey::P2SH(DEFAULT_BTC_ADDRESS))), + amount: DEFAULT_DEPOSIT_AMOUNT, + asset: btc::Asset::Btc, + deposit_details, + }; + + ScheduledTxForReject::::append(tainted_tx_details); + + IngressEgress::on_finalize(1); + + assert_eq!(ScheduledTxForReject::::decode_len(), None); + + assert_has_matching_event!( + Test, + RuntimeEvent::IngressEgress(crate::Event::TaintedTransactionRejected { + broadcast_id: _, + tx_id: _, + }) + ); + }); +} diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index 373cef9289..35e20db33c 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -31,7 +31,7 @@ use cf_chains::{ assets::any::ForeignChainAndAsset, btc::{ api::{BitcoinApi, SelectedUtxosAndChangeAmount, UtxoSelectionType}, - Bitcoin, BitcoinCrypto, BitcoinFeeInfo, BitcoinTransactionData, BtcDepositDetails, UtxoId, + Bitcoin, BitcoinCrypto, BitcoinFeeInfo, BitcoinTransactionData, Utxo, UtxoId, }, dot::{ api::PolkadotApi, Polkadot, PolkadotAccountId, PolkadotCrypto, PolkadotReplayProtection, @@ -768,15 +768,8 @@ pub struct DepositHandler; impl OnDeposit for DepositHandler {} impl OnDeposit for DepositHandler {} impl OnDeposit for DepositHandler { - fn on_deposit_made( - deposit_details: BtcDepositDetails, - amount: ::ChainAmount, - ) { - Environment::add_bitcoin_utxo_to_list( - amount, - deposit_details.utxo_id, - deposit_details.deposit_address, - ) + fn on_deposit_made(utxo: Utxo) { + Environment::add_bitcoin_utxo_to_list(utxo) } } impl OnDeposit for DepositHandler {} @@ -818,29 +811,26 @@ impl OnBroadcastReady for BroadcastReadyProvider { type ApiCall = BitcoinApi; fn on_broadcast_ready(api_call: &Self::ApiCall) { - match api_call { - BitcoinApi::BatchTransfer(batch_transfer) => { - let tx_id = batch_transfer.bitcoin_transaction.txid(); - let outputs = &batch_transfer.bitcoin_transaction.outputs; - let btc_key = pallet_cf_threshold_signature::Pallet::::keys( - pallet_cf_threshold_signature::Pallet::::current_key_epoch() - .expect("We should always have an epoch set")).expect("We should always have a key set for the current epoch"); - for (i, output) in outputs.iter().enumerate() { - if [ - ScriptPubkey::Taproot(btc_key.previous.unwrap_or_default()), - ScriptPubkey::Taproot(btc_key.current), - ] - .contains(&output.script_pubkey) - { - Environment::add_bitcoin_change_utxo( - output.amount, - UtxoId { tx_id, vout: i as u32 }, - batch_transfer.change_utxo_key, - ); - } + if let BitcoinApi::BatchTransfer(batch_transfer) = api_call { + let tx_id = batch_transfer.bitcoin_transaction.txid(); + let outputs = &batch_transfer.bitcoin_transaction.outputs; + let btc_key = pallet_cf_threshold_signature::Pallet::::keys( + pallet_cf_threshold_signature::Pallet::::current_key_epoch() + .expect("We should always have an epoch set")).expect("We should always have a key set for the current epoch"); + for (i, output) in outputs.iter().enumerate() { + if [ + ScriptPubkey::Taproot(btc_key.previous.unwrap_or_default()), + ScriptPubkey::Taproot(btc_key.current), + ] + .contains(&output.script_pubkey) + { + Environment::add_bitcoin_change_utxo( + output.amount, + UtxoId { tx_id, vout: i as u32 }, + batch_transfer.change_utxo_key, + ); } - }, - _ => unreachable!(), + } } } } diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index d676d165e6..aa8679c210 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -898,7 +898,7 @@ pub trait FlipBurnInfo { /// The trait implementation is intentionally no-op by default pub trait OnDeposit { - fn on_deposit_made(_deposit_details: C::DepositDetails, _amount: C::ChainAmount) {} + fn on_deposit_made(_deposit_details: C::DepositDetails) {} } pub trait NetworkEnvironmentProvider { diff --git a/state-chain/traits/src/mocks/api_call.rs b/state-chain/traits/src/mocks/api_call.rs index 05ee49ecdc..63dbd93b6e 100644 --- a/state-chain/traits/src/mocks/api_call.rs +++ b/state-chain/traits/src/mocks/api_call.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use cf_chains::{ btc::BitcoinCrypto, evm::EvmCrypto, AllBatch, AllBatchError, ApiCall, Bitcoin, Chain, ChainCrypto, ChainEnvironment, ConsolidationError, Ethereum, ExecutexSwapAndCall, - ExecutexSwapAndCallError, FetchAssetParams, ForeignChainAddress, TransferAssetParams, - TransferFallback, TransferFallbackError, + ExecutexSwapAndCallError, FetchAssetParams, ForeignChainAddress, RejectCall, RejectError, + TransferAssetParams, TransferFallback, TransferFallbackError, }; use cf_primitives::{chains::assets, EgressId, ForeignChain}; use codec::{Decode, Encode}; @@ -36,6 +36,7 @@ pub enum MockEthereumApiCall { AllBatch(MockEthAllBatch), ExecutexSwapAndCall(MockEthExecutexSwapAndCall), TransferFallback(MockEthTransferFallback), + RejectCall, } impl ApiCall for MockEthereumApiCall { @@ -206,6 +207,7 @@ pub enum MockBitcoinApiCall { AllBatch(MockBtcAllBatch), ExecutexSwapAndCall(MockBtcExecutexSwapAndCall), TransferFallback(MockBtcTransferFallback), + RejectCall, } impl ApiCall for MockBitcoinApiCall { @@ -335,3 +337,23 @@ impl AllBatch for MockBitcoinApiCall { } } } + +impl RejectCall for MockBitcoinApiCall { + fn new_unsigned( + _deposit_details: ::DepositDetails, + _refund_address: ::ChainAccount, + _refund_amount: ::ChainAmount, + ) -> Result { + Ok(Self::RejectCall) + } +} + +impl RejectCall for MockEthereumApiCall { + fn new_unsigned( + _deposit_details: ::DepositDetails, + _refund_address: ::ChainAccount, + _refund_amount: ::ChainAmount, + ) -> Result { + Ok(Self::RejectCall) + } +}