From 9d83bdeeeaf4b71f7c7a67ab12fec020b103bf4e Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Tue, 8 Oct 2024 16:55:13 +0200 Subject: [PATCH 01/23] wip --- foreign-chains/solana/sol-prim/src/consts.rs | 5 +++ state-chain/chains/src/lib.rs | 7 ++++ state-chain/chains/src/sol/api.rs | 14 +++++-- state-chain/pallets/cf-environment/src/lib.rs | 38 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/foreign-chains/solana/sol-prim/src/consts.rs b/foreign-chains/solana/sol-prim/src/consts.rs index 17239b94a9..88a69aef8b 100644 --- a/foreign-chains/solana/sol-prim/src/consts.rs +++ b/foreign-chains/solana/sol-prim/src/consts.rs @@ -42,3 +42,8 @@ pub const LAMPORTS_PER_SIGNATURE: u64 = 5000u64; pub const NONCE_ACCOUNT_LENGTH: u64 = 80u64; pub const SOL_USDC_DECIMAL: u8 = 6u8; + +// todo: confirm this +pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: u8 = 10u8; + +pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; // 1 day diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 7fc4c80909..2dd11ed457 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -4,6 +4,7 @@ #![feature(split_array)] use core::{fmt::Display, iter::Step}; +use sol::api::EventAccountAndSender; use sp_std::marker::PhantomData; use crate::{ @@ -485,6 +486,12 @@ pub trait RegisterRedemption: ApiCall<::ChainCrypto> { ) -> Self; } +pub trait CloseSolanaContractSwapAccounts: ApiCall<::ChainCrypto> { + fn new_unsigned( + accounts: Vec, + ) -> Result; +} + #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] pub enum AllBatchError { /// Empty transaction - the call is not required. diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 2d882d3ecd..8132a48d34 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -17,9 +17,9 @@ use crate::{ SolAsset, SolHash, SolTransaction, SolanaCrypto, }, AllBatch, AllBatchError, ApiCall, CcmChannelMetadata, Chain, ChainCrypto, ChainEnvironment, - ConsolidateCall, ConsolidationError, ExecutexSwapAndCall, ExecutexSwapAndCallError, - FetchAssetParams, ForeignChainAddress, SetAggKeyWithAggKey, SetGovKeyWithAggKey, Solana, - TransferAssetParams, TransferFallback, TransferFallbackError, + CloseSolanaContractSwapAccounts, ConsolidateCall, ConsolidationError, ExecutexSwapAndCall, + ExecutexSwapAndCallError, FetchAssetParams, ForeignChainAddress, SetAggKeyWithAggKey, + SetGovKeyWithAggKey, Solana, TransferAssetParams, TransferFallback, TransferFallbackError, }; use cf_primitives::{EgressId, ForeignChain}; @@ -510,6 +510,14 @@ impl TransferFallback for SolanaApi { } } +impl CloseSolanaContractSwapAccounts for SolanaApi { + fn new_unsigned( + accounts: Vec, + ) -> Result { + Self::batch_close_event_accounts(accounts) + } +} + impl SetGovKeyWithAggKey for SolanaApi { fn new_unsigned( _maybe_old_key: Option<::GovKey>, diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 4b62f53cb7..932331a38c 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -228,6 +228,19 @@ pub mod pallet { #[pallet::getter(fn solana_api_environment)] pub type SolanaApiEnvironment = StorageValue<_, SolApiEnvironment, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn solana_open_contract_swap_accounts)] + pub type SolanaOpenContractSwapAccounts = StorageValue<_, Vec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn solana_closed_contract_swap_accounts)] + pub type SolanaClosedContractSwapAccounts = StorageMap<_, Blake2_128Concat, SolAddress, ()>; + + #[pallet::storage] + #[pallet::getter(fn solana_last_closed_contract_swap_accounts_at)] + pub type SolanaLastClosedContractSwapAccountsAt = + StorageValue<_, BlockNumberFor, OptionQuery>; + // OTHER ENVIRONMENT ITEMS #[pallet::storage] #[pallet::getter(fn safe_mode)] @@ -245,6 +258,15 @@ pub mod pallet { /// Contains the network environment for this runtime. pub type ChainflipNetworkEnvironment = StorageValue<_, NetworkEnvironment, ValueQuery>; + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(block_number: BlockNumberFor, remaining_weight: Weight) -> Weight { + if (SolanaOpenContractSwapAccounts::::decode_len() >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || SolanaLastClosedContractSwapAccountsAt::::get().is_some_and(|n| block_number.checked_sub(n).expect("current block number should always be greater than the block number at which last account closure apicall was created.") >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) ) && Self::get_number_of_available_sol_nonce_accounts() > 4 { + + } + } + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -745,6 +767,22 @@ impl Pallet { log::error!("Nonce account {nonce_account} not found in unavailable nonce accounts"); } } + + pub fn report_sol_contract_swap_accounts( + new_accounts: Vec, + confirm_closed_accounts: Vec, + ) { + SolanaOpenContractSwapAccounts::::mutate(|accts| accts.extend(new_accounts)); + confirm_closed_accounts + .into_iter() + .for_each(SolanaClosedContractSwapAccounts::::remove); + } + + fn get_all_sol_contract_swap_accounts() -> Vec { + let mut all_accounts = SolanaOpenContractSwapAccounts::::get(); + all_accounts.extend(SolanaClosedContractSwapAccounts::::iter_keys().collect::>()); + all_accounts + } } impl CompatibleCfeVersions for Pallet { From b0c5f6ccd9ceb81c84b92251f39e1853f5a49b64 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 11 Oct 2024 14:06:18 +0200 Subject: [PATCH 02/23] feat: solana open/close contract swap accounts --- foreign-chains/solana/sol-prim/src/consts.rs | 2 +- state-chain/chains/src/lib.rs | 4 +- state-chain/chains/src/sol.rs | 8 +- state-chain/chains/src/sol/api.rs | 21 ++-- state-chain/chains/src/sol/sol_tx_core.rs | 95 ++++++++++--------- .../chains/src/sol/transaction_builder.rs | 38 ++++---- state-chain/pallets/cf-environment/src/lib.rs | 71 +++++++++++--- .../pallets/cf-environment/src/mock.rs | 64 ++++++++++++- state-chain/runtime/src/lib.rs | 4 +- 9 files changed, 214 insertions(+), 93 deletions(-) diff --git a/foreign-chains/solana/sol-prim/src/consts.rs b/foreign-chains/solana/sol-prim/src/consts.rs index 88a69aef8b..1e088ec784 100644 --- a/foreign-chains/solana/sol-prim/src/consts.rs +++ b/foreign-chains/solana/sol-prim/src/consts.rs @@ -44,6 +44,6 @@ pub const NONCE_ACCOUNT_LENGTH: u64 = 80u64; pub const SOL_USDC_DECIMAL: u8 = 6u8; // todo: confirm this -pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: u8 = 10u8; +pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: usize = 10; pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; // 1 day diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 2dd11ed457..9e0fa9f25e 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -4,7 +4,7 @@ #![feature(split_array)] use core::{fmt::Display, iter::Step}; -use sol::api::EventAccountAndSender; +use sol::api::ContractSwapAccountAndSender; use sp_std::marker::PhantomData; use crate::{ @@ -488,7 +488,7 @@ pub trait RegisterRedemption: ApiCall<::ChainCrypto> { pub trait CloseSolanaContractSwapAccounts: ApiCall<::ChainCrypto> { fn new_unsigned( - accounts: Vec, + accounts: Vec, ) -> Result; } diff --git a/state-chain/chains/src/sol.rs b/state-chain/chains/src/sol.rs index 37503876ac..10bb3a38c0 100644 --- a/state-chain/chains/src/sol.rs +++ b/state-chain/chains/src/sol.rs @@ -22,7 +22,11 @@ pub mod transaction_builder; pub use crate::assets::sol::Asset as SolAsset; use crate::benchmarking_value::BenchmarkValue; pub use sol_prim::{ - consts::{LAMPORTS_PER_SIGNATURE, MAX_TRANSACTION_LENGTH, MICROLAMPORTS_PER_LAMPORT}, + consts::{ + LAMPORTS_PER_SIGNATURE, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + MAX_TRANSACTION_LENGTH, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, + MICROLAMPORTS_PER_LAMPORT, + }, pda::{Pda as DerivedAddressBuilder, PdaError as AddressDerivationError}, Address as SolAddress, Amount as SolAmount, ComputeLimit as SolComputeLimit, Digest as SolHash, Signature as SolSignature, SlotNumber as SolBlockNumber, @@ -146,7 +150,7 @@ pub mod compute_units_costs { pub const COMPUTE_UNITS_PER_ROTATION: SolComputeLimit = 8_000u32; pub const COMPUTE_UNITS_PER_SET_GOV_KEY: SolComputeLimit = 15_000u32; pub const COMPUTE_UNITS_PER_BUMP_DERIVATION: SolComputeLimit = 2_000u32; - pub const COMPUTE_UNITS_PER_CLOSE_EVENT_ACCOUNTS: SolComputeLimit = 10_000u32; + pub const COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS: SolComputeLimit = 10_000u32; pub const COMPUTE_UNITS_PER_CLOSE_ACCOUNT: SolComputeLimit = 10_000u32; pub const MIN_COMPUTE_PRICE: SolAmount = 10u64; diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 8132a48d34..978219daa9 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -36,7 +36,12 @@ pub struct ApiEnvironment; pub struct CurrentAggKey; pub type DurableNonceAndAccount = (SolAddress, SolHash); -pub type EventAccountAndSender = (SolAddress, SolAddress); + +#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Copy, Eq)] +pub struct ContractSwapAccountAndSender { + pub contract_swap_account: SolAddress, + pub swap_sender: SolAddress, +} /// Super trait combining all Environment lookups required for the Solana chain. /// Also contains some calls for easy data retrieval. @@ -373,8 +378,8 @@ impl SolanaApi { }) } - pub fn batch_close_event_accounts( - event_accounts: Vec, + pub fn batch_close_contract_swap_accounts( + contract_swap_accounts: Vec, ) -> Result { // Lookup environment variables, such as aggkey and durable nonce. let agg_key = Environment::current_agg_key()?; @@ -383,8 +388,8 @@ impl SolanaApi { let durable_nonce = Environment::nonce_account()?; // Build the transaction - let transaction = SolanaTransactionBuilder::close_event_accounts( - event_accounts, + let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( + contract_swap_accounts, sol_api_environment.vault_program_data_account, sol_api_environment.swap_endpoint_program, sol_api_environment.swap_endpoint_program_data_account, @@ -510,11 +515,11 @@ impl TransferFallback for SolanaApi { } } -impl CloseSolanaContractSwapAccounts for SolanaApi { +impl CloseSolanaContractSwapAccounts for SolanaApi { fn new_unsigned( - accounts: Vec, + accounts: Vec, ) -> Result { - Self::batch_close_event_accounts(accounts) + Self::batch_close_contract_swap_accounts(accounts) } } diff --git a/state-chain/chains/src/sol/sol_tx_core.rs b/state-chain/chains/src/sol/sol_tx_core.rs index 01682b20a4..8be65dbe8b 100644 --- a/state-chain/chains/src/sol/sol_tx_core.rs +++ b/state-chain/chains/src/sol/sol_tx_core.rs @@ -871,8 +871,9 @@ impl FromStr for Hash { pub mod sol_test_values { use crate::{ sol::{ - signing_key::SolSigningKey, sol_tx_core::signer::Signer, SolAddress, SolAmount, - SolAsset, SolCcmAccounts, SolCcmAddress, SolComputeLimit, SolHash, + api::ContractSwapAccountAndSender, signing_key::SolSigningKey, + sol_tx_core::signer::Signer, SolAddress, SolAmount, SolAsset, SolCcmAccounts, + SolCcmAddress, SolComputeLimit, SolHash, }, CcmChannelMetadata, CcmDepositMetadata, ForeignChain, ForeignChainAddress, }; @@ -910,51 +911,51 @@ pub mod sol_test_values { const_address("35uYgHdfZQT4kHkaaXQ6ZdCkK5LFrsk43btTLbGCRCNT"); pub const SWAP_ENDPOINT_PROGRAM_DATA_ACCOUNT: SolAddress = const_address("2tmtGLQcBd11BMiE9B1tAkQXwmPNgR79Meki2Eme4Ec9"); - pub const EVENT_AND_SENDER_ACCOUNTS: [(SolAddress, SolAddress); 11] = [ - ( - const_address("2cHcSNtikMpjxJfwwoYL3udpy7hedRExyhakk2eZ6cYA"), - const_address("7tVhSXxGfZyHQem8MdZVB6SoRsrvV4H8h1rX6hwBuvEA"), - ), - ( - const_address("6uuU1NFyThN3KJpU9mYXkGSmd8Qgncmd9aYAWYN71VkC"), - const_address("P3GYr1Z67jdBVimzFjMXQpeuew5TY5txoZ9CvqASpaP"), - ), - ( - const_address("DmAom3kp2ZKk9cnbWEsnbkLHkp3sx9ef1EX6GWj1JRUB"), - const_address("CS7yX5TKX36ugF4bycmVQ5vqB2ZbNVC5tvtrtLP92GDW"), - ), - ( - const_address("CJSdHgxwHLEbTsxKsJk9UyJxUEgku2UC9GXRTzR2ieSh"), - const_address("2taCR53epDtdrFZBxzKcbmv3cb5Umc5x9k2YCjmTDAnH"), - ), - ( - const_address("7DGwjsQEFA7XzZS9z5YbMhYGzWJSh5T78hRrU47RDTd2"), - const_address("FDPzoZj951Hq92jhoFdyzAVyUjyXhL8VEnqBhyjsDhow"), - ), - ( - const_address("A6yYXUmZHa32mcFRnwwq8ZQKCEYUn9ewF1vWn2wsXN5a"), - const_address("9bNNNU9B52VPVGm6zRccwPEexDHD1ntndD2aNu2un3ca"), - ), - ( - const_address("2F3365PULNzt7moa9GgHARy7Lumj5ptDQF7wDt6xeuHK"), - const_address("4m5t38fJsvULKaPyWZKWjzfbvnzBGL86BTRNk5vLLUrh"), - ), - ( - const_address("8sCBWv9tzdf2iC4GNj61UBN6TZpzsLP5Ppv9x1ENX4HT"), - const_address("A3P5kfRU1vgZn7GjNMomS8ye6GHsoHC4JoVNUotMbDPE"), - ), - ( - const_address("3b1FkNvnvKJ4TzKeft7wA47VfYpjkoHPE4ER13ZTNecX"), - const_address("ERwuPnX66dCZqj85kH9QQJmwcVrzcczBnu8onJY2R7tG"), - ), - ( - const_address("Bnrp9X562krXVfaY8FnwJa3Mxp1gbDCrvGNW1qc99rKe"), - const_address("2aoZg41FFnTBnuHpkfHdFsCuPz8DhN4dsUW5386XwE8g"), - ), - ( - const_address("EuLceVgXMaJNPT7C88pnL7DRWcf1poy9BCeWY1GL8Agd"), - const_address("G1iXMtwUU76JGau9cJm6N8wBTmcsvyXuJcC7PtfU1TXZ"), - ), + pub const EVENT_AND_SENDER_ACCOUNTS: [ContractSwapAccountAndSender; 11] = [ + ContractSwapAccountAndSender { + contract_swap_account: const_address("2cHcSNtikMpjxJfwwoYL3udpy7hedRExyhakk2eZ6cYA"), + swap_sender: const_address("7tVhSXxGfZyHQem8MdZVB6SoRsrvV4H8h1rX6hwBuvEA"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("6uuU1NFyThN3KJpU9mYXkGSmd8Qgncmd9aYAWYN71VkC"), + swap_sender: const_address("P3GYr1Z67jdBVimzFjMXQpeuew5TY5txoZ9CvqASpaP"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("DmAom3kp2ZKk9cnbWEsnbkLHkp3sx9ef1EX6GWj1JRUB"), + swap_sender: const_address("CS7yX5TKX36ugF4bycmVQ5vqB2ZbNVC5tvtrtLP92GDW"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("CJSdHgxwHLEbTsxKsJk9UyJxUEgku2UC9GXRTzR2ieSh"), + swap_sender: const_address("2taCR53epDtdrFZBxzKcbmv3cb5Umc5x9k2YCjmTDAnH"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("7DGwjsQEFA7XzZS9z5YbMhYGzWJSh5T78hRrU47RDTd2"), + swap_sender: const_address("FDPzoZj951Hq92jhoFdyzAVyUjyXhL8VEnqBhyjsDhow"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("A6yYXUmZHa32mcFRnwwq8ZQKCEYUn9ewF1vWn2wsXN5a"), + swap_sender: const_address("9bNNNU9B52VPVGm6zRccwPEexDHD1ntndD2aNu2un3ca"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("2F3365PULNzt7moa9GgHARy7Lumj5ptDQF7wDt6xeuHK"), + swap_sender: const_address("4m5t38fJsvULKaPyWZKWjzfbvnzBGL86BTRNk5vLLUrh"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("8sCBWv9tzdf2iC4GNj61UBN6TZpzsLP5Ppv9x1ENX4HT"), + swap_sender: const_address("A3P5kfRU1vgZn7GjNMomS8ye6GHsoHC4JoVNUotMbDPE"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("3b1FkNvnvKJ4TzKeft7wA47VfYpjkoHPE4ER13ZTNecX"), + swap_sender: const_address("ERwuPnX66dCZqj85kH9QQJmwcVrzcczBnu8onJY2R7tG"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("Bnrp9X562krXVfaY8FnwJa3Mxp1gbDCrvGNW1qc99rKe"), + swap_sender: const_address("2aoZg41FFnTBnuHpkfHdFsCuPz8DhN4dsUW5386XwE8g"), + }, + ContractSwapAccountAndSender { + contract_swap_account: const_address("EuLceVgXMaJNPT7C88pnL7DRWcf1poy9BCeWY1GL8Agd"), + swap_sender: const_address("G1iXMtwUU76JGau9cJm6N8wBTmcsvyXuJcC7PtfU1TXZ"), + }, ]; pub const RAW_KEYPAIR: [u8; 32] = [ 6, 151, 150, 20, 145, 210, 176, 113, 98, 200, 192, 80, 73, 63, 133, 232, 208, 124, 81, 213, diff --git a/state-chain/chains/src/sol/transaction_builder.rs b/state-chain/chains/src/sol/transaction_builder.rs index d1b94395ee..bb2cc642da 100644 --- a/state-chain/chains/src/sol/transaction_builder.rs +++ b/state-chain/chains/src/sol/transaction_builder.rs @@ -11,11 +11,13 @@ use sol_prim::consts::{ use crate::{ sol::{ - api::{DurableNonceAndAccount, EventAccountAndSender, SolanaTransactionBuildingError}, + api::{ + ContractSwapAccountAndSender, DurableNonceAndAccount, SolanaTransactionBuildingError, + }, compute_units_costs::{ compute_limit_with_buffer, BASE_COMPUTE_UNITS_PER_TX, COMPUTE_UNITS_PER_BUMP_DERIVATION, COMPUTE_UNITS_PER_CLOSE_ACCOUNT, - COMPUTE_UNITS_PER_CLOSE_EVENT_ACCOUNTS, COMPUTE_UNITS_PER_FETCH_NATIVE, + COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS, COMPUTE_UNITS_PER_FETCH_NATIVE, COMPUTE_UNITS_PER_FETCH_TOKEN, COMPUTE_UNITS_PER_ROTATION, COMPUTE_UNITS_PER_SET_GOV_KEY, COMPUTE_UNITS_PER_TRANSFER_NATIVE, COMPUTE_UNITS_PER_TRANSFER_TOKEN, @@ -445,8 +447,8 @@ impl SolanaTransactionBuilder { /// Creates an instruction to close a number of open event swap accounts created via program /// swap. - pub fn close_event_accounts( - event_accounts: Vec, + pub fn close_contract_swap_accounts( + contract_swap_accounts: Vec, vault_program_data_account: SolAddress, swap_endpoint_program: SolAddress, swap_endpoint_data_account: SolAddress, @@ -454,17 +456,19 @@ impl SolanaTransactionBuilder { durable_nonce: DurableNonceAndAccount, compute_price: SolAmount, ) -> Result { - let number_of_accounts = event_accounts.len(); - let event_and_sender_vec: Vec = event_accounts + let number_of_accounts = contract_swap_accounts.len(); + let swap_and_sender_vec: Vec = contract_swap_accounts .into_iter() - .flat_map(|(event_account, payee)| vec![event_account, payee]) - // Both event account and payee should be writable and non-signers + .flat_map(|ContractSwapAccountAndSender { contract_swap_account, swap_sender }| { + vec![contract_swap_account, swap_sender] + }) + // Both swap account and payee should be writable and non-signers .map(|address| AccountMeta::new(address.into(), false)) .collect(); let instructions = vec![SwapEndpointProgram::with_id(swap_endpoint_program) .close_event_accounts(vault_program_data_account, agg_key, swap_endpoint_data_account) - .with_remaining_accounts(event_and_sender_vec)]; + .with_remaining_accounts(swap_and_sender_vec)]; Self::build( instructions, @@ -472,7 +476,7 @@ impl SolanaTransactionBuilder { agg_key.into(), compute_price, compute_limit_with_buffer( - COMPUTE_UNITS_PER_CLOSE_EVENT_ACCOUNTS + + COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS + COMPUTE_UNITS_PER_CLOSE_ACCOUNT * number_of_accounts as u32, ), ) @@ -721,11 +725,11 @@ mod test { } #[test] - fn can_close_event_accounts() { + fn can_close_contract_swap_accounts() { let env = api_env(); - let event_accounts = vec![EVENT_AND_SENDER_ACCOUNTS[0]]; - let transaction = SolanaTransactionBuilder::close_event_accounts( - event_accounts, + let contract_swap_accounts = vec![EVENT_AND_SENDER_ACCOUNTS[0]]; + let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( + contract_swap_accounts, env.vault_program_data_account, env.swap_endpoint_program, env.swap_endpoint_program_data_account, @@ -735,18 +739,18 @@ mod test { ) .unwrap(); - // Serialized tx built in `close_event_accounts` test + // Serialized tx built in `close_contract_swap_accounts` test let expected_serialized_tx = hex_literal::hex!("01026e2d4bdca9e638b59507a70ea62ad88f098ffb25df028a19288702698fdf6d1cf77618b2123c0205a8e8d272ba8ea645b7e75c606ca3aa4356b65fa52ca20b0100050af79d5e026f12edc6443a534b2cdd5072233989b415d7596573e743f3e5b386fb17e5cc1f4d51a40626e11c783b75a45a4922615ecd7f5320b9d4d46481a196a317eb2b10d3377bda2bc7bea65bec6b8372f4fc3463ec2cd6f9fde4b2c633d1921c1f0efc91eeb48bb80c90cf97775cd5d843a96f16500266cee2c20d053152d2665730decf59d4cd6db8437dab77302287431eb7562b5997601851a0eab6946f00000000000000000000000000000000000000000000000000000000000000000306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a4000000006a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea94000000e14940a2247d0a8a33650d7dfe12d269ecabce61c1219b5a6dcdb6961026e091ef91c791d2aa8492c90f12540abd10056ce5dd8d9ab08461476c1dcc1622938c27e9074fac5e8d36cf04f94a0606fdd8ddbb420e99a489c7915ce5699e4890004050302070004040000000600090340420f000000000006000502307500000905080003010408a5663d01b94dbd79").to_vec(); test_constructed_transaction(transaction, expected_serialized_tx); } #[test] - fn can_close_max_event_accounts() { + fn can_close_max_contract_swap_accounts() { let env = api_env(); // We can close 11 accounts without reaching the transaction length limit. - let transaction = SolanaTransactionBuilder::close_event_accounts( + let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( EVENT_AND_SENDER_ACCOUNTS.to_vec(), env.vault_program_data_account, env.swap_endpoint_program, diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 932331a38c..2deca969d4 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -12,8 +12,13 @@ use cf_chains::{ }, dot::{Polkadot, PolkadotAccountId, PolkadotHash, PolkadotIndex}, eth::Address as EvmAddress, - sol::{api::DurableNonceAndAccount, SolAddress, SolApiEnvironment, SolHash, Solana}, - Chain, + sol::{ + api::{ContractSwapAccountAndSender, DurableNonceAndAccount}, + SolAddress, SolApiEnvironment, SolHash, Solana, + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, + }, + Chain, CloseSolanaContractSwapAccounts, }; use cf_primitives::{ chains::assets::{arb::Asset as ArbAsset, eth::Asset as EthAsset}, @@ -23,7 +28,7 @@ use cf_traits::{ CompatibleCfeVersions, GetBitcoinFeeInfo, KeyProvider, NetworkEnvironmentProvider, SafeMode, SolanaNonceWatch, }; -use frame_support::{pallet_prelude::*, traits::StorageVersion}; +use frame_support::{pallet_prelude::*, sp_runtime::traits::CheckedSub, traits::StorageVersion}; use frame_system::pallet_prelude::*; pub use pallet::*; use sp_std::{vec, vec::Vec}; @@ -65,8 +70,8 @@ pub enum SafeModeUpdate { pub mod pallet { use super::*; use cf_chains::{btc::Utxo, sol::api::DurableNonceAndAccount, Arbitrum}; - use cf_primitives::TxId; - use cf_traits::VaultKeyWitnessedHandler; + use cf_primitives::{BroadcastId, TxId}; + use cf_traits::{Broadcaster, VaultKeyWitnessedHandler}; use frame_support::DefaultNoBound; #[pallet::config] @@ -95,6 +100,10 @@ pub mod pallet { type SolanaNonceWatch: SolanaNonceWatch; + type CloseSolanaContractSwapAccounts: cf_chains::CloseSolanaContractSwapAccounts; + + type SolanaBroadcaster: Broadcaster; + /// Used to access the current Chainflip runtime's release version (distinct from the /// substrate RuntimeVersion) #[pallet::constant] @@ -230,11 +239,13 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn solana_open_contract_swap_accounts)] - pub type SolanaOpenContractSwapAccounts = StorageValue<_, Vec, ValueQuery>; + pub type SolanaOpenContractSwapAccounts = + StorageValue<_, Vec, ValueQuery>; #[pallet::storage] #[pallet::getter(fn solana_closed_contract_swap_accounts)] - pub type SolanaClosedContractSwapAccounts = StorageMap<_, Blake2_128Concat, SolAddress, ()>; + pub type SolanaClosedContractSwapAccounts = + StorageMap<_, Blake2_128Concat, ContractSwapAccountAndSender, ()>; #[pallet::storage] #[pallet::getter(fn solana_last_closed_contract_swap_accounts_at)] @@ -260,9 +271,40 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - fn on_idle(block_number: BlockNumberFor, remaining_weight: Weight) -> Weight { - if (SolanaOpenContractSwapAccounts::::decode_len() >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || SolanaLastClosedContractSwapAccountsAt::::get().is_some_and(|n| block_number.checked_sub(n).expect("current block number should always be greater than the block number at which last account closure apicall was created.") >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) ) && Self::get_number_of_available_sol_nonce_accounts() > 4 { - + fn on_idle(block_number: BlockNumberFor, _remaining_weight: Weight) -> Weight { + if Self::get_number_of_available_sol_nonce_accounts() > 4 && + (SolanaOpenContractSwapAccounts::::decode_len().is_some_and( + |open_accounts| { + open_accounts >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES + }, + ) || SolanaLastClosedContractSwapAccountsAt::::get().is_some_and(|n| { + block_number.checked_sub(&n).expect("current block number should always be greater than the block number at which last account closure apicall was created.") >= + MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS.into() + })) { + SolanaOpenContractSwapAccounts::::try_mutate(|accounts| { + let accounts_to_close: Vec<_> = if accounts.len() > + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES + { + accounts.drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES).collect() + } else { + sp_std::mem::take(accounts) + }; + match T::CloseSolanaContractSwapAccounts::new_unsigned(accounts_to_close) { + Ok(apicall) => { + let (broadcast_id, _) = + T::SolanaBroadcaster::threshold_sign_and_broadcast(apicall); + Self::deposit_event(Event::::SolanaCloseContractSwapAccounts { + broadcast_id, + }); + SolanaLastClosedContractSwapAccountsAt::::put(block_number); + Ok(Weight::zero()) + }, + Err(_) => Err(Weight::zero()), + } + }) + .unwrap_or_else(|weight| weight) + } else { + Weight::zero() } } } @@ -294,6 +336,8 @@ pub mod pallet { StaleUtxosDiscarded { utxos: Vec }, /// Solana durable nonce is updated to a new nonce for the corresponding nonce account. DurableNonceSetForAccount { nonce_account: SolAddress, durable_nonce: SolHash }, + /// apicall to close Solana Contract Swap Accounts has been initiated. + SolanaCloseContractSwapAccounts { broadcast_id: BroadcastId }, } #[pallet::call] @@ -769,8 +813,8 @@ impl Pallet { } pub fn report_sol_contract_swap_accounts( - new_accounts: Vec, - confirm_closed_accounts: Vec, + new_accounts: Vec, + confirm_closed_accounts: Vec, ) { SolanaOpenContractSwapAccounts::::mutate(|accts| accts.extend(new_accounts)); confirm_closed_accounts @@ -778,7 +822,8 @@ impl Pallet { .for_each(SolanaClosedContractSwapAccounts::::remove); } - fn get_all_sol_contract_swap_accounts() -> Vec { + #[allow(dead_code)] + fn get_all_sol_contract_swap_accounts() -> Vec { let mut all_accounts = SolanaOpenContractSwapAccounts::::get(); all_accounts.extend(SolanaClosedContractSwapAccounts::::iter_keys().collect::>()); all_accounts diff --git a/state-chain/pallets/cf-environment/src/mock.rs b/state-chain/pallets/cf-environment/src/mock.rs index c49275c00e..d2a449a934 100644 --- a/state-chain/pallets/cf-environment/src/mock.rs +++ b/state-chain/pallets/cf-environment/src/mock.rs @@ -4,12 +4,19 @@ use crate::{self as pallet_cf_environment, Decode, Encode, TypeInfo}; use cf_chains::{ btc::{BitcoinCrypto, BitcoinFeeInfo}, dot::{api::CreatePolkadotVault, PolkadotCrypto}, - eth, ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, Polkadot, Solana, + eth, + sol::{ + api::{ContractSwapAccountAndSender, SolanaTransactionBuildingError}, + SolanaCrypto, + }, + ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, CloseSolanaContractSwapAccounts, Polkadot, + Solana, }; use cf_primitives::SemVer; use cf_traits::{ impl_mock_chainflip, impl_mock_runtime_safe_mode, impl_pallet_safe_mode, - mocks::key_provider::MockKeyProvider, GetBitcoinFeeInfo, VaultKeyWitnessedHandler, + mocks::{broadcaster::MockBroadcaster, key_provider::MockKeyProvider}, + GetBitcoinFeeInfo, VaultKeyWitnessedHandler, }; use frame_support::{derive_impl, parameter_types}; use sp_core::{H160, H256}; @@ -158,6 +165,57 @@ impl_mock_runtime_safe_mode!(mock: MockPalletSafeMode); pub type MockBitcoinKeyProvider = MockKeyProvider; +#[derive(Clone, Debug, Default, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub struct MockCloseSolanaContractSwapAccounts { + contract_swap_accounts_and_senders: Vec, +} + +impl CloseSolanaContractSwapAccounts for MockCloseSolanaContractSwapAccounts { + fn new_unsigned( + accounts: Vec, + ) -> Result { + Ok(Self { contract_swap_accounts_and_senders: accounts }) + } +} + +impl ApiCall for MockCloseSolanaContractSwapAccounts { + fn threshold_signature_payload( + &self, + ) -> <::ChainCrypto as ChainCrypto>::Payload { + unimplemented!() + } + + fn signed( + self, + _threshold_signature: &<::ChainCrypto as ChainCrypto>::ThresholdSignature, + _signer: <::ChainCrypto as ChainCrypto>::AggKey, + ) -> Self { + unimplemented!() + } + + fn chain_encoded(&self) -> Vec { + unimplemented!() + } + + fn is_signed(&self) -> bool { + unimplemented!() + } + + fn transaction_out_id( + &self, + ) -> <::ChainCrypto as ChainCrypto>::TransactionOutId { + unimplemented!() + } + + fn refresh_replay_protection(&mut self) { + unimplemented!() + } + + fn signer(&self) -> Option<::AggKey> { + unimplemented!() + } +} + impl pallet_cf_environment::Config for Test { type RuntimeEvent = RuntimeEvent; type PolkadotVaultKeyWitnessedHandler = MockPolkadotVaultKeyWitnessedHandler; @@ -165,6 +223,8 @@ impl pallet_cf_environment::Config for Test { type ArbitrumVaultKeyWitnessedHandler = MockArbitrumVaultKeyWitnessedHandler; type SolanaVaultKeyWitnessedHandler = MockSolanaVaultKeyWitnessedHandler; type SolanaNonceWatch = (); + type CloseSolanaContractSwapAccounts = MockCloseSolanaContractSwapAccounts; + type SolanaBroadcaster = MockBroadcaster<(MockCloseSolanaContractSwapAccounts, RuntimeCall)>; type BitcoinFeeInfo = MockBitcoinFeeInfo; type BitcoinKeyProvider = MockBitcoinKeyProvider; type RuntimeSafeMode = MockRuntimeSafeMode; diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 02a2d5e93d..229b64b4d0 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -47,7 +47,7 @@ use cf_chains::{ dot::{self, PolkadotAccountId, PolkadotCrypto}, eth::{self, api::EthereumApi, Address as EthereumAddress, Ethereum}, evm::EvmCrypto, - sol::SolanaCrypto, + sol::{api::SolanaApi, SolanaCrypto}, Arbitrum, Bitcoin, DefaultRetryPolicy, ForeignChain, Polkadot, Solana, TransactionBuilder, }; use cf_primitives::{BroadcastId, EpochIndex, NetworkEnvironment, STABLE_ASSET}; @@ -270,6 +270,8 @@ impl pallet_cf_environment::Config for Runtime { type ArbitrumVaultKeyWitnessedHandler = ArbitrumVault; type SolanaVaultKeyWitnessedHandler = SolanaVault; type SolanaNonceWatch = SolanaNonceTrackingTrigger; + type CloseSolanaContractSwapAccounts = SolanaApi; + type SolanaBroadcaster = SolanaBroadcaster; type BitcoinFeeInfo = chainflip::BitcoinFeeGetter; type BitcoinKeyProvider = BitcoinThresholdSigner; type RuntimeSafeMode = RuntimeSafeMode; From b0e82d0e63c4f0e7af7d598a6e49bf958baed65f Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 11 Oct 2024 15:22:05 +0200 Subject: [PATCH 03/23] feat: initiate contract swap --- state-chain/pallets/cf-environment/src/lib.rs | 39 ++++++++++++++++--- .../pallets/cf-environment/src/mock.rs | 16 +++++++- state-chain/runtime/src/chainflip.rs | 29 ++++++++++++-- state-chain/runtime/src/lib.rs | 3 +- state-chain/traits/src/lib.rs | 10 +++++ 5 files changed, 86 insertions(+), 11 deletions(-) diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 2deca969d4..a2a0fb3b7e 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -4,6 +4,8 @@ #![doc = include_str!("../../cf-doc-head.md")] use cf_chains::{ + address::EncodedAddress, + assets::any::Asset, btc::{ api::{SelectedUtxosAndChangeAmount, UtxoSelectionType}, deposit_address::DepositAddress, @@ -22,11 +24,11 @@ use cf_chains::{ }; use cf_primitives::{ chains::assets::{arb::Asset as ArbAsset, eth::Asset as EthAsset}, - NetworkEnvironment, SemVer, + AssetAmount, NetworkEnvironment, SemVer, TransactionHash, }; use cf_traits::{ CompatibleCfeVersions, GetBitcoinFeeInfo, KeyProvider, NetworkEnvironmentProvider, SafeMode, - SolanaNonceWatch, + SolanaContractSwap, SolanaNonceWatch, }; use frame_support::{pallet_prelude::*, sp_runtime::traits::CheckedSub, traits::StorageVersion}; use frame_system::pallet_prelude::*; @@ -104,6 +106,8 @@ pub mod pallet { type SolanaBroadcaster: Broadcaster; + type SolanaContractSwapper: SolanaContractSwap; + /// Used to access the current Chainflip runtime's release version (distinct from the /// substrate RuntimeVersion) #[pallet::constant] @@ -289,13 +293,14 @@ pub mod pallet { } else { sp_std::mem::take(accounts) }; - match T::CloseSolanaContractSwapAccounts::new_unsigned(accounts_to_close) { + match T::CloseSolanaContractSwapAccounts::new_unsigned(accounts_to_close.clone()) { Ok(apicall) => { let (broadcast_id, _) = T::SolanaBroadcaster::threshold_sign_and_broadcast(apicall); Self::deposit_event(Event::::SolanaCloseContractSwapAccounts { broadcast_id, }); + accounts_to_close.into_iter().for_each(|acct| SolanaClosedContractSwapAccounts::::insert(acct, ())); SolanaLastClosedContractSwapAccountsAt::::put(block_number); Ok(Weight::zero()) }, @@ -813,10 +818,26 @@ impl Pallet { } pub fn report_sol_contract_swap_accounts( - new_accounts: Vec, + new_contract_swaps: Vec<(ContractSwapAccountAndSender, SolanaContractSwapDetails)>, confirm_closed_accounts: Vec, ) { - SolanaOpenContractSwapAccounts::::mutate(|accts| accts.extend(new_accounts)); + SolanaOpenContractSwapAccounts::::mutate(|accts| { + accts.extend( + new_contract_swaps + .into_iter() + .map(|(contract_swap_account, swap_details)| { + T::SolanaContractSwapper::initiate_contract_swap( + swap_details.from, + swap_details.to, + swap_details.deposit_amount, + swap_details.destination_address, + swap_details.tx_hash, + ); + contract_swap_account + }) + .collect::>(), + ) + }); confirm_closed_accounts .into_iter() .for_each(SolanaClosedContractSwapAccounts::::remove); @@ -841,3 +862,11 @@ impl NetworkEnvironmentProvider for Pallet { Self::network_environment() } } + +pub struct SolanaContractSwapDetails { + from: Asset, + to: Asset, + deposit_amount: AssetAmount, + destination_address: EncodedAddress, + tx_hash: TransactionHash, +} diff --git a/state-chain/pallets/cf-environment/src/mock.rs b/state-chain/pallets/cf-environment/src/mock.rs index d2a449a934..96da2f1355 100644 --- a/state-chain/pallets/cf-environment/src/mock.rs +++ b/state-chain/pallets/cf-environment/src/mock.rs @@ -16,7 +16,7 @@ use cf_primitives::SemVer; use cf_traits::{ impl_mock_chainflip, impl_mock_runtime_safe_mode, impl_pallet_safe_mode, mocks::{broadcaster::MockBroadcaster, key_provider::MockKeyProvider}, - GetBitcoinFeeInfo, VaultKeyWitnessedHandler, + GetBitcoinFeeInfo, SolanaContractSwap, VaultKeyWitnessedHandler, }; use frame_support::{derive_impl, parameter_types}; use sp_core::{H160, H256}; @@ -216,6 +216,19 @@ impl ApiCall for MockCloseSolanaContractSwapAccounts { } } +pub struct MockSolanaContractSwapper; +impl SolanaContractSwap for MockSolanaContractSwapper { + fn initiate_contract_swap( + _from: cf_primitives::Asset, + _to: cf_primitives::Asset, + _deposit_amount: cf_primitives::AssetAmount, + _destination_address: cf_chains::address::EncodedAddress, + _tx_hash: cf_primitives::TransactionHash, + ) { + unimplemented!() + } +} + impl pallet_cf_environment::Config for Test { type RuntimeEvent = RuntimeEvent; type PolkadotVaultKeyWitnessedHandler = MockPolkadotVaultKeyWitnessedHandler; @@ -225,6 +238,7 @@ impl pallet_cf_environment::Config for Test { type SolanaNonceWatch = (); type CloseSolanaContractSwapAccounts = MockCloseSolanaContractSwapAccounts; type SolanaBroadcaster = MockBroadcaster<(MockCloseSolanaContractSwapAccounts, RuntimeCall)>; + type SolanaContractSwapper = MockSolanaContractSwapper; type BitcoinFeeInfo = MockBitcoinFeeInfo; type BitcoinKeyProvider = MockBitcoinKeyProvider; type RuntimeSafeMode = MockRuntimeSafeMode; diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index 6226784623..156edf6cde 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -18,8 +18,8 @@ use crate::{ BitcoinThresholdSigner, BlockNumber, Emissions, Environment, EthereumBroadcaster, EthereumChainTracking, EthereumIngressEgress, Flip, FlipBalance, Hash, PolkadotBroadcaster, PolkadotChainTracking, PolkadotIngressEgress, PolkadotThresholdSigner, Runtime, RuntimeCall, - SolanaBroadcaster, SolanaChainTrackingProvider, SolanaIngressEgress, SolanaThresholdSigner, - System, Validator, YEAR, + RuntimeOrigin, SolanaBroadcaster, SolanaChainTrackingProvider, SolanaIngressEgress, + SolanaThresholdSigner, System, Validator, YEAR, }; use backup_node_rewards::calculate_backup_rewards; use cf_chains::{ @@ -60,14 +60,15 @@ use cf_chains::{ Solana, TransactionBuilder, }; use cf_primitives::{ - chains::assets, AccountRole, Asset, BasisPoints, Beneficiaries, ChannelId, DcaParameters, + chains::assets, AccountRole, Asset, AssetAmount, BasisPoints, Beneficiaries, ChannelId, + DcaParameters, TransactionHash, }; use cf_traits::{ AccountInfo, AccountRoleRegistry, BackupRewardsNotifier, BlockEmissions, BroadcastAnyChainGovKey, Broadcaster, Chainflip, CommKeyBroadcaster, DepositApi, EgressApi, EpochInfo, FetchesTransfersLimitProvider, Heartbeat, IngressEgressFeeApi, Issuance, KeyProvider, OnBroadcastReady, OnDeposit, QualifyNode, RewardsDistribution, RuntimeUpgrade, - ScheduledEgressDetails, + ScheduledEgressDetails, SolanaContractSwap, }; use codec::{Decode, Encode}; @@ -969,3 +970,23 @@ impl FetchesTransfersLimitProvider for EvmLimit { Some(20) } } + +pub struct SolanaContractSwapper; +impl SolanaContractSwap for SolanaContractSwapper { + fn initiate_contract_swap( + from: Asset, + to: Asset, + deposit_amount: AssetAmount, + destination_address: EncodedAddress, + tx_hash: TransactionHash, + ) { + let _ = SolanaIngressEgress::contract_swap_request( + RuntimeOrigin::root(), + from, + to, + deposit_amount, + destination_address, + tx_hash, + ); + } +} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 229b64b4d0..3b6f0c5eeb 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -17,7 +17,7 @@ use crate::{ SolanaChainTrackingProvider, SolanaEgressWitnessingTrigger, SolanaIngress, SolanaNonceTrackingTrigger, }, - Offence, + Offence, SolanaContractSwapper, }, migrations::serialize_solana_broadcast::{NoopUpgrade, SerializeSolanaBroadcastMigration}, monitoring_apis::{ @@ -272,6 +272,7 @@ impl pallet_cf_environment::Config for Runtime { type SolanaNonceWatch = SolanaNonceTrackingTrigger; type CloseSolanaContractSwapAccounts = SolanaApi; type SolanaBroadcaster = SolanaBroadcaster; + type SolanaContractSwapper = SolanaContractSwapper; type BitcoinFeeInfo = chainflip::BitcoinFeeGetter; type BitcoinKeyProvider = BitcoinThresholdSigner; type RuntimeSafeMode = RuntimeSafeMode; diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index fa82ca329e..63bcb900dd 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -1100,3 +1100,13 @@ impl ElectionEgressWitnesser for DummyEgressSuccessWitnesser pub trait RotationBroadcastsPending { fn rotation_broadcasts_pending() -> bool; } + +pub trait SolanaContractSwap { + fn initiate_contract_swap( + from: Asset, + to: Asset, + deposit_amount: AssetAmount, + destination_address: cf_chains::address::EncodedAddress, + tx_hash: cf_primitives::TransactionHash, + ); +} From e323c72b90b479bc6d0d194fd8b5345b82ee2841 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Mon, 14 Oct 2024 14:43:34 +0200 Subject: [PATCH 04/23] feat: root origin for contract_swap_request --- state-chain/pallets/cf-ingress-egress/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 5eaadaee53..69bd4b3923 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -1104,7 +1104,9 @@ pub mod pallet { destination_address: EncodedAddress, tx_hash: TransactionHash, ) -> DispatchResult { - T::EnsureWitnessed::ensure_origin(origin)?; + // This extrinsic can either be witnessed called or called from within the runtime (for + // solana contract swaps). + ensure_root(origin.clone()).or(T::EnsureWitnessed::ensure_origin(origin).map(|_| ()))?; let destination_address_internal = match T::AddressConverter::decode_and_validate_address_for_asset( From c505b3c90b2356d5c0b6581624a2994bc1408075 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Mon, 14 Oct 2024 14:58:51 +0200 Subject: [PATCH 05/23] chore: address comment --- foreign-chains/solana/sol-prim/src/consts.rs | 2 +- state-chain/chains/src/sol.rs | 1 + state-chain/pallets/cf-environment/src/lib.rs | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/foreign-chains/solana/sol-prim/src/consts.rs b/foreign-chains/solana/sol-prim/src/consts.rs index 1e088ec784..8918e53f02 100644 --- a/foreign-chains/solana/sol-prim/src/consts.rs +++ b/foreign-chains/solana/sol-prim/src/consts.rs @@ -45,5 +45,5 @@ pub const SOL_USDC_DECIMAL: u8 = 6u8; // todo: confirm this pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: usize = 10; - pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; // 1 day +pub const NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES: usize = 4; // revisit this diff --git a/state-chain/chains/src/sol.rs b/state-chain/chains/src/sol.rs index 10bb3a38c0..1e42304eda 100644 --- a/state-chain/chains/src/sol.rs +++ b/state-chain/chains/src/sol.rs @@ -26,6 +26,7 @@ pub use sol_prim::{ LAMPORTS_PER_SIGNATURE, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, MAX_TRANSACTION_LENGTH, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, MICROLAMPORTS_PER_LAMPORT, + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }, pda::{Pda as DerivedAddressBuilder, PdaError as AddressDerivationError}, Address as SolAddress, Amount as SolAmount, ComputeLimit as SolComputeLimit, Digest as SolHash, diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index a2a0fb3b7e..809e43a2eb 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -19,6 +19,7 @@ use cf_chains::{ SolAddress, SolApiEnvironment, SolHash, Solana, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }, Chain, CloseSolanaContractSwapAccounts, }; @@ -276,7 +277,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_idle(block_number: BlockNumberFor, _remaining_weight: Weight) -> Weight { - if Self::get_number_of_available_sol_nonce_accounts() > 4 && + if Self::get_number_of_available_sol_nonce_accounts() > NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && (SolanaOpenContractSwapAccounts::::decode_len().is_some_and( |open_accounts| { open_accounts >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES From 0e342088396d49ed8113aa1166c60b1f2de7d079 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Thu, 24 Oct 2024 17:38:46 +0200 Subject: [PATCH 06/23] feat: solana vault swaps elections --- engine/src/elections/voter_api.rs | 2 +- engine/src/witness/sol.rs | 25 ++- state-chain/chains/src/address.rs | 4 +- state-chain/chains/src/lib.rs | 2 +- state-chain/chains/src/sol/api.rs | 20 +- .../cf-elections/src/electoral_systems.rs | 1 + .../src/electoral_systems/composite.rs | 3 +- .../solana_swap_accounts_tracking.rs | 195 ++++++++++++++++++ state-chain/pallets/cf-elections/src/lib.rs | 5 +- .../src/vote_storage/composite.rs | 1 + .../src/vote_storage/individual/composite.rs | 1 + state-chain/pallets/cf-environment/src/lib.rs | 70 +++---- .../pallets/cf-environment/src/mock.rs | 13 +- .../runtime/src/chainflip/solana_elections.rs | 118 ++++++++++- state-chain/runtime/src/lib.rs | 2 +- 15 files changed, 395 insertions(+), 67 deletions(-) create mode 100644 state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs diff --git a/engine/src/elections/voter_api.rs b/engine/src/elections/voter_api.rs index f17edbb328..b2a05086ca 100644 --- a/engine/src/elections/voter_api.rs +++ b/engine/src/elections/voter_api.rs @@ -66,4 +66,4 @@ macro_rules! generate_voter_api_tuple_impls { } } -generate_voter_api_tuple_impls!(tuple_5_impls: ((A, A0), (B, B0), (C, C0), (D, D0), (EE, E0))); +generate_voter_api_tuple_impls!(tuple_6_impls: ((A, A0), (B, B0), (C, C0), (D, D0), (EE, E0), (FF,F0))); diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index 9404e56501..56abfb8d49 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -20,7 +20,8 @@ use pallet_cf_elections::{electoral_system::ElectoralSystem, vote_storage::VoteS use state_chain_runtime::{ chainflip::solana_elections::{ SolanaBlockHeightTracking, SolanaEgressWitnessing, SolanaElectoralSystem, - SolanaFeeTracking, SolanaIngressTracking, SolanaNonceTracking, TransactionSuccessDetails, + SolanaFeeTracking, SolanaIngressTracking, SolanaNonceTracking, SolanaVaultSwapTracking, + TransactionSuccessDetails, }, SolanaInstance, }; @@ -140,6 +141,25 @@ impl VoterApi for SolanaEgressWitnessingVoter { } } +#[derive(Clone)] +struct SolanaVaultSwapsVoter { + client: SolRetryRpcClient, +} + +#[async_trait::async_trait] +impl VoterApi for SolanaVaultSwapsVoter { + async fn vote( + &self, + _settings: ::ElectoralSettings, + _properties: ::ElectionProperties, + ) -> Result< + <::Vote as VoteStorage>::Vote, + anyhow::Error, + > { + todo!() + } +} + pub async fn start( scope: &Scope<'_, anyhow::Error>, client: SolRetryRpcClient, @@ -165,7 +185,8 @@ where SolanaFeeTrackingVoter { client: client.clone() }, SolanaIngressTrackingVoter { client: client.clone() }, SolanaNonceTrackingVoter { client: client.clone() }, - SolanaEgressWitnessingVoter { client }, + SolanaEgressWitnessingVoter { client: client.clone() }, + SolanaVaultSwapsVoter { client }, )), ) .continuously_vote() diff --git a/state-chain/chains/src/address.rs b/state-chain/chains/src/address.rs index 77a1cd2d4a..5123b7082d 100644 --- a/state-chain/chains/src/address.rs +++ b/state-chain/chains/src/address.rs @@ -97,7 +97,9 @@ impl ForeignChainAddress { } } -#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, PartialOrd, Ord)] +#[derive( + Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, PartialOrd, Ord, Serialize, Deserialize, +)] pub enum EncodedAddress { Eth([u8; 20]), Dot([u8; 32]), diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 9e0fa9f25e..0938abe5cb 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -486,7 +486,7 @@ pub trait RegisterRedemption: ApiCall<::ChainCrypto> { ) -> Self; } -pub trait CloseSolanaContractSwapAccounts: ApiCall<::ChainCrypto> { +pub trait CloseSolanaVaultSwapAccounts: ApiCall<::ChainCrypto> { fn new_unsigned( accounts: Vec, ) -> Result; diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 978219daa9..319fe00636 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -3,6 +3,7 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound}; use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; use sol_prim::consts::SOL_USDC_DECIMAL; use sp_core::RuntimeDebug; use sp_std::{vec, vec::Vec}; @@ -17,7 +18,7 @@ use crate::{ SolAsset, SolHash, SolTransaction, SolanaCrypto, }, AllBatch, AllBatchError, ApiCall, CcmChannelMetadata, Chain, ChainCrypto, ChainEnvironment, - CloseSolanaContractSwapAccounts, ConsolidateCall, ConsolidationError, ExecutexSwapAndCall, + CloseSolanaVaultSwapAccounts, ConsolidateCall, ConsolidationError, ExecutexSwapAndCall, ExecutexSwapAndCallError, FetchAssetParams, ForeignChainAddress, SetAggKeyWithAggKey, SetGovKeyWithAggKey, Solana, TransferAssetParams, TransferFallback, TransferFallbackError, }; @@ -37,7 +38,20 @@ pub struct CurrentAggKey; pub type DurableNonceAndAccount = (SolAddress, SolHash); -#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Copy, Eq)] +#[derive( + Clone, + Encode, + Decode, + PartialEq, + Debug, + TypeInfo, + Copy, + Serialize, + Deserialize, + Ord, + PartialOrd, + Eq, +)] pub struct ContractSwapAccountAndSender { pub contract_swap_account: SolAddress, pub swap_sender: SolAddress, @@ -515,7 +529,7 @@ impl TransferFallback for SolanaApi { } } -impl CloseSolanaContractSwapAccounts for SolanaApi { +impl CloseSolanaVaultSwapAccounts for SolanaApi { fn new_unsigned( accounts: Vec, ) -> Result { diff --git a/state-chain/pallets/cf-elections/src/electoral_systems.rs b/state-chain/pallets/cf-elections/src/electoral_systems.rs index 306cb2a605..5a1cbb9f6d 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems.rs @@ -5,6 +5,7 @@ pub mod egress_success; #[cfg(test)] pub mod mock; pub mod monotonic_median; +pub mod solana_swap_accounts_tracking; pub mod unsafe_median; #[cfg(test)] diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/composite.rs b/state-chain/pallets/cf-elections/src/electoral_systems/composite.rs index 602b9fa791..1a03e4bf61 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/composite.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/composite.rs @@ -31,6 +31,7 @@ mod tags { pub struct C; pub struct D; pub struct EE; + pub struct FF; } macro_rules! generate_electoral_system_tuple_impls { @@ -595,4 +596,4 @@ macro_rules! generate_electoral_system_tuple_impls { }; } -generate_electoral_system_tuple_impls!(tuple_5_impls: ((A, A0), (B, B0), (C, C0), (D, D0), (EE, E0))); +generate_electoral_system_tuple_impls!(tuple_6_impls: ((A, A0), (B, B0), (C, C0), (D, D0), (EE, E0), (FF, F0))); diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs new file mode 100644 index 0000000000..36c32d2c27 --- /dev/null +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -0,0 +1,195 @@ +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_std::collections::btree_map::BTreeMap; + +use crate::{ + electoral_system::{ + AuthorityVoteOf, ConsensusVotes, ElectionReadAccess, ElectionWriteAccess, ElectoralSystem, + ElectoralWriteAccess, VotePropertiesOf, + }, + vote_storage::{self, VoteStorage}, + CorruptStorageError, ElectionIdentifier, +}; +use cf_chains::sol::{ + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, +}; +use cf_utilities::success_threshold_from_share_count; +use frame_support::{ + pallet_prelude::{MaybeSerializeDeserialize, Member}, + sp_runtime::traits::CheckedSub, + Parameter, +}; +use itertools::Itertools; +use sp_std::vec::Vec; + +pub trait SolanaVaultSwapAccountsHook { + fn close_accounts(accounts: Vec) -> Result<(), E>; + fn initiate_vault_swap(swap_details: SwapDetails); + fn get_number_of_available_sol_nonce_accounts() -> usize; +} + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode)] +pub struct SolanaVaultSwapsElectoralState { + pub block_number_last_closed_accounts: BlockNumber, + pub witnessed_open_accounts: Vec, + pub closure_initiated_accounts: Vec, +} + +pub struct SolanaVaultSwapAccounts< + Account, + SwapDetails, + BlockNumber, + Settings, + Hook, + ValidatorId, + E, +> { + _phantom: core::marker::PhantomData<( + Account, + SwapDetails, + BlockNumber, + Settings, + Hook, + ValidatorId, + E, + )>, +} +impl< + E: sp_std::fmt::Debug + 'static, + Account: MaybeSerializeDeserialize + Member + Parameter + Ord, + SwapDetails: MaybeSerializeDeserialize + Member + Parameter + Ord, + BlockNumber: MaybeSerializeDeserialize + Member + Parameter + Ord + CheckedSub + Into + Copy, + Settings: Member + Parameter + MaybeSerializeDeserialize + Eq, + Hook: SolanaVaultSwapAccountsHook + 'static, + ValidatorId: Member + Parameter + Ord + MaybeSerializeDeserialize, + > ElectoralSystem + for SolanaVaultSwapAccounts +{ + type ValidatorId = ValidatorId; + type ElectoralUnsynchronisedState = SolanaVaultSwapsElectoralState; + type ElectoralUnsynchronisedStateMapKey = (); + type ElectoralUnsynchronisedStateMapValue = (); + + type ElectoralUnsynchronisedSettings = (); + type ElectoralSettings = Settings; + type ElectionIdentifierExtra = (); + type ElectionProperties = (); + type ElectionState = (); + type Vote = vote_storage::bitmap::Bitmap>; + type Consensus = Vec<(Account, SwapDetails)>; + type OnFinalizeContext = BlockNumber; + type OnFinalizeReturn = (); + + fn generate_vote_properties( + _election_identifier: ElectionIdentifier, + _previous_vote: Option<(VotePropertiesOf, AuthorityVoteOf)>, + _vote: &::PartialVote, + ) -> Result, CorruptStorageError> { + Ok(()) + } + + fn on_finalize>( + electoral_access: &mut ElectoralAccess, + election_identifiers: Vec>, + current_block_number: &Self::OnFinalizeContext, + ) -> Result { + if let Some(election_identifier) = election_identifiers + .into_iter() + .at_most_one() + .map_err(|_| CorruptStorageError::new())? + { + let mut election_access = electoral_access.election_mut(election_identifier)?; + if let Some(consensus) = election_access.check_consensus()?.has_consensus() { + election_access.delete(); + electoral_access.new_election((), (), ())?; + electoral_access.mutate_unsynchronised_state( + |_electoral_access, unsynchronised_state| { + unsynchronised_state.witnessed_open_accounts.extend( + consensus.into_iter().map(|(account, swap_details)| { + Hook::initiate_vault_swap(swap_details); + account + }), + ); + + Ok(()) + }, + )?; + } + } else { + electoral_access.new_election((), (), ())?; + } + + electoral_access.mutate_unsynchronised_state(|_, unsynchronised_state| { + //let current_block_number = BlockNumbrer; + // //frame_system::Pallet::::current_block_number(); + if Hook::get_number_of_available_sol_nonce_accounts() > + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && + (unsynchronised_state.witnessed_open_accounts.len() >= + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || + (*current_block_number) + .checked_sub(&unsynchronised_state.block_number_last_closed_accounts) + .expect("") + .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) + { + let accounts_to_close: Vec<_> = + if unsynchronised_state.witnessed_open_accounts.len() > + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES + { + unsynchronised_state + .witnessed_open_accounts + .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) + .collect() + } else { + sp_std::mem::take(&mut unsynchronised_state.witnessed_open_accounts) + }; + match Hook::close_accounts(accounts_to_close.clone()) { + Ok(()) => { + unsynchronised_state.block_number_last_closed_accounts = + *current_block_number; + unsynchronised_state.closure_initiated_accounts.extend(accounts_to_close); + Ok(()) + }, + Err(e) => { + log::error!( + "failed to build Solana CloseSolanaVaultSwapAccounts apicall: {:?}", + e + ); + Err(CorruptStorageError::new()) + }, + } + } else { + Ok(()) + } + }) + } + + fn check_consensus>( + _election_identifier: ElectionIdentifier, + _election_access: &ElectionAccess, + _previous_consensus: Option<&Self::Consensus>, + consensus_votes: ConsensusVotes, + ) -> Result, CorruptStorageError> { + let num_authorities = consensus_votes.num_authorities(); + let success_threshold = success_threshold_from_share_count(num_authorities); + let active_votes = consensus_votes.active_votes(); + let num_active_votes = active_votes.len() as u32; + Ok(if num_active_votes >= success_threshold { + let mut counts = BTreeMap::new(); + for vote in active_votes { + counts.entry(vote).and_modify(|count| *count += 1).or_insert(1); + } + counts.iter().find_map(|(vote, count)| { + if *count >= success_threshold { + Some(vote.clone()) + } else { + None + } + }) + } else { + None + }) + } +} diff --git a/state-chain/pallets/cf-elections/src/lib.rs b/state-chain/pallets/cf-elections/src/lib.rs index e1932cbc6c..0c6dc8abf4 100644 --- a/state-chain/pallets/cf-elections/src/lib.rs +++ b/state-chain/pallets/cf-elections/src/lib.rs @@ -136,6 +136,7 @@ pub mod pallet { use cf_chains::benchmarking_value::BenchmarkValue; use cf_primitives::{AuthorityCount, EpochIndex}; use cf_traits::{AccountRoleRegistry, Chainflip, EpochInfo}; + use frame_support::sp_runtime::traits::{Header, HeaderProvider}; use crate::electoral_system::{ConsensusVote, ConsensusVotes}; use access_impls::{ElectionAccess, ElectoralAccess}; @@ -356,7 +357,7 @@ pub mod pallet { + IsType<::RuntimeEvent>; type ElectoralSystem: ElectoralSystem< - OnFinalizeContext = (), + OnFinalizeContext = <::HeaderT as Header>::Number, ValidatorId = ::ValidatorId, >; @@ -1653,7 +1654,7 @@ pub mod pallet { T::ElectoralSystem::on_finalize( electoral_access, election_identifiers, - &(), + &frame_system::Pallet::::current_block_number(), )?; Ok(()) diff --git a/state-chain/pallets/cf-elections/src/vote_storage/composite.rs b/state-chain/pallets/cf-elections/src/vote_storage/composite.rs index 5ba58c7c44..036d34eac7 100644 --- a/state-chain/pallets/cf-elections/src/vote_storage/composite.rs +++ b/state-chain/pallets/cf-elections/src/vote_storage/composite.rs @@ -279,3 +279,4 @@ generate_vote_storage_tuple_impls!(tuple_2_impls: (A, B)); generate_vote_storage_tuple_impls!(tuple_3_impls: (A, B, C)); generate_vote_storage_tuple_impls!(tuple_4_impls: (A, B, C, D)); generate_vote_storage_tuple_impls!(tuple_5_impls: (A, B, C, D, EE)); +generate_vote_storage_tuple_impls!(tuple_6_impls: (A, B, C, D, EE, FF)); diff --git a/state-chain/pallets/cf-elections/src/vote_storage/individual/composite.rs b/state-chain/pallets/cf-elections/src/vote_storage/individual/composite.rs index e85eb5de33..739940086e 100644 --- a/state-chain/pallets/cf-elections/src/vote_storage/individual/composite.rs +++ b/state-chain/pallets/cf-elections/src/vote_storage/individual/composite.rs @@ -81,3 +81,4 @@ generate_individual_vote_storage_tuple_impls!(tuple_2_impls: (A, B)); generate_individual_vote_storage_tuple_impls!(tuple_3_impls: (A, B, C)); generate_individual_vote_storage_tuple_impls!(tuple_4_impls: (A, B, C, D)); generate_individual_vote_storage_tuple_impls!(tuple_5_impls: (A, B, C, D, EE)); +generate_individual_vote_storage_tuple_impls!(tuple_6_impls: (A, B, C, D, EE, FF)); diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 809e43a2eb..5b8c998e41 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -4,8 +4,6 @@ #![doc = include_str!("../../cf-doc-head.md")] use cf_chains::{ - address::EncodedAddress, - assets::any::Asset, btc::{ api::{SelectedUtxosAndChangeAmount, UtxoSelectionType}, deposit_address::DepositAddress, @@ -21,11 +19,11 @@ use cf_chains::{ MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }, - Chain, CloseSolanaContractSwapAccounts, + Chain, CloseSolanaVaultSwapAccounts, }; use cf_primitives::{ chains::assets::{arb::Asset as ArbAsset, eth::Asset as EthAsset}, - AssetAmount, NetworkEnvironment, SemVer, TransactionHash, + NetworkEnvironment, SemVer, }; use cf_traits::{ CompatibleCfeVersions, GetBitcoinFeeInfo, KeyProvider, NetworkEnvironmentProvider, SafeMode, @@ -103,9 +101,9 @@ pub mod pallet { type SolanaNonceWatch: SolanaNonceWatch; - type CloseSolanaContractSwapAccounts: cf_chains::CloseSolanaContractSwapAccounts; + type CloseSolanaVaultSwapAccounts: cf_chains::CloseSolanaVaultSwapAccounts; - type SolanaBroadcaster: Broadcaster; + type SolanaBroadcaster: Broadcaster; type SolanaContractSwapper: SolanaContractSwap; @@ -294,7 +292,7 @@ pub mod pallet { } else { sp_std::mem::take(accounts) }; - match T::CloseSolanaContractSwapAccounts::new_unsigned(accounts_to_close.clone()) { + match T::CloseSolanaVaultSwapAccounts::new_unsigned(accounts_to_close.clone()) { Ok(apicall) => { let (broadcast_id, _) = T::SolanaBroadcaster::threshold_sign_and_broadcast(apicall); @@ -818,31 +816,31 @@ impl Pallet { } } - pub fn report_sol_contract_swap_accounts( - new_contract_swaps: Vec<(ContractSwapAccountAndSender, SolanaContractSwapDetails)>, - confirm_closed_accounts: Vec, - ) { - SolanaOpenContractSwapAccounts::::mutate(|accts| { - accts.extend( - new_contract_swaps - .into_iter() - .map(|(contract_swap_account, swap_details)| { - T::SolanaContractSwapper::initiate_contract_swap( - swap_details.from, - swap_details.to, - swap_details.deposit_amount, - swap_details.destination_address, - swap_details.tx_hash, - ); - contract_swap_account - }) - .collect::>(), - ) - }); - confirm_closed_accounts - .into_iter() - .for_each(SolanaClosedContractSwapAccounts::::remove); - } + // pub fn report_sol_contract_swap_accounts( + // new_contract_swaps: Vec<(ContractSwapAccountAndSender, SolanaContractSwapDetails)>, + // confirm_closed_accounts: Vec, + // ) { + // SolanaOpenContractSwapAccounts::::mutate(|accts| { + // accts.extend( + // new_contract_swaps + // .into_iter() + // .map(|(contract_swap_account, swap_details)| { + // T::SolanaContractSwapper::initiate_contract_swap( + // swap_details.from, + // swap_details.to, + // swap_details.deposit_amount, + // swap_details.destination_address, + // swap_details.tx_hash, + // ); + // contract_swap_account + // }) + // .collect::>(), + // ) + // }); + // confirm_closed_accounts + // .into_iter() + // .for_each(SolanaClosedContractSwapAccounts::::remove); + // } #[allow(dead_code)] fn get_all_sol_contract_swap_accounts() -> Vec { @@ -863,11 +861,3 @@ impl NetworkEnvironmentProvider for Pallet { Self::network_environment() } } - -pub struct SolanaContractSwapDetails { - from: Asset, - to: Asset, - deposit_amount: AssetAmount, - destination_address: EncodedAddress, - tx_hash: TransactionHash, -} diff --git a/state-chain/pallets/cf-environment/src/mock.rs b/state-chain/pallets/cf-environment/src/mock.rs index 96da2f1355..9f4e66490e 100644 --- a/state-chain/pallets/cf-environment/src/mock.rs +++ b/state-chain/pallets/cf-environment/src/mock.rs @@ -9,8 +9,7 @@ use cf_chains::{ api::{ContractSwapAccountAndSender, SolanaTransactionBuildingError}, SolanaCrypto, }, - ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, CloseSolanaContractSwapAccounts, Polkadot, - Solana, + ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, CloseSolanaVaultSwapAccounts, Polkadot, Solana, }; use cf_primitives::SemVer; use cf_traits::{ @@ -166,11 +165,11 @@ impl_mock_runtime_safe_mode!(mock: MockPalletSafeMode); pub type MockBitcoinKeyProvider = MockKeyProvider; #[derive(Clone, Debug, Default, Eq, PartialEq, Encode, Decode, TypeInfo)] -pub struct MockCloseSolanaContractSwapAccounts { +pub struct MockCloseSolanaVaultSwapAccounts { contract_swap_accounts_and_senders: Vec, } -impl CloseSolanaContractSwapAccounts for MockCloseSolanaContractSwapAccounts { +impl CloseSolanaVaultSwapAccounts for MockCloseSolanaVaultSwapAccounts { fn new_unsigned( accounts: Vec, ) -> Result { @@ -178,7 +177,7 @@ impl CloseSolanaContractSwapAccounts for MockCloseSolanaContractSwapAccounts { } } -impl ApiCall for MockCloseSolanaContractSwapAccounts { +impl ApiCall for MockCloseSolanaVaultSwapAccounts { fn threshold_signature_payload( &self, ) -> <::ChainCrypto as ChainCrypto>::Payload { @@ -236,8 +235,8 @@ impl pallet_cf_environment::Config for Test { type ArbitrumVaultKeyWitnessedHandler = MockArbitrumVaultKeyWitnessedHandler; type SolanaVaultKeyWitnessedHandler = MockSolanaVaultKeyWitnessedHandler; type SolanaNonceWatch = (); - type CloseSolanaContractSwapAccounts = MockCloseSolanaContractSwapAccounts; - type SolanaBroadcaster = MockBroadcaster<(MockCloseSolanaContractSwapAccounts, RuntimeCall)>; + type CloseSolanaVaultSwapAccounts = MockCloseSolanaVaultSwapAccounts; + type SolanaBroadcaster = MockBroadcaster<(MockCloseSolanaVaultSwapAccounts, RuntimeCall)>; type SolanaContractSwapper = MockSolanaContractSwapper; type BitcoinFeeInfo = MockBitcoinFeeInfo; type BitcoinKeyProvider = MockBitcoinKeyProvider; diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 9ee808d3cc..77c2e70cb4 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -1,28 +1,41 @@ use crate::{Environment, Runtime, SolanaBroadcaster, SolanaChainTracking, SolanaThresholdSigner}; use cf_chains::{ instances::ChainInstanceAlias, - sol::{SolAddress, SolAmount, SolHash, SolSignature, SolTrackedData, SolanaCrypto}, + sol::{ + api::{SolanaApi, SolanaTransactionBuildingError}, + SolAddress, SolAmount, SolHash, SolSignature, SolTrackedData, SolanaCrypto, + }, Chain, FeeEstimationApi, Solana, }; use cf_runtime_utilities::log_or_panic; use cf_traits::{ - AdjustedFeeEstimationApi, Chainflip, ElectionEgressWitnesser, GetBlockHeight, IngressSource, - SolanaNonceWatch, + AdjustedFeeEstimationApi, Broadcaster, Chainflip, ElectionEgressWitnesser, GetBlockHeight, + IngressSource, SolanaNonceWatch, }; use codec::{Decode, Encode}; +use frame_system::pallet_prelude::BlockNumberFor; use pallet_cf_elections::{ electoral_system::{ElectoralReadAccess, ElectoralSystem}, electoral_systems::{ self, change::OnChangeHook, - composite::{tuple_5_impls::Hooks, Composite, Translator}, + composite::{tuple_6_impls::Hooks, Composite, Translator}, egress_success::OnEgressSuccess, monotonic_median::MedianChangeHook, + solana_swap_accounts_tracking::{ + SolanaVaultSwapAccountsHook, SolanaVaultSwapsElectoralState, + }, }, CorruptStorageError, ElectionIdentifier, InitialState, InitialStateOf, }; +use crate::{RuntimeOrigin, SolanaIngressEgress}; +use cf_chains::{ + address::EncodedAddress, assets::any::Asset, sol::api::ContractSwapAccountAndSender, + CloseSolanaVaultSwapAccounts, +}; +use cf_primitives::{AssetAmount, TransactionHash}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{DispatchResult, FixedPointNumber, FixedU128}; @@ -31,6 +44,8 @@ use sp_std::vec::Vec; #[cfg(feature = "runtime-benchmarks")] use cf_chains::benchmarking_value::BenchmarkValue; +use super::SolEnvironment; + type Instance = ::Instance; pub type SolanaElectoralSystem = Composite< @@ -40,6 +55,7 @@ pub type SolanaElectoralSystem = Composite< SolanaIngressTracking, SolanaNonceTracking, SolanaEgressWitnessing, + SolanaVaultSwapTracking, ), ::ValidatorId, SolanaElectionHooks, @@ -60,6 +76,11 @@ pub fn initial_state( (), (), (), + SolanaVaultSwapsElectoralState { + block_number_last_closed_accounts: 0, + witnessed_open_accounts: vec![], + closure_initiated_accounts: vec![], + }, ), unsynchronised_settings: ( (), @@ -67,8 +88,16 @@ pub fn initial_state( (), (), (), + (), + ), + settings: ( + (), + (), + SolanaIngressSettings { vault_program, usdc_token_mint_pubkey }, + (), + (), + (), ), - settings: ((), (), SolanaIngressSettings { vault_program, usdc_token_mint_pubkey }, (), ()), } } @@ -107,6 +136,17 @@ pub type SolanaEgressWitnessing = electoral_systems::egress_success::EgressSucce ::ValidatorId, >; +pub type SolanaVaultSwapTracking = + electoral_systems::solana_swap_accounts_tracking::SolanaVaultSwapAccounts< + ContractSwapAccountAndSender, + SolanaVaultSwapDetails, + BlockNumberFor, + (), + SolanaVaultSwapsHandler, + ::ValidatorId, + SolanaTransactionBuildingError, + >; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, TypeInfo)] pub struct TransactionSuccessDetails { pub tx_fee: u64, @@ -167,9 +207,10 @@ impl SolanaIngressTracking, SolanaNonceTracking, SolanaEgressWitnessing, + SolanaVaultSwapTracking, > for SolanaElectionHooks { - type OnFinalizeContext = (); + type OnFinalizeContext = BlockNumberFor; type OnFinalizeReturn = (); fn on_finalize< @@ -179,6 +220,7 @@ impl IngressTranslator: Translator, NonceTrackingTranslator: Translator, EgressWitnessingTranslator: Translator, + VaultSwapTranslator: Translator, >( generic_electoral_access: &mut GenericElectoralAccess, ( @@ -187,12 +229,14 @@ impl ingress_translator, nonce_tracking_translator, egress_witnessing_translator, + vault_swap_translator, ): ( BlockHeightTranslator, FeeTranslator, IngressTranslator, NonceTrackingTranslator, EgressWitnessingTranslator, + VaultSwapTranslator, ), ( block_height_identifiers, @@ -200,6 +244,7 @@ impl ingress_identifiers, nonce_tracking_identifiers, egress_witnessing_identifiers, + vault_swap_identifiers, ): ( Vec< ElectionIdentifier< @@ -224,8 +269,13 @@ impl ::ElectionIdentifierExtra, >, >, + Vec< + ElectionIdentifier< + ::ElectionIdentifierExtra, + >, + >, ), - _context: &Self::OnFinalizeContext, + context: &Self::OnFinalizeContext, ) -> Result { let block_height = SolanaBlockHeightTracking::on_finalize( &mut block_height_translator.translate_electoral_access(generic_electoral_access), @@ -252,6 +302,11 @@ impl ingress_identifiers, &block_height, )?; + SolanaVaultSwapTracking::on_finalize( + &mut vault_swap_translator.translate_electoral_access(generic_electoral_access), + vault_swap_identifiers, + context, + )?; Ok(()) } } @@ -429,7 +484,7 @@ impl ElectionEgressWitnesser for SolanaEgressWitnessingTrigger { pallet_cf_elections::Pallet::::with_electoral_access( |electoral_access| { SolanaElectoralSystem::with_access_translators(|access_translators| { - let (_, _, _, _, access_translator) = &access_translators; + let (_, _, _, _, access_translator, ..) = &access_translators; let mut electoral_access = access_translator.translate_electoral_access(electoral_access); @@ -439,3 +494,50 @@ impl ElectionEgressWitnesser for SolanaEgressWitnessingTrigger { ) } } + +#[derive( + Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode, PartialOrd, Ord, +)] +pub struct SolanaVaultSwapDetails { + from: Asset, + to: Asset, + deposit_amount: AssetAmount, + destination_address: EncodedAddress, + tx_hash: TransactionHash, +} +pub struct SolanaVaultSwapsHandler; + +impl + SolanaVaultSwapAccountsHook< + ContractSwapAccountAndSender, + SolanaVaultSwapDetails, + SolanaTransactionBuildingError, + > for SolanaVaultSwapsHandler +{ + fn initiate_vault_swap(swap_details: SolanaVaultSwapDetails) { + let _ = SolanaIngressEgress::contract_swap_request( + RuntimeOrigin::root(), + swap_details.from, + swap_details.to, + swap_details.deposit_amount, + swap_details.destination_address, + swap_details.tx_hash, + ); + } + + fn close_accounts( + accounts: Vec, + ) -> Result<(), SolanaTransactionBuildingError> { + as CloseSolanaVaultSwapAccounts>::new_unsigned(accounts).map( + |apicall| { + let _ = >::threshold_sign_and_broadcast( + apicall, + ); + }, + ) + } + + fn get_number_of_available_sol_nonce_accounts() -> usize { + Environment::get_number_of_available_sol_nonce_accounts() + } +} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index 3b6f0c5eeb..bf3f8ac505 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -270,7 +270,7 @@ impl pallet_cf_environment::Config for Runtime { type ArbitrumVaultKeyWitnessedHandler = ArbitrumVault; type SolanaVaultKeyWitnessedHandler = SolanaVault; type SolanaNonceWatch = SolanaNonceTrackingTrigger; - type CloseSolanaContractSwapAccounts = SolanaApi; + type CloseSolanaVaultSwapAccounts = SolanaApi; type SolanaBroadcaster = SolanaBroadcaster; type SolanaContractSwapper = SolanaContractSwapper; type BitcoinFeeInfo = chainflip::BitcoinFeeGetter; From 4e458fbf752bf14ddd0e26a9be6130dab586b245 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 25 Oct 2024 12:10:50 +0200 Subject: [PATCH 07/23] feat: account closing tracking in elections --- .../solana_swap_accounts_tracking.rs | 30 ++++++++++++------- .../runtime/src/chainflip/solana_elections.rs | 17 +++++------ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 36c32d2c27..4beb8306af 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -1,7 +1,7 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use sp_std::collections::btree_map::BTreeMap; +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use crate::{ electoral_system::{ @@ -32,10 +32,18 @@ pub trait SolanaVaultSwapAccountsHook { } #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode)] -pub struct SolanaVaultSwapsElectoralState { +pub struct SolanaVaultSwapsElectoralState { pub block_number_last_closed_accounts: BlockNumber, pub witnessed_open_accounts: Vec, - pub closure_initiated_accounts: Vec, + pub closure_initiated_accounts: BTreeSet, +} + +#[derive( + Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode, Ord, PartialOrd, +)] +pub struct SolanaVaultSwapsVote { + pub new_accounts: BTreeSet<(Account, SwapDetails)>, + pub confirm_closed_accounts: BTreeSet, } pub struct SolanaVaultSwapAccounts< @@ -78,8 +86,8 @@ impl< type ElectionIdentifierExtra = (); type ElectionProperties = (); type ElectionState = (); - type Vote = vote_storage::bitmap::Bitmap>; - type Consensus = Vec<(Account, SwapDetails)>; + type Vote = vote_storage::bitmap::Bitmap>; + type Consensus = SolanaVaultSwapsVote; type OnFinalizeContext = BlockNumber; type OnFinalizeReturn = (); @@ -108,12 +116,16 @@ impl< electoral_access.mutate_unsynchronised_state( |_electoral_access, unsynchronised_state| { unsynchronised_state.witnessed_open_accounts.extend( - consensus.into_iter().map(|(account, swap_details)| { - Hook::initiate_vault_swap(swap_details); - account + consensus.new_accounts.iter().map(|(account, swap_details)| { + Hook::initiate_vault_swap((*swap_details).clone()); + (*account).clone() }), ); + consensus.confirm_closed_accounts.into_iter().for_each(|acc| { + unsynchronised_state.closure_initiated_accounts.remove(&acc); + }); + Ok(()) }, )?; @@ -123,8 +135,6 @@ impl< } electoral_access.mutate_unsynchronised_state(|_, unsynchronised_state| { - //let current_block_number = BlockNumbrer; - // //frame_system::Pallet::::current_block_number(); if Hook::get_number_of_available_sol_nonce_accounts() > NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && (unsynchronised_state.witnessed_open_accounts.len() >= diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 77c2e70cb4..f7e043d39b 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -13,6 +13,12 @@ use cf_traits::{ IngressSource, SolanaNonceWatch, }; +use crate::{RuntimeOrigin, SolanaIngressEgress}; +use cf_chains::{ + address::EncodedAddress, assets::any::Asset, sol::api::ContractSwapAccountAndSender, + CloseSolanaVaultSwapAccounts, +}; +use cf_primitives::{AssetAmount, TransactionHash}; use codec::{Decode, Encode}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_cf_elections::{ @@ -29,17 +35,10 @@ use pallet_cf_elections::{ }, CorruptStorageError, ElectionIdentifier, InitialState, InitialStateOf, }; - -use crate::{RuntimeOrigin, SolanaIngressEgress}; -use cf_chains::{ - address::EncodedAddress, assets::any::Asset, sol::api::ContractSwapAccountAndSender, - CloseSolanaVaultSwapAccounts, -}; -use cf_primitives::{AssetAmount, TransactionHash}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{DispatchResult, FixedPointNumber, FixedU128}; -use sp_std::vec::Vec; +use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; #[cfg(feature = "runtime-benchmarks")] use cf_chains::benchmarking_value::BenchmarkValue; @@ -79,7 +78,7 @@ pub fn initial_state( SolanaVaultSwapsElectoralState { block_number_last_closed_accounts: 0, witnessed_open_accounts: vec![], - closure_initiated_accounts: vec![], + closure_initiated_accounts: BTreeSet::new(), }, ), unsynchronised_settings: ( From 695af88cb8e444a7c22527531d9f20333bae903c Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 25 Oct 2024 13:41:08 +0200 Subject: [PATCH 08/23] chore: clippy --- engine/src/witness/sol.rs | 1 + .../cf-integration-tests/src/mock_runtime.rs | 11 ++++++++++- state-chain/chains/src/sol/benchmarking.rs | 13 +++++++++++-- .../cf-elections/src/electoral_systems/mock.rs | 2 +- .../solana_swap_accounts_tracking.rs | 16 +++++++++++++++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index 56abfb8d49..b8eed7354a 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -141,6 +141,7 @@ impl VoterApi for SolanaEgressWitnessingVoter { } } +#[allow(dead_code)] #[derive(Clone)] struct SolanaVaultSwapsVoter { client: SolRetryRpcClient, diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 8be7c3a0d7..8f9f3d7a9e 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -10,7 +10,9 @@ use chainflip_node::{ chain_spec::testnet::{EXPIRY_SPAN_IN_SECONDS, REDEMPTION_TTL_SECS}, test_account_from_seed, }; -use pallet_cf_elections::InitialState; +use pallet_cf_elections::{ + electoral_systems::solana_swap_accounts_tracking::SolanaVaultSwapsElectoralState, InitialState, +}; use pallet_cf_validator::SetSizeParameters; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -303,6 +305,11 @@ impl ExtBuilder { (), (), (), + SolanaVaultSwapsElectoralState { + block_number_last_closed_accounts: Default::default(), + witnessed_open_accounts: Default::default(), + closure_initiated_accounts: Default::default(), + }, ), unsynchronised_settings: ( (), @@ -312,6 +319,7 @@ impl ExtBuilder { (), (), (), + (), ), settings: ( (), @@ -322,6 +330,7 @@ impl ExtBuilder { }, (), (), + (), ), }), }, diff --git a/state-chain/chains/src/sol/benchmarking.rs b/state-chain/chains/src/sol/benchmarking.rs index 1eb8b75659..06a8dead67 100644 --- a/state-chain/chains/src/sol/benchmarking.rs +++ b/state-chain/chains/src/sol/benchmarking.rs @@ -1,7 +1,8 @@ #![cfg(feature = "runtime-benchmarks")] use super::{ - api::SolanaApi, SolAddress, SolHash, SolMessage, SolSignature, SolTrackedData, SolTransaction, + api::{ContractSwapAccountAndSender, SolanaApi}, + SolAddress, SolHash, SolMessage, SolSignature, SolTrackedData, SolTransaction, SolanaTransactionData, }; @@ -25,7 +26,6 @@ impl BenchmarkValue for SolTrackedData { } } -#[cfg(feature = "runtime-benchmarks")] impl BenchmarkValue for SolMessage { fn benchmark_value() -> Self { Self::new_with_blockhash(&[], None, &SolHash::default().into()) @@ -66,3 +66,12 @@ impl BenchmarkValue for SolanaApi { .expect("Benchmark value for SolApi must work.") } } + +impl BenchmarkValue for ContractSwapAccountAndSender { + fn benchmark_value() -> Self { + Self { + swap_sender: BenchmarkValue::benchmark_value(), + contract_swap_account: BenchmarkValue::benchmark_value(), + } + } +} diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs b/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs index dad3538d90..b7bdc1665e 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs @@ -130,7 +130,7 @@ impl ElectoralSystem for MockElectoralSystem { type Vote = vote_storage::individual::Individual<(), vote_storage::individual::shared::Shared<()>>; type Consensus = AuthorityCount; - type OnFinalizeContext = (); + type OnFinalizeContext = u64; type OnFinalizeReturn = (); fn generate_vote_properties( diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 4beb8306af..70a4406e75 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -3,6 +3,9 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; +#[cfg(feature = "runtime-benchmarks")] +use cf_chains::benchmarking_value::BenchmarkValue; + use crate::{ electoral_system::{ AuthorityVoteOf, ConsensusVotes, ElectionReadAccess, ElectionWriteAccess, ElectoralSystem, @@ -12,7 +15,7 @@ use crate::{ CorruptStorageError, ElectionIdentifier, }; use cf_chains::sol::{ - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + api::ContractSwapAccountAndSender, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }; @@ -38,6 +41,17 @@ pub struct SolanaVaultSwapsElectoralState { pub closure_initiated_accounts: BTreeSet, } +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkValue for SolanaVaultSwapsElectoralState { + fn benchmark_value() -> Self { + Self { + block_number_last_closed_accounts: 1u32, + witnessed_open_accounts: vec![BenchmarkValue::benchmark_value()], + closure_initiated_accounts: BTreeSet::from([BenchmarkValue::benchmark_value()]), + } + } +} + #[derive( Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode, Ord, PartialOrd, )] From a4bfda699c052d11607f446ad3596fecdc643569 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 25 Oct 2024 13:58:17 +0200 Subject: [PATCH 09/23] chore: minor --- .../solana_swap_accounts_tracking.rs | 97 +++++++++---------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 70a4406e75..de265867e9 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -127,67 +127,62 @@ impl< if let Some(consensus) = election_access.check_consensus()?.has_consensus() { election_access.delete(); electoral_access.new_election((), (), ())?; - electoral_access.mutate_unsynchronised_state( - |_electoral_access, unsynchronised_state| { - unsynchronised_state.witnessed_open_accounts.extend( - consensus.new_accounts.iter().map(|(account, swap_details)| { - Hook::initiate_vault_swap((*swap_details).clone()); - (*account).clone() - }), - ); + electoral_access.mutate_unsynchronised_state(|_, unsynchronised_state| { + unsynchronised_state.witnessed_open_accounts.extend( + consensus.new_accounts.iter().map(|(account, swap_details)| { + Hook::initiate_vault_swap((*swap_details).clone()); + (*account).clone() + }), + ); - consensus.confirm_closed_accounts.into_iter().for_each(|acc| { - unsynchronised_state.closure_initiated_accounts.remove(&acc); - }); + consensus.confirm_closed_accounts.into_iter().for_each(|acc| { + unsynchronised_state.closure_initiated_accounts.remove(&acc); + }); - Ok(()) - }, - )?; + Ok(()) + })?; } } else { electoral_access.new_election((), (), ())?; } - electoral_access.mutate_unsynchronised_state(|_, unsynchronised_state| { - if Hook::get_number_of_available_sol_nonce_accounts() > - NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && - (unsynchronised_state.witnessed_open_accounts.len() >= - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || - (*current_block_number) - .checked_sub(&unsynchronised_state.block_number_last_closed_accounts) - .expect("") - .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) + let mut unsynchronised_state = electoral_access.unsynchronised_state()?; + if Hook::get_number_of_available_sol_nonce_accounts() > + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && + (unsynchronised_state.witnessed_open_accounts.len() >= + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || + (*current_block_number) + .checked_sub(&unsynchronised_state.block_number_last_closed_accounts) + .expect( + "current block number is always greater than when apicall was last created", + ) + .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) + { + let accounts_to_close: Vec<_> = if unsynchronised_state.witnessed_open_accounts.len() > + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES { - let accounts_to_close: Vec<_> = - if unsynchronised_state.witnessed_open_accounts.len() > - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES - { - unsynchronised_state - .witnessed_open_accounts - .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) - .collect() - } else { - sp_std::mem::take(&mut unsynchronised_state.witnessed_open_accounts) - }; - match Hook::close_accounts(accounts_to_close.clone()) { - Ok(()) => { - unsynchronised_state.block_number_last_closed_accounts = - *current_block_number; - unsynchronised_state.closure_initiated_accounts.extend(accounts_to_close); - Ok(()) - }, - Err(e) => { - log::error!( - "failed to build Solana CloseSolanaVaultSwapAccounts apicall: {:?}", - e - ); - Err(CorruptStorageError::new()) - }, - } + unsynchronised_state + .witnessed_open_accounts + .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) + .collect() } else { - Ok(()) + sp_std::mem::take(&mut unsynchronised_state.witnessed_open_accounts) + }; + match Hook::close_accounts(accounts_to_close.clone()) { + Ok(()) => { + unsynchronised_state.block_number_last_closed_accounts = *current_block_number; + unsynchronised_state.closure_initiated_accounts.extend(accounts_to_close); + electoral_access.set_unsynchronised_state(unsynchronised_state)?; + }, + Err(e) => { + log::error!( + "failed to build Solana CloseSolanaVaultSwapAccounts apicall: {:?}", + e + ); + }, } - }) + } + Ok(()) } fn check_consensus>( From df598e8bff697bd23de986b01bca922b64fc88af Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Fri, 25 Oct 2024 14:11:42 +0200 Subject: [PATCH 10/23] chore: remove previous impl --- state-chain/pallets/cf-environment/src/lib.rs | 115 +----------------- .../pallets/cf-environment/src/mock.rs | 77 +----------- state-chain/runtime/src/chainflip.rs | 29 +---- state-chain/runtime/src/lib.rs | 7 +- state-chain/traits/src/lib.rs | 10 -- 5 files changed, 14 insertions(+), 224 deletions(-) diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs index 5b8c998e41..4b62f53cb7 100644 --- a/state-chain/pallets/cf-environment/src/lib.rs +++ b/state-chain/pallets/cf-environment/src/lib.rs @@ -12,14 +12,8 @@ use cf_chains::{ }, dot::{Polkadot, PolkadotAccountId, PolkadotHash, PolkadotIndex}, eth::Address as EvmAddress, - sol::{ - api::{ContractSwapAccountAndSender, DurableNonceAndAccount}, - SolAddress, SolApiEnvironment, SolHash, Solana, - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, - MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, - NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, - }, - Chain, CloseSolanaVaultSwapAccounts, + sol::{api::DurableNonceAndAccount, SolAddress, SolApiEnvironment, SolHash, Solana}, + Chain, }; use cf_primitives::{ chains::assets::{arb::Asset as ArbAsset, eth::Asset as EthAsset}, @@ -27,9 +21,9 @@ use cf_primitives::{ }; use cf_traits::{ CompatibleCfeVersions, GetBitcoinFeeInfo, KeyProvider, NetworkEnvironmentProvider, SafeMode, - SolanaContractSwap, SolanaNonceWatch, + SolanaNonceWatch, }; -use frame_support::{pallet_prelude::*, sp_runtime::traits::CheckedSub, traits::StorageVersion}; +use frame_support::{pallet_prelude::*, traits::StorageVersion}; use frame_system::pallet_prelude::*; pub use pallet::*; use sp_std::{vec, vec::Vec}; @@ -71,8 +65,8 @@ pub enum SafeModeUpdate { pub mod pallet { use super::*; use cf_chains::{btc::Utxo, sol::api::DurableNonceAndAccount, Arbitrum}; - use cf_primitives::{BroadcastId, TxId}; - use cf_traits::{Broadcaster, VaultKeyWitnessedHandler}; + use cf_primitives::TxId; + use cf_traits::VaultKeyWitnessedHandler; use frame_support::DefaultNoBound; #[pallet::config] @@ -101,12 +95,6 @@ pub mod pallet { type SolanaNonceWatch: SolanaNonceWatch; - type CloseSolanaVaultSwapAccounts: cf_chains::CloseSolanaVaultSwapAccounts; - - type SolanaBroadcaster: Broadcaster; - - type SolanaContractSwapper: SolanaContractSwap; - /// Used to access the current Chainflip runtime's release version (distinct from the /// substrate RuntimeVersion) #[pallet::constant] @@ -240,21 +228,6 @@ pub mod pallet { #[pallet::getter(fn solana_api_environment)] pub type SolanaApiEnvironment = StorageValue<_, SolApiEnvironment, ValueQuery>; - #[pallet::storage] - #[pallet::getter(fn solana_open_contract_swap_accounts)] - pub type SolanaOpenContractSwapAccounts = - StorageValue<_, Vec, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn solana_closed_contract_swap_accounts)] - pub type SolanaClosedContractSwapAccounts = - StorageMap<_, Blake2_128Concat, ContractSwapAccountAndSender, ()>; - - #[pallet::storage] - #[pallet::getter(fn solana_last_closed_contract_swap_accounts_at)] - pub type SolanaLastClosedContractSwapAccountsAt = - StorageValue<_, BlockNumberFor, OptionQuery>; - // OTHER ENVIRONMENT ITEMS #[pallet::storage] #[pallet::getter(fn safe_mode)] @@ -272,47 +245,6 @@ pub mod pallet { /// Contains the network environment for this runtime. pub type ChainflipNetworkEnvironment = StorageValue<_, NetworkEnvironment, ValueQuery>; - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_idle(block_number: BlockNumberFor, _remaining_weight: Weight) -> Weight { - if Self::get_number_of_available_sol_nonce_accounts() > NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && - (SolanaOpenContractSwapAccounts::::decode_len().is_some_and( - |open_accounts| { - open_accounts >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES - }, - ) || SolanaLastClosedContractSwapAccountsAt::::get().is_some_and(|n| { - block_number.checked_sub(&n).expect("current block number should always be greater than the block number at which last account closure apicall was created.") >= - MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS.into() - })) { - SolanaOpenContractSwapAccounts::::try_mutate(|accounts| { - let accounts_to_close: Vec<_> = if accounts.len() > - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES - { - accounts.drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES).collect() - } else { - sp_std::mem::take(accounts) - }; - match T::CloseSolanaVaultSwapAccounts::new_unsigned(accounts_to_close.clone()) { - Ok(apicall) => { - let (broadcast_id, _) = - T::SolanaBroadcaster::threshold_sign_and_broadcast(apicall); - Self::deposit_event(Event::::SolanaCloseContractSwapAccounts { - broadcast_id, - }); - accounts_to_close.into_iter().for_each(|acct| SolanaClosedContractSwapAccounts::::insert(acct, ())); - SolanaLastClosedContractSwapAccountsAt::::put(block_number); - Ok(Weight::zero()) - }, - Err(_) => Err(Weight::zero()), - } - }) - .unwrap_or_else(|weight| weight) - } else { - Weight::zero() - } - } - } - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -340,8 +272,6 @@ pub mod pallet { StaleUtxosDiscarded { utxos: Vec }, /// Solana durable nonce is updated to a new nonce for the corresponding nonce account. DurableNonceSetForAccount { nonce_account: SolAddress, durable_nonce: SolHash }, - /// apicall to close Solana Contract Swap Accounts has been initiated. - SolanaCloseContractSwapAccounts { broadcast_id: BroadcastId }, } #[pallet::call] @@ -815,39 +745,6 @@ impl Pallet { log::error!("Nonce account {nonce_account} not found in unavailable nonce accounts"); } } - - // pub fn report_sol_contract_swap_accounts( - // new_contract_swaps: Vec<(ContractSwapAccountAndSender, SolanaContractSwapDetails)>, - // confirm_closed_accounts: Vec, - // ) { - // SolanaOpenContractSwapAccounts::::mutate(|accts| { - // accts.extend( - // new_contract_swaps - // .into_iter() - // .map(|(contract_swap_account, swap_details)| { - // T::SolanaContractSwapper::initiate_contract_swap( - // swap_details.from, - // swap_details.to, - // swap_details.deposit_amount, - // swap_details.destination_address, - // swap_details.tx_hash, - // ); - // contract_swap_account - // }) - // .collect::>(), - // ) - // }); - // confirm_closed_accounts - // .into_iter() - // .for_each(SolanaClosedContractSwapAccounts::::remove); - // } - - #[allow(dead_code)] - fn get_all_sol_contract_swap_accounts() -> Vec { - let mut all_accounts = SolanaOpenContractSwapAccounts::::get(); - all_accounts.extend(SolanaClosedContractSwapAccounts::::iter_keys().collect::>()); - all_accounts - } } impl CompatibleCfeVersions for Pallet { diff --git a/state-chain/pallets/cf-environment/src/mock.rs b/state-chain/pallets/cf-environment/src/mock.rs index 9f4e66490e..c49275c00e 100644 --- a/state-chain/pallets/cf-environment/src/mock.rs +++ b/state-chain/pallets/cf-environment/src/mock.rs @@ -4,18 +4,12 @@ use crate::{self as pallet_cf_environment, Decode, Encode, TypeInfo}; use cf_chains::{ btc::{BitcoinCrypto, BitcoinFeeInfo}, dot::{api::CreatePolkadotVault, PolkadotCrypto}, - eth, - sol::{ - api::{ContractSwapAccountAndSender, SolanaTransactionBuildingError}, - SolanaCrypto, - }, - ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, CloseSolanaVaultSwapAccounts, Polkadot, Solana, + eth, ApiCall, Arbitrum, Bitcoin, Chain, ChainCrypto, Polkadot, Solana, }; use cf_primitives::SemVer; use cf_traits::{ impl_mock_chainflip, impl_mock_runtime_safe_mode, impl_pallet_safe_mode, - mocks::{broadcaster::MockBroadcaster, key_provider::MockKeyProvider}, - GetBitcoinFeeInfo, SolanaContractSwap, VaultKeyWitnessedHandler, + mocks::key_provider::MockKeyProvider, GetBitcoinFeeInfo, VaultKeyWitnessedHandler, }; use frame_support::{derive_impl, parameter_types}; use sp_core::{H160, H256}; @@ -164,70 +158,6 @@ impl_mock_runtime_safe_mode!(mock: MockPalletSafeMode); pub type MockBitcoinKeyProvider = MockKeyProvider; -#[derive(Clone, Debug, Default, Eq, PartialEq, Encode, Decode, TypeInfo)] -pub struct MockCloseSolanaVaultSwapAccounts { - contract_swap_accounts_and_senders: Vec, -} - -impl CloseSolanaVaultSwapAccounts for MockCloseSolanaVaultSwapAccounts { - fn new_unsigned( - accounts: Vec, - ) -> Result { - Ok(Self { contract_swap_accounts_and_senders: accounts }) - } -} - -impl ApiCall for MockCloseSolanaVaultSwapAccounts { - fn threshold_signature_payload( - &self, - ) -> <::ChainCrypto as ChainCrypto>::Payload { - unimplemented!() - } - - fn signed( - self, - _threshold_signature: &<::ChainCrypto as ChainCrypto>::ThresholdSignature, - _signer: <::ChainCrypto as ChainCrypto>::AggKey, - ) -> Self { - unimplemented!() - } - - fn chain_encoded(&self) -> Vec { - unimplemented!() - } - - fn is_signed(&self) -> bool { - unimplemented!() - } - - fn transaction_out_id( - &self, - ) -> <::ChainCrypto as ChainCrypto>::TransactionOutId { - unimplemented!() - } - - fn refresh_replay_protection(&mut self) { - unimplemented!() - } - - fn signer(&self) -> Option<::AggKey> { - unimplemented!() - } -} - -pub struct MockSolanaContractSwapper; -impl SolanaContractSwap for MockSolanaContractSwapper { - fn initiate_contract_swap( - _from: cf_primitives::Asset, - _to: cf_primitives::Asset, - _deposit_amount: cf_primitives::AssetAmount, - _destination_address: cf_chains::address::EncodedAddress, - _tx_hash: cf_primitives::TransactionHash, - ) { - unimplemented!() - } -} - impl pallet_cf_environment::Config for Test { type RuntimeEvent = RuntimeEvent; type PolkadotVaultKeyWitnessedHandler = MockPolkadotVaultKeyWitnessedHandler; @@ -235,9 +165,6 @@ impl pallet_cf_environment::Config for Test { type ArbitrumVaultKeyWitnessedHandler = MockArbitrumVaultKeyWitnessedHandler; type SolanaVaultKeyWitnessedHandler = MockSolanaVaultKeyWitnessedHandler; type SolanaNonceWatch = (); - type CloseSolanaVaultSwapAccounts = MockCloseSolanaVaultSwapAccounts; - type SolanaBroadcaster = MockBroadcaster<(MockCloseSolanaVaultSwapAccounts, RuntimeCall)>; - type SolanaContractSwapper = MockSolanaContractSwapper; type BitcoinFeeInfo = MockBitcoinFeeInfo; type BitcoinKeyProvider = MockBitcoinKeyProvider; type RuntimeSafeMode = MockRuntimeSafeMode; diff --git a/state-chain/runtime/src/chainflip.rs b/state-chain/runtime/src/chainflip.rs index 156edf6cde..6226784623 100644 --- a/state-chain/runtime/src/chainflip.rs +++ b/state-chain/runtime/src/chainflip.rs @@ -18,8 +18,8 @@ use crate::{ BitcoinThresholdSigner, BlockNumber, Emissions, Environment, EthereumBroadcaster, EthereumChainTracking, EthereumIngressEgress, Flip, FlipBalance, Hash, PolkadotBroadcaster, PolkadotChainTracking, PolkadotIngressEgress, PolkadotThresholdSigner, Runtime, RuntimeCall, - RuntimeOrigin, SolanaBroadcaster, SolanaChainTrackingProvider, SolanaIngressEgress, - SolanaThresholdSigner, System, Validator, YEAR, + SolanaBroadcaster, SolanaChainTrackingProvider, SolanaIngressEgress, SolanaThresholdSigner, + System, Validator, YEAR, }; use backup_node_rewards::calculate_backup_rewards; use cf_chains::{ @@ -60,15 +60,14 @@ use cf_chains::{ Solana, TransactionBuilder, }; use cf_primitives::{ - chains::assets, AccountRole, Asset, AssetAmount, BasisPoints, Beneficiaries, ChannelId, - DcaParameters, TransactionHash, + chains::assets, AccountRole, Asset, BasisPoints, Beneficiaries, ChannelId, DcaParameters, }; use cf_traits::{ AccountInfo, AccountRoleRegistry, BackupRewardsNotifier, BlockEmissions, BroadcastAnyChainGovKey, Broadcaster, Chainflip, CommKeyBroadcaster, DepositApi, EgressApi, EpochInfo, FetchesTransfersLimitProvider, Heartbeat, IngressEgressFeeApi, Issuance, KeyProvider, OnBroadcastReady, OnDeposit, QualifyNode, RewardsDistribution, RuntimeUpgrade, - ScheduledEgressDetails, SolanaContractSwap, + ScheduledEgressDetails, }; use codec::{Decode, Encode}; @@ -970,23 +969,3 @@ impl FetchesTransfersLimitProvider for EvmLimit { Some(20) } } - -pub struct SolanaContractSwapper; -impl SolanaContractSwap for SolanaContractSwapper { - fn initiate_contract_swap( - from: Asset, - to: Asset, - deposit_amount: AssetAmount, - destination_address: EncodedAddress, - tx_hash: TransactionHash, - ) { - let _ = SolanaIngressEgress::contract_swap_request( - RuntimeOrigin::root(), - from, - to, - deposit_amount, - destination_address, - tx_hash, - ); - } -} diff --git a/state-chain/runtime/src/lib.rs b/state-chain/runtime/src/lib.rs index bf3f8ac505..02a2d5e93d 100644 --- a/state-chain/runtime/src/lib.rs +++ b/state-chain/runtime/src/lib.rs @@ -17,7 +17,7 @@ use crate::{ SolanaChainTrackingProvider, SolanaEgressWitnessingTrigger, SolanaIngress, SolanaNonceTrackingTrigger, }, - Offence, SolanaContractSwapper, + Offence, }, migrations::serialize_solana_broadcast::{NoopUpgrade, SerializeSolanaBroadcastMigration}, monitoring_apis::{ @@ -47,7 +47,7 @@ use cf_chains::{ dot::{self, PolkadotAccountId, PolkadotCrypto}, eth::{self, api::EthereumApi, Address as EthereumAddress, Ethereum}, evm::EvmCrypto, - sol::{api::SolanaApi, SolanaCrypto}, + sol::SolanaCrypto, Arbitrum, Bitcoin, DefaultRetryPolicy, ForeignChain, Polkadot, Solana, TransactionBuilder, }; use cf_primitives::{BroadcastId, EpochIndex, NetworkEnvironment, STABLE_ASSET}; @@ -270,9 +270,6 @@ impl pallet_cf_environment::Config for Runtime { type ArbitrumVaultKeyWitnessedHandler = ArbitrumVault; type SolanaVaultKeyWitnessedHandler = SolanaVault; type SolanaNonceWatch = SolanaNonceTrackingTrigger; - type CloseSolanaVaultSwapAccounts = SolanaApi; - type SolanaBroadcaster = SolanaBroadcaster; - type SolanaContractSwapper = SolanaContractSwapper; type BitcoinFeeInfo = chainflip::BitcoinFeeGetter; type BitcoinKeyProvider = BitcoinThresholdSigner; type RuntimeSafeMode = RuntimeSafeMode; diff --git a/state-chain/traits/src/lib.rs b/state-chain/traits/src/lib.rs index 63bcb900dd..fa82ca329e 100644 --- a/state-chain/traits/src/lib.rs +++ b/state-chain/traits/src/lib.rs @@ -1100,13 +1100,3 @@ impl ElectionEgressWitnesser for DummyEgressSuccessWitnesser pub trait RotationBroadcastsPending { fn rotation_broadcasts_pending() -> bool; } - -pub trait SolanaContractSwap { - fn initiate_contract_swap( - from: Asset, - to: Asset, - deposit_amount: AssetAmount, - destination_address: cf_chains::address::EncodedAddress, - tx_hash: cf_primitives::TransactionHash, - ); -} From 108d839cf450e8268ae6dfc7f65d77cd5bd178d8 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Mon, 28 Oct 2024 18:08:01 +0100 Subject: [PATCH 11/23] fix: consensus --- .../solana_swap_accounts_tracking.rs | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index de265867e9..751cf036dc 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -196,17 +196,40 @@ impl< let active_votes = consensus_votes.active_votes(); let num_active_votes = active_votes.len() as u32; Ok(if num_active_votes >= success_threshold { - let mut counts = BTreeMap::new(); + let mut counts_votes = BTreeMap::new(); + let mut counts_new_accounts = BTreeMap::new(); + let mut counts_confirm_closed_accounts = BTreeMap::new(); + for vote in active_votes { - counts.entry(vote).and_modify(|count| *count += 1).or_insert(1); + counts_votes.entry(vote).and_modify(|count| *count += 1).or_insert(1); + } + + counts_votes.iter().for_each(|(vote, count)| { + vote.new_accounts.iter().for_each(|new_account| { + counts_new_accounts + .entry(new_account) + .and_modify(|c| *c += *count) + .or_insert(*count); + }); + vote.confirm_closed_accounts.iter().for_each(|confirm_closed_account| { + counts_confirm_closed_accounts + .entry(confirm_closed_account) + .and_modify(|c| *c += *count) + .or_insert(*count); + }); + }); + + counts_new_accounts.retain(|_, count| *count >= success_threshold); + let new_accounts = counts_new_accounts.into_keys().cloned().collect::>(); + counts_confirm_closed_accounts.retain(|_, count| *count >= success_threshold); + let confirm_closed_accounts = + counts_confirm_closed_accounts.into_keys().cloned().collect::>(); + + if new_accounts.is_empty() && confirm_closed_accounts.is_empty() { + None + } else { + Some(SolanaVaultSwapsVote { new_accounts, confirm_closed_accounts }) } - counts.iter().find_map(|(vote, count)| { - if *count >= success_threshold { - Some(vote.clone()) - } else { - None - } - }) } else { None }) From 3c9e11f941d900210205f57786b0d96a3842b769 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Mon, 28 Oct 2024 18:14:50 +0100 Subject: [PATCH 12/23] chore: minor --- foreign-chains/solana/sol-prim/src/consts.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/foreign-chains/solana/sol-prim/src/consts.rs b/foreign-chains/solana/sol-prim/src/consts.rs index 8918e53f02..9e229bfa70 100644 --- a/foreign-chains/solana/sol-prim/src/consts.rs +++ b/foreign-chains/solana/sol-prim/src/consts.rs @@ -43,7 +43,6 @@ pub const NONCE_ACCOUNT_LENGTH: u64 = 80u64; pub const SOL_USDC_DECIMAL: u8 = 6u8; -// todo: confirm this pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: usize = 10; -pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; // 1 day -pub const NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES: usize = 4; // revisit this +pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; +pub const NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES: usize = 4; From ca07a5e3bbb87042bcbf7ee2fd1dac051fa452d5 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Mon, 28 Oct 2024 18:18:55 +0100 Subject: [PATCH 13/23] chore: minor rename --- state-chain/cf-integration-tests/src/mock_runtime.rs | 2 +- .../electoral_systems/solana_swap_accounts_tracking.rs | 8 ++++---- state-chain/runtime/src/chainflip/solana_elections.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 8f9f3d7a9e..66394cecf2 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -306,7 +306,7 @@ impl ExtBuilder { (), (), SolanaVaultSwapsElectoralState { - block_number_last_closed_accounts: Default::default(), + accounts_last_closed_at: Default::default(), witnessed_open_accounts: Default::default(), closure_initiated_accounts: Default::default(), }, diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 751cf036dc..eb975ad461 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -36,7 +36,7 @@ pub trait SolanaVaultSwapAccountsHook { #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode)] pub struct SolanaVaultSwapsElectoralState { - pub block_number_last_closed_accounts: BlockNumber, + pub accounts_last_closed_at: BlockNumber, pub witnessed_open_accounts: Vec, pub closure_initiated_accounts: BTreeSet, } @@ -45,7 +45,7 @@ pub struct SolanaVaultSwapsElectoralState { impl BenchmarkValue for SolanaVaultSwapsElectoralState { fn benchmark_value() -> Self { Self { - block_number_last_closed_accounts: 1u32, + accounts_last_closed_at: 1u32, witnessed_open_accounts: vec![BenchmarkValue::benchmark_value()], closure_initiated_accounts: BTreeSet::from([BenchmarkValue::benchmark_value()]), } @@ -152,7 +152,7 @@ impl< (unsynchronised_state.witnessed_open_accounts.len() >= MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || (*current_block_number) - .checked_sub(&unsynchronised_state.block_number_last_closed_accounts) + .checked_sub(&unsynchronised_state.accounts_last_closed_at) .expect( "current block number is always greater than when apicall was last created", ) @@ -170,7 +170,7 @@ impl< }; match Hook::close_accounts(accounts_to_close.clone()) { Ok(()) => { - unsynchronised_state.block_number_last_closed_accounts = *current_block_number; + unsynchronised_state.accounts_last_closed_at = *current_block_number; unsynchronised_state.closure_initiated_accounts.extend(accounts_to_close); electoral_access.set_unsynchronised_state(unsynchronised_state)?; }, diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index f7e043d39b..a18c54faa0 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -76,7 +76,7 @@ pub fn initial_state( (), (), SolanaVaultSwapsElectoralState { - block_number_last_closed_accounts: 0, + accounts_last_closed_at: 0, witnessed_open_accounts: vec![], closure_initiated_accounts: BTreeSet::new(), }, From 8f91452ff95ebd11c64a3b949e886fa4a9e1ad89 Mon Sep 17 00:00:00 2001 From: Albert Llimos <53186777+albert-llimos@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:29:52 +0100 Subject: [PATCH 14/23] Feat: engine logic to witness solana program swaps (#5313) * chore: add get_event_accounts * feat: add get_program_swaps * chore: add get_program_swaps * chore: improvements * chore: also update persa.rs * chore: cleanup --- Cargo.lock | 1 + engine/Cargo.toml | 5 + engine/src/witness/sol.rs | 1 + .../witness/sol/program_swaps_witnessing.rs | 451 ++++++++++++++++++ state-chain/node/src/chain_spec/berghain.rs | 2 +- state-chain/node/src/chain_spec/devnet.rs | 2 +- .../node/src/chain_spec/perseverance.rs | 2 +- state-chain/node/src/chain_spec/sisyphos.rs | 2 +- 8 files changed, 462 insertions(+), 4 deletions(-) create mode 100644 engine/src/witness/sol/program_swaps_witnessing.rs diff --git a/Cargo.lock b/Cargo.lock index ca2caab4c4..0c3c12d374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1827,6 +1827,7 @@ dependencies = [ "base64 0.22.1", "bincode 1.3.3", "bitcoin", + "borsh", "bs58 0.5.1", "cf-amm", "cf-chains", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 2a5cc0886b..c260ef65c6 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -20,6 +20,11 @@ async-channel = "1.7.1" async-trait = "0.1.49" bincode = "1.3.3" bitcoin = { version = "0.30.0", features = ["serde"] } +borsh = { version = "1.2.1", default_features = false, features = [ + "derive", + "unstable__schema", + "hashbrown", +] } chrono = { version = "0.4.21", default_features = false, features = ["clock"] } clap = { version = "3.2.16", features = ["derive", "env"] } config = "0.13.1" diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index b8eed7354a..9dcd2d17a6 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -1,6 +1,7 @@ mod egress_witnessing; mod fee_tracking; mod nonce_witnessing; +mod program_swaps_witnessing; mod sol_deposits; use crate::{ diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs new file mode 100644 index 0000000000..ceb39ad1b1 --- /dev/null +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -0,0 +1,451 @@ +use std::collections::HashSet; + +use crate::sol::{ + commitment_config::CommitmentConfig, + retry_rpc::{SolRetryRpcApi, SolRetryRpcClient}, + rpc_client_api::{RpcAccountInfoConfig, UiAccount, UiAccountData, UiAccountEncoding}, +}; +use anyhow::ensure; +use anyhow::{anyhow /* ensure */}; +use base64::Engine; +use borsh::{BorshDeserialize, BorshSerialize}; +use cf_chains::sol::SolAddress; +use futures::{stream, StreamExt, TryStreamExt}; +use itertools::Itertools; +use tracing::warn; + +const MAXIMUM_CONCURRENT_RPCS: usize = 16; +const SWAP_ENDPOINT_DATA_ACCOUNT_DISCRIMINATOR: [u8; 8] = [79, 152, 191, 225, 128, 108, 11, 139]; +const SWAP_EVENT_ACCOUNT_DISCRIMINATOR: [u8; 8] = [150, 251, 114, 94, 200, 113, 248, 70]; +// Querying less than 100 (rpc call max) as those event accounts can be quite big. +// Max length ~ 1300 bytes per account. We set it to 10 as an arbitrary number to +// avoid large queries. +const MAX_MULTIPLE_EVENT_ACCOUNTS_QUERY: usize = 10; + +#[derive(BorshDeserialize, BorshSerialize, Debug)] +struct SwapEndpointDataAccount { + discriminator: [u8; 8], + historical_number_event_accounts: u128, + open_event_accounts: Vec<[u8; sol_prim::consts::SOLANA_ADDRESS_LEN]>, +} + +#[derive(BorshDeserialize, BorshSerialize, Debug, Default)] +pub struct SwapEvent { + discriminator: [u8; 8], + sender: [u8; sol_prim::consts::SOLANA_ADDRESS_LEN], + dst_chain: u32, + dst_address: Vec, + dst_token: u32, + amount: u64, + src_token: Option<[u8; sol_prim::consts::SOLANA_ADDRESS_LEN]>, + ccm_parameters: Option, + cf_parameters: Vec, +} + +#[derive(BorshDeserialize, BorshSerialize, Debug, Default)] +pub struct CcmParams { + message: Vec, + gas_amount: u64, +} + +// 1. Query the on-chain list of opened accounts from SwapEndpointDataAccount. +// 2. Check the returned accounts against the SC opened_accounts. The SC is the source of truth for +// the opened channels we can rely on that to not query the same accounts multiple times. +// 3. If they are already seen in the SC we do nothing with them and skip the query. +// 4. If an account is in the SC but not see in the engine we report it as closed. +// 5. If they are not seen in the SC we query the account data. Then we parse the account data and +// ensure it's a valid a program swap. The new program swap needs to be reported to the SC. + +pub async fn get_program_swaps( + sol_rpc: &SolRetryRpcClient, + swap_endpoint_data_account_address: SolAddress, + sc_opened_accounts: Vec, + _token_pubkey: SolAddress, +) -> Result, anyhow::Error> { + let (new_program_swap_accounts, _closed_accounts, slot) = get_changed_program_swap_accounts( + sol_rpc, + sc_opened_accounts, + swap_endpoint_data_account_address, + ) + .await?; + + stream::iter(new_program_swap_accounts) + .chunks(MAX_MULTIPLE_EVENT_ACCOUNTS_QUERY) + .map(|new_program_swap_accounts_chunk| { + get_program_swap_event_accounts_data(sol_rpc, new_program_swap_accounts_chunk, slot) + }) + .buffered(MAXIMUM_CONCURRENT_RPCS) + .map_ok(|program_swap_account_data_chunk| { + stream::iter(program_swap_account_data_chunk.into_iter().filter_map( + |program_swap_account_data| match program_swap_account_data { + Some(data) => Some(Ok(data)), + // It could happen that some account is closed between the queries. This should + // not happen because: + // 1. Accounts in `new_program_swap_accounts` can only be accounts that have + // newly been opened and they won't be closed until consensus is reached. + // 2. If due to rpc load management the get event accounts rpc is queried at a + // slot < get swap endpoint data rpc slot, the min_context_slot will prevent + // it from being executed before that. + // This could only happen if an engine is behind and were to see the account + // opened and closed between queries. That's not reallistic as it takes minutes + // for an account to be closed and even if it were to happen it's not + // problematic as we'd have reached consensus and the engine would just filter + // it out. + None => { + warn!("Event account not found for solana event account"); + None + }, + }, + )) + }) + .try_flatten() + .try_collect() + .await + + // TODO: Submit closed_accounts and new opened accounts from SwapEvents. The only additional + // step required is checking the SwapEvent's src_token. If empty, submit it as native. Otherwise + // it should match token_pubkey. A token not matching the token_pubkey should never happen. + // The submission might be done in a layer above (sol.rs). + + // TODO: When submitting data we could technically submit the slot when the SwapEvent was + // queried for the new opened accounts. However, it's just easier to submit the slot when the + // SwapEndpointDataAccount was queried for both closed accounts and new opened accounts. +} + +async fn get_changed_program_swap_accounts( + sol_rpc: &SolRetryRpcClient, + sc_opened_accounts: Vec, + swap_endpoint_data_account_address: SolAddress, +) -> Result<(Vec, Vec, u64), anyhow::Error> { + let (_historical_number_event_accounts, open_event_accounts, slot) = + get_swap_endpoint_data(sol_rpc, swap_endpoint_data_account_address) + .await + .expect("Failed to get the event accounts"); + + let sc_opened_accounts_hashset: HashSet<_> = sc_opened_accounts.iter().collect(); + let mut new_program_swap_accounts = Vec::new(); + let mut closed_accounts = Vec::new(); + + for account in &open_event_accounts { + if !sc_opened_accounts_hashset.contains(account) { + new_program_swap_accounts.push(*account); + } + } + + let open_event_accounts_hashset: HashSet<_> = open_event_accounts.iter().collect(); + for account in sc_opened_accounts { + if !open_event_accounts_hashset.contains(&account) { + closed_accounts.push(account); + } + } + + Ok((new_program_swap_accounts, closed_accounts, slot)) +} + +// Query the list of opened accounts from SwapEndpointDataAccount. The Swap Endpoint program ensures +// that the list is updated atomically whenever a swap event account is opened or closed. +async fn get_swap_endpoint_data( + sol_rpc: &SolRetryRpcClient, + swap_endpoint_data_account_address: SolAddress, +) -> Result<(u128, Vec, u64), anyhow::Error> { + let accounts_info_response = sol_rpc + .get_multiple_accounts( + &[swap_endpoint_data_account_address], + RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: Some(CommitmentConfig::finalized()), + min_context_slot: None, + }, + ) + .await; + + let slot = accounts_info_response.context.slot; + let accounts_info = accounts_info_response + .value + .into_iter() + .exactly_one() + .expect("We queried for exactly one account."); + + match accounts_info { + Some(UiAccount { data: UiAccountData::Binary(base64_string, encoding), .. }) => { + if encoding != UiAccountEncoding::Base64 { + return Err(anyhow!("Data account encoding is not base64")); + } + let bytes = base64::engine::general_purpose::STANDARD + .decode(base64_string) + .expect("Failed to decode base64 string"); + + // 8 Discriminator + 16 Historical Number Event Accounts + 4 bytes vector length + data + if bytes.len() < 28 { + return Err(anyhow!("Expected account to have at least 28 bytes")); + } + + let deserialized_data: SwapEndpointDataAccount = + SwapEndpointDataAccount::try_from_slice(&bytes) + .map_err(|e| anyhow!("Failed to deserialize data: {:?}", e))?; + + ensure!( + deserialized_data.discriminator == SWAP_ENDPOINT_DATA_ACCOUNT_DISCRIMINATOR, + "Discriminator does not match. Found: {:?}", + deserialized_data.discriminator + ); + + Ok(( + deserialized_data.historical_number_event_accounts, + deserialized_data.open_event_accounts.into_iter().map(SolAddress).collect(), + slot, + )) + }, + Some(_) => + Err(anyhow!("Expected UiAccountData::Binary(String, UiAccountEncoding::Base64)")), + None => Err(anyhow!( + "Expected swap_endpoint_data_account_address to be found: {:?}", + swap_endpoint_data_account_address + )), + } +} + +async fn get_program_swap_event_accounts_data( + sol_rpc: &SolRetryRpcClient, + program_swap_event_accounts: Vec, + min_context_slot: u64, +) -> Result>, anyhow::Error> { + let accounts_info_response = sol_rpc + .get_multiple_accounts( + program_swap_event_accounts.as_slice(), + RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: Some(CommitmentConfig::finalized()), + min_context_slot: Some(min_context_slot), + }, + ) + .await; + + let _slot = accounts_info_response.context.slot; + let accounts_info = accounts_info_response.value; + + ensure!(accounts_info.len() == program_swap_event_accounts.len()); + + accounts_info + .into_iter() + .map(|accounts_info| match accounts_info { + Some(UiAccount { data: UiAccountData::Binary(base64_string, encoding), .. }) => { + if encoding != UiAccountEncoding::Base64 { + return Err(anyhow!("Data account encoding is not base64")); + } + let bytes = base64::engine::general_purpose::STANDARD + .decode(base64_string) + .expect("Failed to decode base64 string"); + + if bytes.len() < 8 { + return Err(anyhow!("Expected account to have at least 28 bytes")); + } + + let deserialized_data: SwapEvent = SwapEvent::try_from_slice(&bytes) + .map_err(|e| anyhow!("Failed to deserialize data: {:?}", e))?; + + ensure!( + deserialized_data.discriminator == SWAP_EVENT_ACCOUNT_DISCRIMINATOR, + "Discriminator does not match. Found: {:?}", + deserialized_data.discriminator + ); + + Ok(Some(deserialized_data)) + }, + Some(_) => + Err(anyhow!("Expected UiAccountData::Binary(String, UiAccountEncoding::Base64)")), + None => Ok(None), + }) + .collect() +} + +#[cfg(test)] +mod tests { + use crate::{ + settings::{HttpEndpoint, NodeContainer}, + sol::retry_rpc::SolRetryRpcClient, + }; + + use cf_chains::{Chain, Solana}; + use futures_util::FutureExt; + use std::str::FromStr; + use utilities::task_scope; + + use super::*; + + #[tokio::test] + #[ignore] + async fn test_get_swap_endpoint_data() { + task_scope::task_scope(|scope| { + async { + let client = SolRetryRpcClient::new( + scope, + NodeContainer { + primary: HttpEndpoint { + http_endpoint: "https://api.devnet.solana.com".into(), + }, + backup: None, + }, + None, + Solana::WITNESS_PERIOD, + ) + .await + .unwrap(); + + let (historical_number_event_accounts, open_event_accounts, _) = + get_swap_endpoint_data( + &client, + // Swap Endpoint Data Account Address with no opened accounts + SolAddress::from_str("BckDu65u2ofAfaSDDEPg2qJTufKB4PvGxwcYhJ2wkBTC") + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(historical_number_event_accounts, 0_u128); + assert_eq!(open_event_accounts.len(), 0); + + let (historical_number_event_accounts, open_event_accounts, _) = + get_swap_endpoint_data( + &client, + // Swap Endpoint Data Account Address with two opened accounts + SolAddress::from_str("72HKrbbesW9FGuBoebns77uvY9fF9MEsw4HTMEeV53W9") + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(historical_number_event_accounts, 2_u128); + assert_eq!( + open_event_accounts, + vec![ + SolAddress::from_str("HhxGAt8THMtsW97Zuo5ZrhKgqsdD5EBgCx9vZ4n62xpf") + .unwrap(), + SolAddress::from_str("E81G7Q1BjierakQCfL9B5Tm485eiaRPb22bcKD2vtRfU") + .unwrap() + ] + ); + + Ok(()) + } + .boxed() + }) + .await + .unwrap(); + } + + #[tokio::test] + #[ignore] + async fn test_get_changed_program_swap_accounts() { + task_scope::task_scope(|scope| { + async { + let client = SolRetryRpcClient::new( + scope, + NodeContainer { + primary: HttpEndpoint { + http_endpoint: "https://api.devnet.solana.com".into(), + }, + backup: None, + }, + None, + Solana::WITNESS_PERIOD, + ) + .await + .unwrap(); + + let (new_program_swap_accounts, closed_accounts, _) = + get_changed_program_swap_accounts( + &client, + vec![SolAddress::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") + .unwrap()], + // Swap Endpoint Data Account Address with no opened accounts + SolAddress::from_str("BckDu65u2ofAfaSDDEPg2qJTufKB4PvGxwcYhJ2wkBTC") + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(new_program_swap_accounts, vec![]); + assert_eq!( + closed_accounts, + vec![SolAddress::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") + .unwrap()] + ); + + let (new_program_swap_accounts, closed_accounts, _) = + get_changed_program_swap_accounts( + &client, + vec![SolAddress::from_str("HhxGAt8THMtsW97Zuo5ZrhKgqsdD5EBgCx9vZ4n62xpf") + .unwrap()], + // Swap Endpoint Data Account Address with two opened accounts + SolAddress::from_str("72HKrbbesW9FGuBoebns77uvY9fF9MEsw4HTMEeV53W9") + .unwrap(), + ) + .await + .unwrap(); + + println!("new_program_swap_accounts: {:?}", new_program_swap_accounts); + println!("closed_accounts: {:?}", closed_accounts); + + assert_eq!( + new_program_swap_accounts, + vec![SolAddress::from_str("E81G7Q1BjierakQCfL9B5Tm485eiaRPb22bcKD2vtRfU") + .unwrap()] + ); + assert_eq!(closed_accounts, vec![]); + + Ok(()) + } + .boxed() + }) + .await + .unwrap(); + } + + #[tokio::test] + #[ignore] + async fn test_get_program_swap_event_accounts_data() { + task_scope::task_scope(|scope| { + async { + let client = SolRetryRpcClient::new( + scope, + NodeContainer { + primary: HttpEndpoint { http_endpoint: "http://127.0.0.1:8899".into() }, + backup: None, + }, + None, + Solana::WITNESS_PERIOD, + ) + .await + .unwrap(); + + let program_swap_event_accounts_data = get_program_swap_event_accounts_data( + &client, + vec![ + SolAddress::from_str("GNrA2Ztxv1tJF3G4NVPEQtbRb9uT8rXcEY6ddPfzpnnT") + .unwrap(), + SolAddress::from_str("8yeBhX5BB4L9MfDddhwzktdmzMeNUEcvgZGPWLD3HDDY") + .unwrap(), + SolAddress::from_str("Dd1k91cWt84qJoQr3FT7EXQpSaMtZtwPwdho7RbMWtEV") + .unwrap(), + ], + 123, + ) + .await + .unwrap(); + + println!( + "program_swap_event_accounts_data: {:?}", + program_swap_event_accounts_data + ); + + Ok(()) + } + .boxed() + }) + .await + .unwrap(); + } +} diff --git a/state-chain/node/src/chain_spec/berghain.rs b/state-chain/node/src/chain_spec/berghain.rs index dbde497410..a155a3def2 100644 --- a/state-chain/node/src/chain_spec/berghain.rs +++ b/state-chain/node/src/chain_spec/berghain.rs @@ -159,4 +159,4 @@ pub const AUCTION_PARAMETERS: SetSizeParameters = pub const BITCOIN_SAFETY_MARGIN: u64 = 2; pub const ETHEREUM_SAFETY_MARGIN: u64 = 6; pub const ARBITRUM_SAFETY_MARGIN: u64 = 1; -pub const SOLANA_SAFETY_MARGIN: u64 = 1; //TODO: put correct value +pub const SOLANA_SAFETY_MARGIN: u64 = 1; // Unused - we use "finalized" instead diff --git a/state-chain/node/src/chain_spec/devnet.rs b/state-chain/node/src/chain_spec/devnet.rs index 78caab48db..ed57cf86d8 100644 --- a/state-chain/node/src/chain_spec/devnet.rs +++ b/state-chain/node/src/chain_spec/devnet.rs @@ -21,4 +21,4 @@ pub const AUCTION_PARAMETERS: SetSizeParameters = SetSizeParameters { pub const BITCOIN_SAFETY_MARGIN: u64 = 2; pub const ETHEREUM_SAFETY_MARGIN: u64 = 2; pub const ARBITRUM_SAFETY_MARGIN: u64 = 1; -pub const SOLANA_SAFETY_MARGIN: u64 = 1; //TODO: put correct value +pub const SOLANA_SAFETY_MARGIN: u64 = 1; // Unused - we use "finalized" instead diff --git a/state-chain/node/src/chain_spec/perseverance.rs b/state-chain/node/src/chain_spec/perseverance.rs index 04456f7c4c..06fb97bffa 100644 --- a/state-chain/node/src/chain_spec/perseverance.rs +++ b/state-chain/node/src/chain_spec/perseverance.rs @@ -158,4 +158,4 @@ pub fn extra_accounts() -> Vec<(AccountId, AccountRole, FlipBalance, Option Vec<(AccountId, AccountRole, FlipBalance, Option Date: Tue, 29 Oct 2024 14:37:33 +0100 Subject: [PATCH 15/23] feat: return swap account along with swap details --- .../src/witness/sol/program_swaps_witnessing.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs index ceb39ad1b1..5da665d48d 100644 --- a/engine/src/witness/sol/program_swaps_witnessing.rs +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -61,7 +61,7 @@ pub async fn get_program_swaps( swap_endpoint_data_account_address: SolAddress, sc_opened_accounts: Vec, _token_pubkey: SolAddress, -) -> Result, anyhow::Error> { +) -> Result, anyhow::Error> { let (new_program_swap_accounts, _closed_accounts, slot) = get_changed_program_swap_accounts( sol_rpc, sc_opened_accounts, @@ -77,8 +77,8 @@ pub async fn get_program_swaps( .buffered(MAXIMUM_CONCURRENT_RPCS) .map_ok(|program_swap_account_data_chunk| { stream::iter(program_swap_account_data_chunk.into_iter().filter_map( - |program_swap_account_data| match program_swap_account_data { - Some(data) => Some(Ok(data)), + |(account, program_swap_account_data)| match program_swap_account_data { + Some(data) => Some(Ok((account, data))), // It could happen that some account is closed between the queries. This should // not happen because: // 1. Accounts in `new_program_swap_accounts` can only be accounts that have @@ -210,7 +210,7 @@ async fn get_program_swap_event_accounts_data( sol_rpc: &SolRetryRpcClient, program_swap_event_accounts: Vec, min_context_slot: u64, -) -> Result>, anyhow::Error> { +) -> Result)>, anyhow::Error> { let accounts_info_response = sol_rpc .get_multiple_accounts( program_swap_event_accounts.as_slice(), @@ -228,9 +228,10 @@ async fn get_program_swap_event_accounts_data( ensure!(accounts_info.len() == program_swap_event_accounts.len()); - accounts_info + program_swap_event_accounts .into_iter() - .map(|accounts_info| match accounts_info { + .zip(accounts_info.into_iter()) + .map(|(account, accounts_info)| match accounts_info { Some(UiAccount { data: UiAccountData::Binary(base64_string, encoding), .. }) => { if encoding != UiAccountEncoding::Base64 { return Err(anyhow!("Data account encoding is not base64")); @@ -252,11 +253,11 @@ async fn get_program_swap_event_accounts_data( deserialized_data.discriminator ); - Ok(Some(deserialized_data)) + Ok((account, Some(deserialized_data))) }, Some(_) => Err(anyhow!("Expected UiAccountData::Binary(String, UiAccountEncoding::Base64)")), - None => Ok(None), + None => Ok((account, None)), }) .collect() } From 222a79b1c8ee8c37533ecfdb566884e3a94a0292 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Tue, 29 Oct 2024 15:25:38 +0100 Subject: [PATCH 16/23] refactor: use election properties --- .../cf-integration-tests/src/mock_runtime.rs | 10 +- .../solana_swap_accounts_tracking.rs | 121 +++++++++--------- .../runtime/src/chainflip/solana_elections.rs | 12 +- 3 files changed, 67 insertions(+), 76 deletions(-) diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 66394cecf2..05551abb75 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -10,9 +10,7 @@ use chainflip_node::{ chain_spec::testnet::{EXPIRY_SPAN_IN_SECONDS, REDEMPTION_TTL_SECS}, test_account_from_seed, }; -use pallet_cf_elections::{ - electoral_systems::solana_swap_accounts_tracking::SolanaVaultSwapsElectoralState, InitialState, -}; +use pallet_cf_elections::InitialState; use pallet_cf_validator::SetSizeParameters; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -305,11 +303,7 @@ impl ExtBuilder { (), (), (), - SolanaVaultSwapsElectoralState { - accounts_last_closed_at: Default::default(), - witnessed_open_accounts: Default::default(), - closure_initiated_accounts: Default::default(), - }, + Default::default(), ), unsynchronised_settings: ( (), diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index eb975ad461..1bffe81fc8 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -5,6 +5,8 @@ use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; #[cfg(feature = "runtime-benchmarks")] use cf_chains::benchmarking_value::BenchmarkValue; +#[cfg(feature = "runtime-benchmarks")] +use cf_chains::sol::api::ContractSwapAccountAndSender; use crate::{ electoral_system::{ @@ -15,7 +17,7 @@ use crate::{ CorruptStorageError, ElectionIdentifier, }; use cf_chains::sol::{ - api::ContractSwapAccountAndSender, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }; @@ -34,18 +36,18 @@ pub trait SolanaVaultSwapAccountsHook { fn get_number_of_available_sol_nonce_accounts() -> usize; } +pub type SolanaVaultSwapAccountsLastClosedAt = BlockNumber; + #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode)] -pub struct SolanaVaultSwapsElectoralState { - pub accounts_last_closed_at: BlockNumber, +pub struct SolanaVaultSwapsKnownAccounts { pub witnessed_open_accounts: Vec, pub closure_initiated_accounts: BTreeSet, } #[cfg(feature = "runtime-benchmarks")] -impl BenchmarkValue for SolanaVaultSwapsElectoralState { +impl BenchmarkValue for SolanaVaultSwapsKnownAccounts { fn benchmark_value() -> Self { Self { - accounts_last_closed_at: 1u32, witnessed_open_accounts: vec![BenchmarkValue::benchmark_value()], closure_initiated_accounts: BTreeSet::from([BenchmarkValue::benchmark_value()]), } @@ -91,14 +93,14 @@ impl< for SolanaVaultSwapAccounts { type ValidatorId = ValidatorId; - type ElectoralUnsynchronisedState = SolanaVaultSwapsElectoralState; + type ElectoralUnsynchronisedState = SolanaVaultSwapAccountsLastClosedAt; type ElectoralUnsynchronisedStateMapKey = (); type ElectoralUnsynchronisedStateMapValue = (); type ElectoralUnsynchronisedSettings = (); type ElectoralSettings = Settings; type ElectionIdentifierExtra = (); - type ElectionProperties = (); + type ElectionProperties = SolanaVaultSwapsKnownAccounts; type ElectionState = (); type Vote = vote_storage::bitmap::Bitmap>; type Consensus = SolanaVaultSwapsVote; @@ -125,63 +127,64 @@ impl< { let mut election_access = electoral_access.election_mut(election_identifier)?; if let Some(consensus) = election_access.check_consensus()?.has_consensus() { + let mut known_accounts = election_access.properties()?; election_access.delete(); - electoral_access.new_election((), (), ())?; - electoral_access.mutate_unsynchronised_state(|_, unsynchronised_state| { - unsynchronised_state.witnessed_open_accounts.extend( - consensus.new_accounts.iter().map(|(account, swap_details)| { - Hook::initiate_vault_swap((*swap_details).clone()); - (*account).clone() - }), - ); - - consensus.confirm_closed_accounts.into_iter().for_each(|acc| { - unsynchronised_state.closure_initiated_accounts.remove(&acc); - }); - - Ok(()) - })?; + known_accounts.witnessed_open_accounts.extend(consensus.new_accounts.iter().map( + |(account, swap_details)| { + Hook::initiate_vault_swap((*swap_details).clone()); + (*account).clone() + }, + )); + consensus.confirm_closed_accounts.into_iter().for_each(|acc| { + known_accounts.closure_initiated_accounts.remove(&acc); + }); + + if Hook::get_number_of_available_sol_nonce_accounts() > + NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && + (known_accounts.witnessed_open_accounts.len() >= + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || + (*current_block_number) + .checked_sub(&electoral_access.unsynchronised_state()?) + .expect("current block number is always greater than when apicall was last created") + .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) + { + let accounts_to_close: Vec<_> = if known_accounts.witnessed_open_accounts.len() > + MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES + { + known_accounts + .witnessed_open_accounts + .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) + .collect() + } else { + sp_std::mem::take(&mut known_accounts.witnessed_open_accounts) + }; + match Hook::close_accounts(accounts_to_close.clone()) { + Ok(()) => { + known_accounts.closure_initiated_accounts.extend(accounts_to_close); + electoral_access.set_unsynchronised_state(*current_block_number)?; + }, + Err(e) => { + log::error!( + "failed to build Solana CloseSolanaVaultSwapAccounts apicall: {:?}", + e + ); + known_accounts.witnessed_open_accounts.extend(accounts_to_close); + }, + } + } + electoral_access.new_election((), known_accounts, ())?; } } else { - electoral_access.new_election((), (), ())?; - } - - let mut unsynchronised_state = electoral_access.unsynchronised_state()?; - if Hook::get_number_of_available_sol_nonce_accounts() > - NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && - (unsynchronised_state.witnessed_open_accounts.len() >= - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || - (*current_block_number) - .checked_sub(&unsynchronised_state.accounts_last_closed_at) - .expect( - "current block number is always greater than when apicall was last created", - ) - .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) - { - let accounts_to_close: Vec<_> = if unsynchronised_state.witnessed_open_accounts.len() > - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES - { - unsynchronised_state - .witnessed_open_accounts - .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) - .collect() - } else { - sp_std::mem::take(&mut unsynchronised_state.witnessed_open_accounts) - }; - match Hook::close_accounts(accounts_to_close.clone()) { - Ok(()) => { - unsynchronised_state.accounts_last_closed_at = *current_block_number; - unsynchronised_state.closure_initiated_accounts.extend(accounts_to_close); - electoral_access.set_unsynchronised_state(unsynchronised_state)?; - }, - Err(e) => { - log::error!( - "failed to build Solana CloseSolanaVaultSwapAccounts apicall: {:?}", - e - ); + electoral_access.new_election( + (), + SolanaVaultSwapsKnownAccounts { + witnessed_open_accounts: Vec::new(), + closure_initiated_accounts: BTreeSet::new(), }, - } + (), + )?; } + Ok(()) } diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index a18c54faa0..90144ec95b 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -29,16 +29,14 @@ use pallet_cf_elections::{ composite::{tuple_6_impls::Hooks, Composite, Translator}, egress_success::OnEgressSuccess, monotonic_median::MedianChangeHook, - solana_swap_accounts_tracking::{ - SolanaVaultSwapAccountsHook, SolanaVaultSwapsElectoralState, - }, + solana_swap_accounts_tracking::SolanaVaultSwapAccountsHook, }, CorruptStorageError, ElectionIdentifier, InitialState, InitialStateOf, }; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{DispatchResult, FixedPointNumber, FixedU128}; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; +use sp_std::vec::Vec; #[cfg(feature = "runtime-benchmarks")] use cf_chains::benchmarking_value::BenchmarkValue; @@ -75,11 +73,7 @@ pub fn initial_state( (), (), (), - SolanaVaultSwapsElectoralState { - accounts_last_closed_at: 0, - witnessed_open_accounts: vec![], - closure_initiated_accounts: BTreeSet::new(), - }, + 0, ), unsynchronised_settings: ( (), From 9c70be03786762e6e944ac640a31fb598e165988 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Wed, 30 Oct 2024 16:16:49 +0100 Subject: [PATCH 17/23] feat: engine --- engine/src/witness/sol.rs | 32 +++++++-- .../witness/sol/program_swaps_witnessing.rs | 67 ++++++++++++++----- .../cf-integration-tests/src/mock_runtime.rs | 10 ++- state-chain/chains/src/sol/sol_tx_core.rs | 2 + state-chain/node/src/chain_spec.rs | 2 + .../runtime/src/chainflip/solana_elections.rs | 33 +++++++-- 6 files changed, 114 insertions(+), 32 deletions(-) diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index 9dcd2d17a6..539aec40fa 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -16,8 +16,13 @@ use crate::{ }, }; use anyhow::Result; +use cf_chains::sol::api::ContractSwapAccountAndSender; use futures::FutureExt; -use pallet_cf_elections::{electoral_system::ElectoralSystem, vote_storage::VoteStorage}; +use pallet_cf_elections::{ + electoral_system::ElectoralSystem, + electoral_systems::solana_swap_accounts_tracking::SolanaVaultSwapsVote, + vote_storage::VoteStorage, +}; use state_chain_runtime::{ chainflip::solana_elections::{ SolanaBlockHeightTracking, SolanaEgressWitnessing, SolanaElectoralSystem, @@ -27,7 +32,7 @@ use state_chain_runtime::{ SolanaInstance, }; -use std::sync::Arc; +use std::{collections::BTreeSet, sync::Arc}; use utilities::{task_scope, task_scope::Scope}; #[derive(Clone)] @@ -152,13 +157,30 @@ struct SolanaVaultSwapsVoter { impl VoterApi for SolanaVaultSwapsVoter { async fn vote( &self, - _settings: ::ElectoralSettings, - _properties: ::ElectionProperties, + settings: ::ElectoralSettings, + properties: ::ElectionProperties, ) -> Result< <::Vote as VoteStorage>::Vote, anyhow::Error, > { - todo!() + program_swaps_witnessing::get_program_swaps( + &self.client, + settings.swap_endpoint_data_account_address, + properties + .witnessed_open_accounts + .into_iter() + .map(|ContractSwapAccountAndSender { contract_swap_account, .. }| { + contract_swap_account + }) + .collect(), + properties.closure_initiated_accounts, + settings.usdc_token_mint_pubkey, + ) + .await + .map(|(new_accounts, confirm_closed_accounts)| SolanaVaultSwapsVote { + new_accounts: new_accounts.into_iter().collect::>(), + confirm_closed_accounts: confirm_closed_accounts.into_iter().collect::>(), + }) } } diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs index 5da665d48d..db1317cf22 100644 --- a/engine/src/witness/sol/program_swaps_witnessing.rs +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; use crate::sol::{ commitment_config::CommitmentConfig, @@ -9,9 +9,11 @@ use anyhow::ensure; use anyhow::{anyhow /* ensure */}; use base64::Engine; use borsh::{BorshDeserialize, BorshSerialize}; -use cf_chains::sol::SolAddress; +use cf_chains::sol::{api::ContractSwapAccountAndSender, SolAddress}; +use cf_primitives::Asset; use futures::{stream, StreamExt, TryStreamExt}; use itertools::Itertools; +use state_chain_runtime::chainflip::solana_elections::SolanaVaultSwapDetails; use tracing::warn; const MAXIMUM_CONCURRENT_RPCS: usize = 16; @@ -59,17 +61,25 @@ pub struct CcmParams { pub async fn get_program_swaps( sol_rpc: &SolRetryRpcClient, swap_endpoint_data_account_address: SolAddress, - sc_opened_accounts: Vec, - _token_pubkey: SolAddress, -) -> Result, anyhow::Error> { - let (new_program_swap_accounts, _closed_accounts, slot) = get_changed_program_swap_accounts( + sc_open_accounts: Vec, + sc_closure_initiated_accounts: BTreeSet, + usdc_token_mint_pubkey: SolAddress, +) -> Result< + ( + Vec<(ContractSwapAccountAndSender, SolanaVaultSwapDetails)>, + Vec, + ), + anyhow::Error, +> { + let (new_program_swap_accounts, closed_accounts, slot) = get_changed_program_swap_accounts( sol_rpc, - sc_opened_accounts, + sc_open_accounts, + sc_closure_initiated_accounts, swap_endpoint_data_account_address, ) .await?; - stream::iter(new_program_swap_accounts) + let new_swaps = stream::iter(new_program_swap_accounts) .chunks(MAX_MULTIPLE_EVENT_ACCOUNTS_QUERY) .map(|new_program_swap_accounts_chunk| { get_program_swap_event_accounts_data(sol_rpc, new_program_swap_accounts_chunk, slot) @@ -78,7 +88,19 @@ pub async fn get_program_swaps( .map_ok(|program_swap_account_data_chunk| { stream::iter(program_swap_account_data_chunk.into_iter().filter_map( |(account, program_swap_account_data)| match program_swap_account_data { - Some(data) => Some(Ok((account, data))), + Some(data) + if (data.src_token.is_none() || + data.src_token.is_some_and(|addr| addr == usdc_token_mint_pubkey.0)) => + Some(Ok((ContractSwapAccountAndSender { + contract_swap_account: account, + swap_sender: data.sender.into() + }, SolanaVaultSwapDetails { + from: if data.src_token.is_none() {Asset::Sol} else {Asset::SolUsdc}, + deposit_amount: data.amount.into(), + to: todo!(), + destination_address: todo!(), + tx_hash: todo!() + }))), // It could happen that some account is closed between the queries. This should // not happen because: // 1. Accounts in `new_program_swap_accounts` can only be accounts that have @@ -95,17 +117,18 @@ pub async fn get_program_swaps( warn!("Event account not found for solana event account"); None }, + _ => { + warn!("Unsupported input token for the witnessed solana vault swap, omitting the swap and the swap account."); + None + }, }, )) }) .try_flatten() .try_collect() - .await + .await; - // TODO: Submit closed_accounts and new opened accounts from SwapEvents. The only additional - // step required is checking the SwapEvent's src_token. If empty, submit it as native. Otherwise - // it should match token_pubkey. A token not matching the token_pubkey should never happen. - // The submission might be done in a layer above (sol.rs). + new_swaps.map(|swaps| (swaps, closed_accounts)) // TODO: When submitting data we could technically submit the slot when the SwapEvent was // queried for the new opened accounts. However, it's just easier to submit the slot when the @@ -115,26 +138,34 @@ pub async fn get_program_swaps( async fn get_changed_program_swap_accounts( sol_rpc: &SolRetryRpcClient, sc_opened_accounts: Vec, + sc_closure_initiated_accounts: BTreeSet, swap_endpoint_data_account_address: SolAddress, -) -> Result<(Vec, Vec, u64), anyhow::Error> { +) -> Result<(Vec, Vec, u64), anyhow::Error> { let (_historical_number_event_accounts, open_event_accounts, slot) = get_swap_endpoint_data(sol_rpc, swap_endpoint_data_account_address) .await .expect("Failed to get the event accounts"); let sc_opened_accounts_hashset: HashSet<_> = sc_opened_accounts.iter().collect(); + let sc_closure_initiated_accounts_hashset = sc_closure_initiated_accounts + .iter() + .map(|ContractSwapAccountAndSender { contract_swap_account, .. }| contract_swap_account) + .collect::>(); + let mut new_program_swap_accounts = Vec::new(); let mut closed_accounts = Vec::new(); for account in &open_event_accounts { - if !sc_opened_accounts_hashset.contains(account) { + if !sc_opened_accounts_hashset.contains(account) && + !sc_closure_initiated_accounts_hashset.contains(account) + { new_program_swap_accounts.push(*account); } } let open_event_accounts_hashset: HashSet<_> = open_event_accounts.iter().collect(); - for account in sc_opened_accounts { - if !open_event_accounts_hashset.contains(&account) { + for account in sc_closure_initiated_accounts { + if !open_event_accounts_hashset.contains(&account.contract_swap_account) { closed_accounts.push(account); } } diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 05551abb75..b6fad341f8 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -17,7 +17,9 @@ use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_runtime::{FixedU128, Percent, Permill}; use state_chain_runtime::{ chainflip::{ - solana_elections::{SolanaFeeUnsynchronisedSettings, SolanaIngressSettings}, + solana_elections::{ + SolanaFeeUnsynchronisedSettings, SolanaIngressSettings, SolanaVaultSwapsSettings, + }, Offence, }, constants::common::*, @@ -324,7 +326,11 @@ impl ExtBuilder { }, (), (), - (), + SolanaVaultSwapsSettings { + swap_endpoint_data_account_address: + sol_test_values::SWAP_ENDPOINT_DATA_ACCOUNT_ADDRESS, + usdc_token_mint_pubkey: sol_test_values::USDC_TOKEN_MINT_PUB_KEY, + }, ), }), }, diff --git a/state-chain/chains/src/sol/sol_tx_core.rs b/state-chain/chains/src/sol/sol_tx_core.rs index 8be65dbe8b..f288e6da32 100644 --- a/state-chain/chains/src/sol/sol_tx_core.rs +++ b/state-chain/chains/src/sol/sol_tx_core.rs @@ -895,6 +895,8 @@ pub mod sol_test_values { // stored There will be a different one per each supported spl-token pub const USDC_TOKEN_VAULT_ASSOCIATED_TOKEN_ACCOUNT: SolAddress = const_address("GgqCE4bTwMy4QWVaTRTKJqETAgim49zNrH1dL6zXaTpd"); + pub const SWAP_ENDPOINT_DATA_ACCOUNT_ADDRESS: SolAddress = + const_address("GgqCE4bTwMy4QWVaTRTKJqETAgim49zNrH1dL6zXaTpd"); pub const NONCE_ACCOUNTS: [SolAddress; 10] = [ const_address("2cNMwUCF51djw2xAiiU54wz1WrU8uG4Q8Kp8nfEuwghw"), const_address("HVG21SovGzMBJDB9AQNuWb6XYq4dDZ6yUwCbRUuFnYDo"), diff --git a/state-chain/node/src/chain_spec.rs b/state-chain/node/src/chain_spec.rs index f6ce8fdbdc..b9c374b087 100644 --- a/state-chain/node/src/chain_spec.rs +++ b/state-chain/node/src/chain_spec.rs @@ -374,6 +374,7 @@ pub fn inner_cf_development_config( 100_000, sol_vault_program, sol_usdc_token_mint_pubkey, + sol_swap_endpoint_program_data_account, )), }, )) @@ -535,6 +536,7 @@ macro_rules! network_spec { 100000, sol_vault_program, sol_usdc_token_mint_pubkey, + sol_swap_endpoint_program_data_account, )), }, )) diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 90144ec95b..d642183514 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -63,6 +63,7 @@ pub fn initial_state( priority_fee: SolAmount, vault_program: SolAddress, usdc_token_mint_pubkey: SolAddress, + swap_endpoint_data_account_address: SolAddress, ) -> InitialStateOf { InitialState { unsynchronised_state: ( @@ -89,7 +90,7 @@ pub fn initial_state( SolanaIngressSettings { vault_program, usdc_token_mint_pubkey }, (), (), - (), + SolanaVaultSwapsSettings { swap_endpoint_data_account_address, usdc_token_mint_pubkey }, ), } } @@ -134,7 +135,7 @@ pub type SolanaVaultSwapTracking = ContractSwapAccountAndSender, SolanaVaultSwapDetails, BlockNumberFor, - (), + SolanaVaultSwapsSettings, SolanaVaultSwapsHandler, ::ValidatorId, SolanaTransactionBuildingError, @@ -492,11 +493,11 @@ impl ElectionEgressWitnesser for SolanaEgressWitnessingTrigger { Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode, PartialOrd, Ord, )] pub struct SolanaVaultSwapDetails { - from: Asset, - to: Asset, - deposit_amount: AssetAmount, - destination_address: EncodedAddress, - tx_hash: TransactionHash, + pub from: Asset, + pub to: Asset, + pub deposit_amount: AssetAmount, + pub destination_address: EncodedAddress, + pub tx_hash: TransactionHash, } pub struct SolanaVaultSwapsHandler; @@ -534,3 +535,21 @@ impl Environment::get_number_of_available_sol_nonce_accounts() } } + +#[derive( + Clone, PartialEq, Eq, Debug, Serialize, Deserialize, TypeInfo, Encode, Decode, PartialOrd, Ord, +)] +pub struct SolanaVaultSwapsSettings { + pub swap_endpoint_data_account_address: SolAddress, + pub usdc_token_mint_pubkey: SolAddress, +} + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkValue for SolanaVaultSwapsSettings { + fn benchmark_value() -> Self { + Self { + swap_endpoint_data_account_address: BenchmarkValue::benchmark_value(), + usdc_token_mint_pubkey: BenchmarkValue::benchmark_value(), + } + } +} From bdea2f10ecf390a339380930fd74b5a758f4c4b3 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Wed, 30 Oct 2024 17:07:53 +0100 Subject: [PATCH 18/23] feat: vault swap details --- engine/src/witness/sol/program_swaps_witnessing.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs index db1317cf22..45c210f9e3 100644 --- a/engine/src/witness/sol/program_swaps_witnessing.rs +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -9,7 +9,10 @@ use anyhow::ensure; use anyhow::{anyhow /* ensure */}; use base64::Engine; use borsh::{BorshDeserialize, BorshSerialize}; -use cf_chains::sol::{api::ContractSwapAccountAndSender, SolAddress}; +use cf_chains::{ + address::EncodedAddress, + sol::{api::ContractSwapAccountAndSender, SolAddress}, +}; use cf_primitives::Asset; use futures::{stream, StreamExt, TryStreamExt}; use itertools::Itertools; @@ -97,9 +100,9 @@ pub async fn get_program_swaps( }, SolanaVaultSwapDetails { from: if data.src_token.is_none() {Asset::Sol} else {Asset::SolUsdc}, deposit_amount: data.amount.into(), - to: todo!(), - destination_address: todo!(), - tx_hash: todo!() + destination_address: EncodedAddress::from_chain_bytes(data.dst_chain.try_into().map_err(|e| warn!("error while parsing destination chain for solana vault swap:{}. Omitting swap", e)).ok()?, data.dst_address.to_vec()).map_err(|e| warn!("failed to decode the destination chain address for solana vault swap:{}. Omitting swap", e)).ok()?, + to: data.dst_token.try_into().map_err(|e| warn!("error while decoding destination token for solana vault swap: {}. Omitting swap", e)).ok()?, + tx_hash: Default::default(), // TODO }))), // It could happen that some account is closed between the queries. This should // not happen because: From be95b64686158a6e58ad5bcc6095554ef72190b2 Mon Sep 17 00:00:00 2001 From: albert Date: Thu, 31 Oct 2024 11:21:16 +0100 Subject: [PATCH 19/23] chore: fix issues and renamings --- engine/src/witness/sol.rs | 6 +-- .../witness/sol/program_swaps_witnessing.rs | 20 ++++---- foreign-chains/solana/sol-prim/src/consts.rs | 2 +- state-chain/chains/src/lib.rs | 4 +- state-chain/chains/src/sol.rs | 4 +- state-chain/chains/src/sol/api.rs | 16 +++---- state-chain/chains/src/sol/benchmarking.rs | 6 +-- state-chain/chains/src/sol/sol_tx_core.rs | 48 +++++++++---------- .../chains/src/sol/transaction_builder.rs | 32 ++++++------- .../solana_swap_accounts_tracking.rs | 14 +++--- .../runtime/src/chainflip/solana_elections.rs | 10 ++-- 11 files changed, 81 insertions(+), 81 deletions(-) diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index 5c7a6c3834..25246a2dc5 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -17,7 +17,7 @@ use crate::{ }, }; use anyhow::Result; -use cf_chains::{sol::{api::ContractSwapAccountAndSender, SolHash}, Chain}; +use cf_chains::{sol::{api::VaultSwapAccountAndSender, SolHash}, Chain}; use futures::FutureExt; use pallet_cf_elections::{ electoral_system::ElectoralSystem, @@ -202,8 +202,8 @@ impl VoterApi for SolanaVaultSwapsVoter { properties .witnessed_open_accounts .into_iter() - .map(|ContractSwapAccountAndSender { contract_swap_account, .. }| { - contract_swap_account + .map(|VaultSwapAccountAndSender { vault_swap_account, .. }| { + vault_swap_account }) .collect(), properties.closure_initiated_accounts, diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs index f29bbb846e..81e62767db 100644 --- a/engine/src/witness/sol/program_swaps_witnessing.rs +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -12,7 +12,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use cf_chains::{ address::EncodedAddress, assets::sol::Asset as SolAsset, - sol::{api::ContractSwapAccountAndSender, SolAddress}, + sol::{api::VaultSwapAccountAndSender, SolAddress}, }; use futures::{stream, StreamExt, TryStreamExt}; use itertools::Itertools; @@ -65,12 +65,12 @@ pub async fn get_program_swaps( sol_rpc: &SolRetryRpcClient, swap_endpoint_data_account_address: SolAddress, sc_open_accounts: Vec, - sc_closure_initiated_accounts: BTreeSet, + sc_closure_initiated_accounts: BTreeSet, usdc_token_mint_pubkey: SolAddress, ) -> Result< ( - Vec<(ContractSwapAccountAndSender, SolanaVaultSwapDetails)>, - Vec, + Vec<(VaultSwapAccountAndSender, SolanaVaultSwapDetails)>, + Vec, ), anyhow::Error, > { @@ -94,8 +94,8 @@ pub async fn get_program_swaps( Some(data) if (data.src_token.is_none() || data.src_token.is_some_and(|addr| addr == usdc_token_mint_pubkey.0)) => - Some(Ok((ContractSwapAccountAndSender { - contract_swap_account: account, + Some(Ok((VaultSwapAccountAndSender { + vault_swap_account: account, swap_sender: data.sender.into() }, SolanaVaultSwapDetails { from: if data.src_token.is_none() {SolAsset::Sol} else {SolAsset::SolUsdc}, @@ -141,9 +141,9 @@ pub async fn get_program_swaps( async fn get_changed_program_swap_accounts( sol_rpc: &SolRetryRpcClient, sc_opened_accounts: Vec, - sc_closure_initiated_accounts: BTreeSet, + sc_closure_initiated_accounts: BTreeSet, swap_endpoint_data_account_address: SolAddress, -) -> Result<(Vec, Vec, u64), anyhow::Error> { +) -> Result<(Vec, Vec, u64), anyhow::Error> { let (_historical_number_event_accounts, open_event_accounts, slot) = get_swap_endpoint_data(sol_rpc, swap_endpoint_data_account_address) .await @@ -152,7 +152,7 @@ async fn get_changed_program_swap_accounts( let sc_opened_accounts_hashset: HashSet<_> = sc_opened_accounts.iter().collect(); let sc_closure_initiated_accounts_hashset = sc_closure_initiated_accounts .iter() - .map(|ContractSwapAccountAndSender { contract_swap_account, .. }| contract_swap_account) + .map(|VaultSwapAccountAndSender { vault_swap_account, .. }| vault_swap_account) .collect::>(); let mut new_program_swap_accounts = Vec::new(); @@ -168,7 +168,7 @@ async fn get_changed_program_swap_accounts( let open_event_accounts_hashset: HashSet<_> = open_event_accounts.iter().collect(); for account in sc_closure_initiated_accounts { - if !open_event_accounts_hashset.contains(&account.contract_swap_account) { + if !open_event_accounts_hashset.contains(&account.vault_swap_account) { closed_accounts.push(account); } } diff --git a/foreign-chains/solana/sol-prim/src/consts.rs b/foreign-chains/solana/sol-prim/src/consts.rs index 73c8b9f698..a80c364606 100644 --- a/foreign-chains/solana/sol-prim/src/consts.rs +++ b/foreign-chains/solana/sol-prim/src/consts.rs @@ -44,6 +44,6 @@ pub const NONCE_ACCOUNT_LENGTH: u64 = 80u64; pub const SOL_USDC_DECIMAL: u8 = 6u8; -pub const MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES: usize = 10; +pub const MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES: usize = 10; pub const MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS: u32 = 14400; pub const NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES: usize = 4; diff --git a/state-chain/chains/src/lib.rs b/state-chain/chains/src/lib.rs index 6e53887535..15bc91532f 100644 --- a/state-chain/chains/src/lib.rs +++ b/state-chain/chains/src/lib.rs @@ -4,7 +4,7 @@ #![feature(split_array)] use core::{fmt::Display, iter::Step}; -use sol::api::ContractSwapAccountAndSender; +use sol::api::VaultSwapAccountAndSender; use sp_std::marker::PhantomData; use crate::{ @@ -492,7 +492,7 @@ pub trait RegisterRedemption: ApiCall<::ChainCrypto> { pub trait CloseSolanaVaultSwapAccounts: ApiCall<::ChainCrypto> { fn new_unsigned( - accounts: Vec, + accounts: Vec, ) -> Result; } diff --git a/state-chain/chains/src/sol.rs b/state-chain/chains/src/sol.rs index b65b637fc1..fb20b1186a 100644 --- a/state-chain/chains/src/sol.rs +++ b/state-chain/chains/src/sol.rs @@ -26,7 +26,7 @@ pub use crate::assets::sol::Asset as SolAsset; use crate::benchmarking_value::BenchmarkValue; pub use sol_prim::{ consts::{ - LAMPORTS_PER_SIGNATURE, MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + LAMPORTS_PER_SIGNATURE, MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES, MAX_TRANSACTION_LENGTH, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, MICROLAMPORTS_PER_LAMPORT, NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, TOKEN_ACCOUNT_RENT, @@ -155,7 +155,7 @@ pub mod compute_units_costs { pub const COMPUTE_UNITS_PER_ROTATION: SolComputeLimit = 8_000u32; pub const COMPUTE_UNITS_PER_SET_GOV_KEY: SolComputeLimit = 15_000u32; pub const COMPUTE_UNITS_PER_BUMP_DERIVATION: SolComputeLimit = 2_000u32; - pub const COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS: SolComputeLimit = 10_000u32; + pub const COMPUTE_UNITS_PER_CLOSE_VAULT_SWAP_ACCOUNTS: SolComputeLimit = 10_000u32; pub const COMPUTE_UNITS_PER_CLOSE_ACCOUNT: SolComputeLimit = 10_000u32; /// This is equivalent to a priority fee diff --git a/state-chain/chains/src/sol/api.rs b/state-chain/chains/src/sol/api.rs index 9079a10724..33e647dd48 100644 --- a/state-chain/chains/src/sol/api.rs +++ b/state-chain/chains/src/sol/api.rs @@ -53,8 +53,8 @@ pub type DurableNonceAndAccount = (SolAddress, SolHash); PartialOrd, Eq, )] -pub struct ContractSwapAccountAndSender { - pub contract_swap_account: SolAddress, +pub struct VaultSwapAccountAndSender { + pub vault_swap_account: SolAddress, pub swap_sender: SolAddress, } @@ -404,8 +404,8 @@ impl SolanaApi { }) } - pub fn batch_close_contract_swap_accounts( - contract_swap_accounts: Vec, + pub fn batch_close_vault_swap_accounts( + vault_swap_accounts: Vec, ) -> Result { // Lookup environment variables, such as aggkey and durable nonce. let agg_key = Environment::current_agg_key()?; @@ -414,8 +414,8 @@ impl SolanaApi { let durable_nonce = Environment::nonce_account()?; // Build the transaction - let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( - contract_swap_accounts, + let transaction = SolanaTransactionBuilder::close_vault_swap_accounts( + vault_swap_accounts, sol_api_environment.vault_program_data_account, sol_api_environment.swap_endpoint_program, sol_api_environment.swap_endpoint_program_data_account, @@ -543,9 +543,9 @@ impl TransferFallback for SolanaApi { impl CloseSolanaVaultSwapAccounts for SolanaApi { fn new_unsigned( - accounts: Vec, + accounts: Vec, ) -> Result { - Self::batch_close_contract_swap_accounts(accounts) + Self::batch_close_vault_swap_accounts(accounts) } } diff --git a/state-chain/chains/src/sol/benchmarking.rs b/state-chain/chains/src/sol/benchmarking.rs index 06a8dead67..aad9226fde 100644 --- a/state-chain/chains/src/sol/benchmarking.rs +++ b/state-chain/chains/src/sol/benchmarking.rs @@ -1,7 +1,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::{ - api::{ContractSwapAccountAndSender, SolanaApi}, + api::{VaultSwapAccountAndSender, SolanaApi}, SolAddress, SolHash, SolMessage, SolSignature, SolTrackedData, SolTransaction, SolanaTransactionData, }; @@ -67,11 +67,11 @@ impl BenchmarkValue for SolanaApi { } } -impl BenchmarkValue for ContractSwapAccountAndSender { +impl BenchmarkValue for VaultSwapAccountAndSender { fn benchmark_value() -> Self { Self { swap_sender: BenchmarkValue::benchmark_value(), - contract_swap_account: BenchmarkValue::benchmark_value(), + vault_swap_account: BenchmarkValue::benchmark_value(), } } } diff --git a/state-chain/chains/src/sol/sol_tx_core.rs b/state-chain/chains/src/sol/sol_tx_core.rs index 17a6ea4798..ba3b08daeb 100644 --- a/state-chain/chains/src/sol/sol_tx_core.rs +++ b/state-chain/chains/src/sol/sol_tx_core.rs @@ -874,7 +874,7 @@ impl FromStr for Hash { pub mod sol_test_values { use crate::{ sol::{ - api::ContractSwapAccountAndSender, signing_key::SolSigningKey, + api::VaultSwapAccountAndSender, signing_key::SolSigningKey, sol_tx_core::signer::Signer, SolAddress, SolAmount, SolAsset, SolCcmAccounts, SolCcmAddress, SolComputeLimit, SolHash, }, @@ -916,49 +916,49 @@ pub mod sol_test_values { const_address("35uYgHdfZQT4kHkaaXQ6ZdCkK5LFrsk43btTLbGCRCNT"); pub const SWAP_ENDPOINT_PROGRAM_DATA_ACCOUNT: SolAddress = const_address("2tmtGLQcBd11BMiE9B1tAkQXwmPNgR79Meki2Eme4Ec9"); - pub const EVENT_AND_SENDER_ACCOUNTS: [ContractSwapAccountAndSender; 11] = [ - ContractSwapAccountAndSender { - contract_swap_account: const_address("2cHcSNtikMpjxJfwwoYL3udpy7hedRExyhakk2eZ6cYA"), + pub const EVENT_AND_SENDER_ACCOUNTS: [VaultSwapAccountAndSender; 11] = [ + VaultSwapAccountAndSender { + vault_swap_account: const_address("2cHcSNtikMpjxJfwwoYL3udpy7hedRExyhakk2eZ6cYA"), swap_sender: const_address("7tVhSXxGfZyHQem8MdZVB6SoRsrvV4H8h1rX6hwBuvEA"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("6uuU1NFyThN3KJpU9mYXkGSmd8Qgncmd9aYAWYN71VkC"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("6uuU1NFyThN3KJpU9mYXkGSmd8Qgncmd9aYAWYN71VkC"), swap_sender: const_address("P3GYr1Z67jdBVimzFjMXQpeuew5TY5txoZ9CvqASpaP"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("DmAom3kp2ZKk9cnbWEsnbkLHkp3sx9ef1EX6GWj1JRUB"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("DmAom3kp2ZKk9cnbWEsnbkLHkp3sx9ef1EX6GWj1JRUB"), swap_sender: const_address("CS7yX5TKX36ugF4bycmVQ5vqB2ZbNVC5tvtrtLP92GDW"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("CJSdHgxwHLEbTsxKsJk9UyJxUEgku2UC9GXRTzR2ieSh"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("CJSdHgxwHLEbTsxKsJk9UyJxUEgku2UC9GXRTzR2ieSh"), swap_sender: const_address("2taCR53epDtdrFZBxzKcbmv3cb5Umc5x9k2YCjmTDAnH"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("7DGwjsQEFA7XzZS9z5YbMhYGzWJSh5T78hRrU47RDTd2"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("7DGwjsQEFA7XzZS9z5YbMhYGzWJSh5T78hRrU47RDTd2"), swap_sender: const_address("FDPzoZj951Hq92jhoFdyzAVyUjyXhL8VEnqBhyjsDhow"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("A6yYXUmZHa32mcFRnwwq8ZQKCEYUn9ewF1vWn2wsXN5a"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("A6yYXUmZHa32mcFRnwwq8ZQKCEYUn9ewF1vWn2wsXN5a"), swap_sender: const_address("9bNNNU9B52VPVGm6zRccwPEexDHD1ntndD2aNu2un3ca"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("2F3365PULNzt7moa9GgHARy7Lumj5ptDQF7wDt6xeuHK"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("2F3365PULNzt7moa9GgHARy7Lumj5ptDQF7wDt6xeuHK"), swap_sender: const_address("4m5t38fJsvULKaPyWZKWjzfbvnzBGL86BTRNk5vLLUrh"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("8sCBWv9tzdf2iC4GNj61UBN6TZpzsLP5Ppv9x1ENX4HT"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("8sCBWv9tzdf2iC4GNj61UBN6TZpzsLP5Ppv9x1ENX4HT"), swap_sender: const_address("A3P5kfRU1vgZn7GjNMomS8ye6GHsoHC4JoVNUotMbDPE"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("3b1FkNvnvKJ4TzKeft7wA47VfYpjkoHPE4ER13ZTNecX"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("3b1FkNvnvKJ4TzKeft7wA47VfYpjkoHPE4ER13ZTNecX"), swap_sender: const_address("ERwuPnX66dCZqj85kH9QQJmwcVrzcczBnu8onJY2R7tG"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("Bnrp9X562krXVfaY8FnwJa3Mxp1gbDCrvGNW1qc99rKe"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("Bnrp9X562krXVfaY8FnwJa3Mxp1gbDCrvGNW1qc99rKe"), swap_sender: const_address("2aoZg41FFnTBnuHpkfHdFsCuPz8DhN4dsUW5386XwE8g"), }, - ContractSwapAccountAndSender { - contract_swap_account: const_address("EuLceVgXMaJNPT7C88pnL7DRWcf1poy9BCeWY1GL8Agd"), + VaultSwapAccountAndSender { + vault_swap_account: const_address("EuLceVgXMaJNPT7C88pnL7DRWcf1poy9BCeWY1GL8Agd"), swap_sender: const_address("G1iXMtwUU76JGau9cJm6N8wBTmcsvyXuJcC7PtfU1TXZ"), }, ]; diff --git a/state-chain/chains/src/sol/transaction_builder.rs b/state-chain/chains/src/sol/transaction_builder.rs index bb83676dbb..49715fec98 100644 --- a/state-chain/chains/src/sol/transaction_builder.rs +++ b/state-chain/chains/src/sol/transaction_builder.rs @@ -12,12 +12,12 @@ use sol_prim::consts::{ use crate::{ sol::{ api::{ - ContractSwapAccountAndSender, DurableNonceAndAccount, SolanaTransactionBuildingError, + VaultSwapAccountAndSender, DurableNonceAndAccount, SolanaTransactionBuildingError, }, compute_units_costs::{ compute_limit_with_buffer, BASE_COMPUTE_UNITS_PER_TX, COMPUTE_UNITS_PER_BUMP_DERIVATION, COMPUTE_UNITS_PER_CLOSE_ACCOUNT, - COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS, COMPUTE_UNITS_PER_FETCH_NATIVE, + COMPUTE_UNITS_PER_CLOSE_VAULT_SWAP_ACCOUNTS, COMPUTE_UNITS_PER_FETCH_NATIVE, COMPUTE_UNITS_PER_FETCH_TOKEN, COMPUTE_UNITS_PER_ROTATION, COMPUTE_UNITS_PER_SET_GOV_KEY, COMPUTE_UNITS_PER_TRANSFER_NATIVE, COMPUTE_UNITS_PER_TRANSFER_TOKEN, @@ -447,8 +447,8 @@ impl SolanaTransactionBuilder { /// Creates an instruction to close a number of open event swap accounts created via program /// swap. - pub fn close_contract_swap_accounts( - contract_swap_accounts: Vec, + pub fn close_vault_swap_accounts( + vault_swap_accounts: Vec, vault_program_data_account: SolAddress, swap_endpoint_program: SolAddress, swap_endpoint_data_account: SolAddress, @@ -456,13 +456,13 @@ impl SolanaTransactionBuilder { durable_nonce: DurableNonceAndAccount, compute_price: SolAmount, ) -> Result { - let number_of_accounts = contract_swap_accounts.len(); - let swap_and_sender_vec: Vec = contract_swap_accounts + let number_of_accounts = vault_swap_accounts.len(); + let swap_and_sender_vec: Vec = vault_swap_accounts .into_iter() // Both event account and payee should be writable and non-signers - .flat_map(|ContractSwapAccountAndSender { contract_swap_account, swap_sender }| { + .flat_map(|VaultSwapAccountAndSender { vault_swap_account, swap_sender }| { vec![ - AccountMeta::new(contract_swap_account.into(), false), + AccountMeta::new(vault_swap_account.into(), false), AccountMeta::new(swap_sender.into(), false), ] }) @@ -478,7 +478,7 @@ impl SolanaTransactionBuilder { agg_key.into(), compute_price, compute_limit_with_buffer( - COMPUTE_UNITS_PER_CLOSE_CONTRACT_SWAP_ACCOUNTS + + COMPUTE_UNITS_PER_CLOSE_VAULT_SWAP_ACCOUNTS + COMPUTE_UNITS_PER_CLOSE_ACCOUNT * number_of_accounts as u32, ), ) @@ -727,11 +727,11 @@ mod test { } #[test] - fn can_close_contract_swap_accounts() { + fn can_close_vault_swap_accounts() { let env = api_env(); - let contract_swap_accounts = vec![EVENT_AND_SENDER_ACCOUNTS[0]]; - let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( - contract_swap_accounts, + let vault_swap_accounts = vec![EVENT_AND_SENDER_ACCOUNTS[0]]; + let transaction = SolanaTransactionBuilder::close_vault_swap_accounts( + vault_swap_accounts, env.vault_program_data_account, env.swap_endpoint_program, env.swap_endpoint_program_data_account, @@ -741,18 +741,18 @@ mod test { ) .unwrap(); - // Serialized tx built in `close_contract_swap_accounts` test + // Serialized tx built in `close_vault_swap_accounts` test let expected_serialized_tx = hex_literal::hex!("01026e2d4bdca9e638b59507a70ea62ad88f098ffb25df028a19288702698fdf6d1cf77618b2123c0205a8e8d272ba8ea645b7e75c606ca3aa4356b65fa52ca20b0100050af79d5e026f12edc6443a534b2cdd5072233989b415d7596573e743f3e5b386fb17e5cc1f4d51a40626e11c783b75a45a4922615ecd7f5320b9d4d46481a196a317eb2b10d3377bda2bc7bea65bec6b8372f4fc3463ec2cd6f9fde4b2c633d1921c1f0efc91eeb48bb80c90cf97775cd5d843a96f16500266cee2c20d053152d2665730decf59d4cd6db8437dab77302287431eb7562b5997601851a0eab6946f00000000000000000000000000000000000000000000000000000000000000000306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a4000000006a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea94000000e14940a2247d0a8a33650d7dfe12d269ecabce61c1219b5a6dcdb6961026e091ef91c791d2aa8492c90f12540abd10056ce5dd8d9ab08461476c1dcc1622938c27e9074fac5e8d36cf04f94a0606fdd8ddbb420e99a489c7915ce5699e4890004050302070004040000000600090340420f000000000006000502307500000905080003010408a5663d01b94dbd79").to_vec(); test_constructed_transaction(transaction, expected_serialized_tx); } #[test] - fn can_close_max_contract_swap_accounts() { + fn can_close_max_vault_swap_accounts() { let env = api_env(); // We can close 11 accounts without reaching the transaction length limit. - let transaction = SolanaTransactionBuilder::close_contract_swap_accounts( + let transaction = SolanaTransactionBuilder::close_vault_swap_accounts( EVENT_AND_SENDER_ACCOUNTS.to_vec(), env.vault_program_data_account, env.swap_endpoint_program, diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 1bffe81fc8..9fb7e0fc7d 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -6,7 +6,7 @@ use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; #[cfg(feature = "runtime-benchmarks")] use cf_chains::benchmarking_value::BenchmarkValue; #[cfg(feature = "runtime-benchmarks")] -use cf_chains::sol::api::ContractSwapAccountAndSender; +use cf_chains::sol::api::VaultSwapAccountAndSender; use crate::{ electoral_system::{ @@ -17,7 +17,7 @@ use crate::{ CorruptStorageError, ElectionIdentifier, }; use cf_chains::sol::{ - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES, + MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES, MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS, NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES, }; @@ -45,10 +45,10 @@ pub struct SolanaVaultSwapsKnownAccounts { } #[cfg(feature = "runtime-benchmarks")] -impl BenchmarkValue for SolanaVaultSwapsKnownAccounts { +impl BenchmarkValue for SolanaVaultSwapsKnownAccounts { fn benchmark_value() -> Self { Self { - witnessed_open_accounts: vec![BenchmarkValue::benchmark_value()], + witnessed_open_accounts: sp_std::vec![BenchmarkValue::benchmark_value()], closure_initiated_accounts: BTreeSet::from([BenchmarkValue::benchmark_value()]), } } @@ -142,18 +142,18 @@ impl< if Hook::get_number_of_available_sol_nonce_accounts() > NONCE_AVAILABILITY_THRESHOLD_FOR_INITIATING_SWAP_ACCOUNT_CLOSURES && (known_accounts.witnessed_open_accounts.len() >= - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES || + MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES || (*current_block_number) .checked_sub(&electoral_access.unsynchronised_state()?) .expect("current block number is always greater than when apicall was last created") .into() >= MAX_WAIT_BLOCKS_FOR_SWAP_ACCOUNT_CLOSURE_APICALLS) { let accounts_to_close: Vec<_> = if known_accounts.witnessed_open_accounts.len() > - MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES + MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES { known_accounts .witnessed_open_accounts - .drain(..MAX_BATCH_SIZE_OF_CONTRACT_SWAP_ACCOUNT_CLOSURES) + .drain(..MAX_BATCH_SIZE_OF_VAULT_SWAP_ACCOUNT_CLOSURES) .collect() } else { sp_std::mem::take(&mut known_accounts.witnessed_open_accounts) diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 555d3a3a3d..f504b16acc 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -8,7 +8,7 @@ use cf_chains::{ instances::ChainInstanceAlias, sol::{ api::{ - ContractSwapAccountAndSender, SolanaApi, SolanaTransactionBuildingError, + VaultSwapAccountAndSender, SolanaApi, SolanaTransactionBuildingError, SolanaTransactionType, }, SolAddress, SolAmount, SolHash, SolSignature, SolTrackedData, SolanaCrypto, @@ -490,7 +490,7 @@ pub type SolanaEgressWitnessing = electoral_systems::egress_success::EgressSucce pub type SolanaVaultSwapTracking = electoral_systems::solana_swap_accounts_tracking::SolanaVaultSwapAccounts< - ContractSwapAccountAndSender, + VaultSwapAccountAndSender, SolanaVaultSwapDetails, BlockNumberFor, SolanaVaultSwapsSettings, @@ -906,13 +906,13 @@ pub struct SolanaVaultSwapsHandler; impl SolanaVaultSwapAccountsHook< - ContractSwapAccountAndSender, + VaultSwapAccountAndSender, SolanaVaultSwapDetails, SolanaTransactionBuildingError, > for SolanaVaultSwapsHandler { fn initiate_vault_swap(swap_details: SolanaVaultSwapDetails) { - let _ = SolanaIngressEgress::contract_swap_request( + let _ = SolanaIngressEgress::vault_swap_request( RuntimeOrigin::root(), swap_details.from.into(), swap_details.to, @@ -929,7 +929,7 @@ impl } fn close_accounts( - accounts: Vec, + accounts: Vec, ) -> Result<(), SolanaTransactionBuildingError> { as CloseSolanaVaultSwapAccounts>::new_unsigned(accounts).map( |apicall| { From 634a962bad6d683486e6a35280945c36fa12a3cc Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Thu, 31 Oct 2024 12:35:18 +0100 Subject: [PATCH 20/23] chore: get block number from runtime --- state-chain/pallets/cf-elections/src/lib.rs | 5 ++--- state-chain/runtime/src/chainflip/solana_elections.rs | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/state-chain/pallets/cf-elections/src/lib.rs b/state-chain/pallets/cf-elections/src/lib.rs index ff89d50fb5..2f497525bf 100644 --- a/state-chain/pallets/cf-elections/src/lib.rs +++ b/state-chain/pallets/cf-elections/src/lib.rs @@ -136,7 +136,6 @@ pub mod pallet { use cf_chains::benchmarking_value::BenchmarkValue; use cf_primitives::{AuthorityCount, EpochIndex}; use cf_traits::{AccountRoleRegistry, Chainflip, EpochInfo}; - use frame_support::sp_runtime::traits::{Header, HeaderProvider}; use crate::electoral_system::{ConsensusVote, ConsensusVotes}; use access_impls::{ElectionAccess, ElectoralAccess}; @@ -357,7 +356,7 @@ pub mod pallet { + IsType<::RuntimeEvent>; type ElectoralSystem: ElectoralSystem< - OnFinalizeContext = <::HeaderT as Header>::Number, + OnFinalizeContext = (), ValidatorId = ::ValidatorId, >; @@ -1639,7 +1638,7 @@ pub mod pallet { T::ElectoralSystem::on_finalize( electoral_access, election_identifiers, - &frame_system::Pallet::::current_block_number(), + &(), )?; Ok(()) diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index f504b16acc..1a7d36a84b 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -8,8 +8,8 @@ use cf_chains::{ instances::ChainInstanceAlias, sol::{ api::{ - VaultSwapAccountAndSender, SolanaApi, SolanaTransactionBuildingError, - SolanaTransactionType, + SolanaApi, SolanaTransactionBuildingError, SolanaTransactionType, + VaultSwapAccountAndSender, }, SolAddress, SolAmount, SolHash, SolSignature, SolTrackedData, SolanaCrypto, }, @@ -598,7 +598,7 @@ impl SolanaLiveness, > for SolanaElectionHooks { - type OnFinalizeContext = BlockNumberFor; + type OnFinalizeContext = (); type OnFinalizeReturn = (); fn on_finalize< @@ -668,7 +668,7 @@ impl >, Vec::ElectionIdentifierExtra>>, ), - context: &Self::OnFinalizeContext, + _context: &Self::OnFinalizeContext, ) -> Result { let block_height = SolanaBlockHeightTracking::on_finalize( &mut block_height_translator.translate_electoral_access(generic_electoral_access), @@ -703,7 +703,7 @@ impl SolanaVaultSwapTracking::on_finalize( &mut vault_swap_translator.translate_electoral_access(generic_electoral_access), vault_swap_identifiers, - context, + &crate::System::current_block_number(), )?; Ok(()) } From eb7146c35786f21c10c5f5ab1f00180ac5ef9ff1 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Thu, 31 Oct 2024 12:42:50 +0100 Subject: [PATCH 21/23] chore: vault_swap_request remove origin --- .../pallets/cf-ingress-egress/src/lib.rs | 2 +- .../runtime/src/chainflip/solana_elections.rs | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index 0b8d18f7da..309dfe7484 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -2100,7 +2100,7 @@ impl, I: 'static> Pallet { Ok(()) } - fn process_vault_swap_request( + pub fn process_vault_swap_request( source_asset: TargetChainAsset, deposit_amount: ::ChainAmount, destination_asset: Asset, diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 1a7d36a84b..975c5d47a5 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -1,6 +1,6 @@ use crate::{ - Environment, Offence, Reputation, Runtime, RuntimeOrigin, SolanaBroadcaster, - SolanaChainTracking, SolanaIngressEgress, SolanaThresholdSigner, + Environment, Offence, Reputation, Runtime, SolanaBroadcaster, SolanaChainTracking, + SolanaIngressEgress, SolanaThresholdSigner, }; use cf_chains::{ address::EncodedAddress, @@ -912,19 +912,18 @@ impl > for SolanaVaultSwapsHandler { fn initiate_vault_swap(swap_details: SolanaVaultSwapDetails) { - let _ = SolanaIngressEgress::vault_swap_request( - RuntimeOrigin::root(), + SolanaIngressEgress::process_vault_swap_request( swap_details.from.into(), - swap_details.to, swap_details.deposit_amount, + swap_details.to, swap_details.destination_address, - Default::default(), + Default::default(), // TODO swap_details.tx_hash, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), + Default::default(), // TODO + Default::default(), // TODO + Default::default(), // TODO + Default::default(), // TODO + Default::default(), // TODO ); } From bf40c4267b21bd0ac3a89624ba57561054e184e8 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Thu, 31 Oct 2024 14:37:14 +0100 Subject: [PATCH 22/23] fix: add is_vote_desired() --- .../electoral_systems/solana_swap_accounts_tracking.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 9fb7e0fc7d..4f1d3f3c0e 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -115,6 +115,14 @@ impl< Ok(()) } + fn is_vote_desired>( + _election_identifier_with_extra: crate::electoral_system::ElectionIdentifierOf, + _election_access: &ElectionAccess, + _current_vote: Option<(VotePropertiesOf, AuthorityVoteOf)>, + ) -> Result { + Ok(true) + } + fn on_finalize>( electoral_access: &mut ElectoralAccess, election_identifiers: Vec>, From e5dcf4b320ef203230c2f3cbe94ecc14e1973f58 Mon Sep 17 00:00:00 2001 From: Ramiz Siddiqui Date: Thu, 31 Oct 2024 16:25:47 +0100 Subject: [PATCH 23/23] fix: clippy, tests, benchmarks --- engine/src/witness/sol.rs | 9 ++--- .../witness/sol/program_swaps_witnessing.rs | 36 +++++++++++++------ .../cf-integration-tests/src/mock_runtime.rs | 1 + state-chain/chains/src/benchmarking_value.rs | 1 + state-chain/chains/src/sol/benchmarking.rs | 2 +- .../chains/src/sol/transaction_builder.rs | 4 +-- .../src/electoral_systems/mock.rs | 2 +- .../solana_swap_accounts_tracking.rs | 15 ++++++++ .../runtime/src/chainflip/solana_elections.rs | 16 ++++++++- 9 files changed, 65 insertions(+), 21 deletions(-) diff --git a/engine/src/witness/sol.rs b/engine/src/witness/sol.rs index 25246a2dc5..aa2be564a0 100644 --- a/engine/src/witness/sol.rs +++ b/engine/src/witness/sol.rs @@ -17,7 +17,10 @@ use crate::{ }, }; use anyhow::Result; -use cf_chains::{sol::{api::VaultSwapAccountAndSender, SolHash}, Chain}; +use cf_chains::{ + sol::{api::VaultSwapAccountAndSender, SolHash}, + Chain, +}; use futures::FutureExt; use pallet_cf_elections::{ electoral_system::ElectoralSystem, @@ -202,9 +205,7 @@ impl VoterApi for SolanaVaultSwapsVoter { properties .witnessed_open_accounts .into_iter() - .map(|VaultSwapAccountAndSender { vault_swap_account, .. }| { - vault_swap_account - }) + .map(|VaultSwapAccountAndSender { vault_swap_account, .. }| vault_swap_account) .collect(), properties.closure_initiated_accounts, settings.usdc_token_mint_pubkey, diff --git a/engine/src/witness/sol/program_swaps_witnessing.rs b/engine/src/witness/sol/program_swaps_witnessing.rs index 81e62767db..c061372878 100644 --- a/engine/src/witness/sol/program_swaps_witnessing.rs +++ b/engine/src/witness/sol/program_swaps_witnessing.rs @@ -68,10 +68,7 @@ pub async fn get_program_swaps( sc_closure_initiated_accounts: BTreeSet, usdc_token_mint_pubkey: SolAddress, ) -> Result< - ( - Vec<(VaultSwapAccountAndSender, SolanaVaultSwapDetails)>, - Vec, - ), + (Vec<(VaultSwapAccountAndSender, SolanaVaultSwapDetails)>, Vec), anyhow::Error, > { let (new_program_swap_accounts, closed_accounts, slot) = get_changed_program_swap_accounts( @@ -304,9 +301,9 @@ mod tests { }; use cf_chains::{Chain, Solana}; + use cf_utilities::task_scope; use futures_util::FutureExt; use std::str::FromStr; - use utilities::task_scope; use super::*; @@ -393,8 +390,14 @@ mod tests { let (new_program_swap_accounts, closed_accounts, _) = get_changed_program_swap_accounts( &client, - vec![SolAddress::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - .unwrap()], + vec![], + BTreeSet::from([VaultSwapAccountAndSender { + vault_swap_account: SolAddress::from_str( + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + ) + .unwrap(), + swap_sender: Default::default(), + }]), // Swap Endpoint Data Account Address with no opened accounts SolAddress::from_str("BckDu65u2ofAfaSDDEPg2qJTufKB4PvGxwcYhJ2wkBTC") .unwrap(), @@ -405,15 +408,26 @@ mod tests { assert_eq!(new_program_swap_accounts, vec![]); assert_eq!( closed_accounts, - vec![SolAddress::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") - .unwrap()] + vec![VaultSwapAccountAndSender { + vault_swap_account: SolAddress::from_str( + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + ) + .unwrap(), + swap_sender: Default::default(), + }] ); let (new_program_swap_accounts, closed_accounts, _) = get_changed_program_swap_accounts( &client, - vec![SolAddress::from_str("HhxGAt8THMtsW97Zuo5ZrhKgqsdD5EBgCx9vZ4n62xpf") - .unwrap()], + vec![], + BTreeSet::from([VaultSwapAccountAndSender { + vault_swap_account: SolAddress::from_str( + "HhxGAt8THMtsW97Zuo5ZrhKgqsdD5EBgCx9vZ4n62xpf", + ) + .unwrap(), + swap_sender: Default::default(), + }]), // Swap Endpoint Data Account Address with two opened accounts SolAddress::from_str("72HKrbbesW9FGuBoebns77uvY9fF9MEsw4HTMEeV53W9") .unwrap(), diff --git a/state-chain/cf-integration-tests/src/mock_runtime.rs b/state-chain/cf-integration-tests/src/mock_runtime.rs index 43da207fb4..19ad18cb31 100644 --- a/state-chain/cf-integration-tests/src/mock_runtime.rs +++ b/state-chain/cf-integration-tests/src/mock_runtime.rs @@ -320,6 +320,7 @@ impl ExtBuilder { (), (), (), + (), ), settings: ( (), diff --git a/state-chain/chains/src/benchmarking_value.rs b/state-chain/chains/src/benchmarking_value.rs index 9af451aaff..1705250c93 100644 --- a/state-chain/chains/src/benchmarking_value.rs +++ b/state-chain/chains/src/benchmarking_value.rs @@ -258,3 +258,4 @@ impl_tuple_benchmark_value!(A, B, C); impl_tuple_benchmark_value!(A, B, C, D); impl_tuple_benchmark_value!(A, B, C, D, EE); impl_tuple_benchmark_value!(A, B, C, D, EE, F); +impl_tuple_benchmark_value!(A, B, C, D, EE, F, GG); diff --git a/state-chain/chains/src/sol/benchmarking.rs b/state-chain/chains/src/sol/benchmarking.rs index aad9226fde..71bdd3818f 100644 --- a/state-chain/chains/src/sol/benchmarking.rs +++ b/state-chain/chains/src/sol/benchmarking.rs @@ -1,7 +1,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::{ - api::{VaultSwapAccountAndSender, SolanaApi}, + api::{SolanaApi, VaultSwapAccountAndSender}, SolAddress, SolHash, SolMessage, SolSignature, SolTrackedData, SolTransaction, SolanaTransactionData, }; diff --git a/state-chain/chains/src/sol/transaction_builder.rs b/state-chain/chains/src/sol/transaction_builder.rs index 49715fec98..380c861cb0 100644 --- a/state-chain/chains/src/sol/transaction_builder.rs +++ b/state-chain/chains/src/sol/transaction_builder.rs @@ -11,9 +11,7 @@ use sol_prim::consts::{ use crate::{ sol::{ - api::{ - VaultSwapAccountAndSender, DurableNonceAndAccount, SolanaTransactionBuildingError, - }, + api::{DurableNonceAndAccount, SolanaTransactionBuildingError, VaultSwapAccountAndSender}, compute_units_costs::{ compute_limit_with_buffer, BASE_COMPUTE_UNITS_PER_TX, COMPUTE_UNITS_PER_BUMP_DERIVATION, COMPUTE_UNITS_PER_CLOSE_ACCOUNT, diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs b/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs index c1750dc31c..d613a5c15e 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/mock.rs @@ -130,7 +130,7 @@ impl ElectoralSystem for MockElectoralSystem { type Vote = vote_storage::individual::Individual<(), vote_storage::individual::shared::Shared<()>>; type Consensus = AuthorityCount; - type OnFinalizeContext = u64; + type OnFinalizeContext = (); type OnFinalizeReturn = (); fn generate_vote_properties( diff --git a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs index 4f1d3f3c0e..9636003534 100644 --- a/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs +++ b/state-chain/pallets/cf-elections/src/electoral_systems/solana_swap_accounts_tracking.rs @@ -62,6 +62,21 @@ pub struct SolanaVaultSwapsVote { pub confirm_closed_accounts: BTreeSet, } +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkValue + for SolanaVaultSwapsVote +{ + fn benchmark_value() -> Self { + Self { + new_accounts: BTreeSet::from([ + BenchmarkValue::benchmark_value(), + BenchmarkValue::benchmark_value(), + ]), + confirm_closed_accounts: BTreeSet::from([BenchmarkValue::benchmark_value()]), + } + } +} + pub struct SolanaVaultSwapAccounts< Account, SwapDetails, diff --git a/state-chain/runtime/src/chainflip/solana_elections.rs b/state-chain/runtime/src/chainflip/solana_elections.rs index 975c5d47a5..cf653bbe26 100644 --- a/state-chain/runtime/src/chainflip/solana_elections.rs +++ b/state-chain/runtime/src/chainflip/solana_elections.rs @@ -902,6 +902,20 @@ pub struct SolanaVaultSwapDetails { pub destination_address: EncodedAddress, pub tx_hash: TransactionHash, } + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkValue for SolanaVaultSwapDetails { + fn benchmark_value() -> Self { + Self { + from: BenchmarkValue::benchmark_value(), + to: BenchmarkValue::benchmark_value(), + deposit_amount: BenchmarkValue::benchmark_value(), + destination_address: BenchmarkValue::benchmark_value(), + tx_hash: BenchmarkValue::benchmark_value(), + } + } +} + pub struct SolanaVaultSwapsHandler; impl @@ -913,7 +927,7 @@ impl { fn initiate_vault_swap(swap_details: SolanaVaultSwapDetails) { SolanaIngressEgress::process_vault_swap_request( - swap_details.from.into(), + swap_details.from, swap_details.deposit_amount, swap_details.to, swap_details.destination_address,