diff --git a/magicblock-committor-service/src/intent_executor/error.rs b/magicblock-committor-service/src/intent_executor/error.rs index 62199ac56..e17eda365 100644 --- a/magicblock-committor-service/src/intent_executor/error.rs +++ b/magicblock-committor-service/src/intent_executor/error.rs @@ -40,6 +40,8 @@ pub enum IntentExecutorError { EmptyIntentError, #[error("User supplied actions are ill-formed: {0}. {:?}", .1)] ActionsError(#[source] TransactionError, Option), + #[error("Invalid undelegation: {0}. {:?}", .1)] + UndelegationError(#[source] TransactionError, Option), #[error("Accounts committed with an invalid Commit id: {0}. {:?}", .1)] CommitIDError(#[source] TransactionError, Option), #[error("Max instruction trace length exceeded: {0}. {:?}", .1)] @@ -94,6 +96,10 @@ impl IntentExecutorError { err, signature, ) => IntentExecutorError::CommitIDError(err, signature), + TransactionStrategyExecutionError::UndelegationError( + err, + signature, + ) => IntentExecutorError::UndelegationError(err, signature), TransactionStrategyExecutionError::InternalError(err) => { converter(err) } @@ -117,6 +123,8 @@ impl metrics::LabelValue for IntentExecutorError { pub enum TransactionStrategyExecutionError { #[error("User supplied actions are ill-formed: {0}. {:?}", .1)] ActionsError(#[source] TransactionError, Option), + #[error("Invalid undelegation: {0}. {:?}", .1)] + UndelegationError(#[source] TransactionError, Option), #[error("Accounts committed with an invalid Commit id: {0}. {:?}", .1)] CommitIDError(#[source] TransactionError, Option), #[error("Max instruction trace length exceeded: {0}. {:?}", .1)] @@ -146,14 +154,6 @@ impl TransactionStrategyExecutionError { dlp::error::DlpError::NonceOutOfOrder as u32; match err { - // Filter CommitIdError by custom error code - transaction_err @ TransactionError::InstructionError( - _, - InstructionError::Custom(NONCE_OUT_OF_ORDER), - ) => Ok(TransactionStrategyExecutionError::CommitIDError( - transaction_err, - signature, - )), // Some tx may use too much CPIs and we can handle it in certain cases transaction_err @ TransactionError::InstructionError( _, @@ -162,25 +162,45 @@ impl TransactionStrategyExecutionError { transaction_err, signature, )), - // Filter ActionError, we can attempt recovery by stripping away actions - transaction_err @ TransactionError::InstructionError(index, _) => { + // Map per-task InstructionError into CommitID / Actions / Undelegation errors when possible + TransactionError::InstructionError(index, instruction_err) => { + let tx_err_helper = |instruction_err| -> TransactionError { + TransactionError::InstructionError(index, instruction_err) + }; let Some(action_index) = index.checked_sub(OFFSET) else { - return Err(transaction_err); + return Err(tx_err_helper(instruction_err)); + }; + + let Some(task_type) = tasks + .get(action_index as usize) + .map(|task| task.task_type()) + else { + return Err(tx_err_helper(instruction_err)); }; - // If index corresponds to an Action -> ActionsError; otherwise -> InternalError. - if matches!( - tasks - .get(action_index as usize) - .map(|task| task.task_type()), - Some(TaskType::Action) - ) { - Ok(TransactionStrategyExecutionError::ActionsError( - transaction_err, + match (task_type, instruction_err) { + ( + TaskType::Commit, + InstructionError::Custom(NONCE_OUT_OF_ORDER), + ) => Ok(TransactionStrategyExecutionError::CommitIDError( + tx_err_helper(InstructionError::Custom( + NONCE_OUT_OF_ORDER, + )), signature, - )) - } else { - Err(transaction_err) + )), + (TaskType::Action, instruction_err) => { + Ok(TransactionStrategyExecutionError::ActionsError( + tx_err_helper(instruction_err), + signature, + )) + } + (TaskType::Undelegate, instruction_err) => Ok( + TransactionStrategyExecutionError::UndelegationError( + tx_err_helper(instruction_err), + signature, + ), + ), + (_, instruction_err) => Err(tx_err_helper(instruction_err)), } } // This means transaction failed to other reasons that we don't handle - propagate diff --git a/magicblock-committor-service/src/intent_executor/mod.rs b/magicblock-committor-service/src/intent_executor/mod.rs index fa85cfee1..6c61cd8cc 100644 --- a/magicblock-committor-service/src/intent_executor/mod.rs +++ b/magicblock-committor-service/src/intent_executor/mod.rs @@ -445,6 +445,35 @@ where (commit_strategy, finalize_strategy, to_cleanup) } + /// Handles actions error, stripping away actions + /// Returns [`TransactionStrategy`] to be cleaned up + fn handle_undelegation_error( + &self, + strategy: &mut TransactionStrategy, + ) -> TransactionStrategy { + let position = strategy + .optimized_tasks + .iter() + .position(|el| el.task_type() == TaskType::Undelegate); + + if let Some(position) = position { + // Remove everything after undelegation including post undelegation actions + let removed_task = + strategy.optimized_tasks.drain(position..).collect(); + let old_alts = + strategy.dummy_revaluate_alts(&self.authority.pubkey()); + TransactionStrategy { + optimized_tasks: removed_task, + lookup_tables_keys: old_alts, + } + } else { + TransactionStrategy { + optimized_tasks: vec![], + lookup_tables_keys: vec![], + } + } + } + /// Shared helper for sending transactions async fn send_prepared_message( &self, @@ -520,7 +549,8 @@ where } Err(IntentExecutorError::CommitIDError(_, _)) | Err(IntentExecutorError::ActionsError(_, _)) - | Err(IntentExecutorError::CpiLimitError(_, _)) => None, + | Err(IntentExecutorError::CpiLimitError(_, _)) + | Err(IntentExecutorError::UndelegationError(_, _)) => None, Err(IntentExecutorError::EmptyIntentError) | Err(IntentExecutorError::FailedToFitError) | Err(IntentExecutorError::TaskBuilderError(_)) @@ -758,6 +788,8 @@ where let result = self.execute_inner(base_intent, &persister).await; if let Some(pubkeys) = pubkeys { // Reset TaskInfoFetcher, as cache could become invalid + // NOTE: if undelegation was removed - we still reset + // We assume its safe since all consecutive commits will fail if result.is_err() || is_undelegate { self.task_info_fetcher.reset(ResetType::Specific(&pubkeys)); } diff --git a/magicblock-committor-service/src/intent_executor/single_stage_executor.rs b/magicblock-committor-service/src/intent_executor/single_stage_executor.rs index ad42448c6..991051ae9 100644 --- a/magicblock-committor-service/src/intent_executor/single_stage_executor.rs +++ b/magicblock-committor-service/src/intent_executor/single_stage_executor.rs @@ -175,6 +175,13 @@ where .await?; Ok(ControlFlow::Continue(to_cleanup)) } + TransactionStrategyExecutionError::UndelegationError(_, _) => { + // Here we patch strategy for it to be retried in next iteration + // & we also record data that has to be cleaned up after patch + let to_cleanup = + self.handle_undelegation_error(transaction_strategy); + Ok(ControlFlow::Continue(to_cleanup)) + } TransactionStrategyExecutionError::CpiLimitError(_, _) => { // Can't be handled in scope of single stage execution // We signal flow break diff --git a/magicblock-committor-service/src/intent_executor/two_stage_executor.rs b/magicblock-committor-service/src/intent_executor/two_stage_executor.rs index 227a67221..25f6626aa 100644 --- a/magicblock-committor-service/src/intent_executor/two_stage_executor.rs +++ b/magicblock-committor-service/src/intent_executor/two_stage_executor.rs @@ -174,7 +174,13 @@ where TransactionStrategyExecutionError::ActionsError(_, _) => { // Unexpected in Two Stage commit // That would mean that Two Stage executes Standalone commit - error!("Unexpected error in Two stage commit flow: {}", err); + error!("Unexpected error in two stage commit flow: {}", err); + Ok(ControlFlow::Break(())) + } + TransactionStrategyExecutionError::UndelegationError(_, _) => { + // Unexpected in Two Stage commit + // That would mean that Two Stage executes undelegation in commit phase + error!("Unexpected error in two stage commit flow: {}", err); Ok(ControlFlow::Break(())) } TransactionStrategyExecutionError::CpiLimitError(_, _) => { @@ -213,6 +219,13 @@ where let to_cleanup = self.handle_actions_error(finalize_strategy); Ok(ControlFlow::Continue(to_cleanup)) } + TransactionStrategyExecutionError::UndelegationError(_, _) => { + // Here we patch strategy for it to be retried in next iteration + // & we also record data that has to be cleaned up after patch + let to_cleanup = + self.handle_undelegation_error(finalize_strategy); + Ok(ControlFlow::Continue(to_cleanup)) + } TransactionStrategyExecutionError::CpiLimitError(_, _) => { // Can't be handled warn!("Finalization tasks exceeded CpiLimitError: {}", err); diff --git a/test-integration/Cargo.lock b/test-integration/Cargo.lock index 7c76ccce9..72d363a06 100644 --- a/test-integration/Cargo.lock +++ b/test-integration/Cargo.lock @@ -719,7 +719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" dependencies = [ "borsh-derive 0.10.4", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -2268,10 +2268,10 @@ dependencies = [ [[package]] name = "guinea" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "serde", "solana-program", ] @@ -3454,7 +3454,7 @@ dependencies = [ [[package]] name = "magicblock-account-cloner" -version = "0.2.3" +version = "0.2.4" dependencies = [ "async-trait", "bincode", @@ -3465,7 +3465,7 @@ dependencies = [ "magicblock-config", "magicblock-core", "magicblock-ledger", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "magicblock-program", "magicblock-rpc-client", "solana-sdk", @@ -3475,7 +3475,7 @@ dependencies = [ [[package]] name = "magicblock-accounts" -version = "0.2.3" +version = "0.2.4" dependencies = [ "async-trait", "futures-util", @@ -3488,7 +3488,7 @@ dependencies = [ "magicblock-core", "magicblock-delegation-program", "magicblock-ledger", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "magicblock-metrics", "magicblock-processor", "magicblock-program", @@ -3503,7 +3503,7 @@ dependencies = [ [[package]] name = "magicblock-accounts-db" -version = "0.2.3" +version = "0.2.4" dependencies = [ "lmdb-rkv", "log", @@ -3520,7 +3520,7 @@ dependencies = [ [[package]] name = "magicblock-aperture" -version = "0.2.3" +version = "0.2.4" dependencies = [ "arc-swap", "base64 0.21.7", @@ -3569,7 +3569,7 @@ dependencies = [ [[package]] name = "magicblock-api" -version = "0.2.3" +version = "0.2.4" dependencies = [ "anyhow", "bincode", @@ -3590,7 +3590,7 @@ dependencies = [ "magicblock-core", "magicblock-delegation-program", "magicblock-ledger", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "magicblock-metrics", "magicblock-processor", "magicblock-program", @@ -3613,7 +3613,7 @@ dependencies = [ [[package]] name = "magicblock-chainlink" -version = "0.2.3" +version = "0.2.4" dependencies = [ "arc-swap", "async-trait", @@ -3624,7 +3624,7 @@ dependencies = [ "lru 0.16.0", "magicblock-core", "magicblock-delegation-program", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "magicblock-metrics", "serde_json", "solana-account", @@ -3648,7 +3648,7 @@ dependencies = [ [[package]] name = "magicblock-committor-program" -version = "0.2.3" +version = "0.2.4" dependencies = [ "borsh 1.5.7", "borsh-derive 1.5.7", @@ -3662,7 +3662,7 @@ dependencies = [ [[package]] name = "magicblock-committor-service" -version = "0.2.3" +version = "0.2.4" dependencies = [ "async-trait", "base64 0.21.7", @@ -3694,7 +3694,7 @@ dependencies = [ [[package]] name = "magicblock-config" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bs58", "clap 4.5.41", @@ -3713,11 +3713,11 @@ dependencies = [ [[package]] name = "magicblock-config-helpers" -version = "0.2.3" +version = "0.2.4" [[package]] name = "magicblock-config-macro" -version = "0.2.3" +version = "0.2.4" dependencies = [ "clap 4.5.41", "convert_case 0.8.0", @@ -3729,11 +3729,11 @@ dependencies = [ [[package]] name = "magicblock-core" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "flume", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "serde", "solana-account", "solana-account-decoder", @@ -3770,7 +3770,7 @@ dependencies = [ [[package]] name = "magicblock-ledger" -version = "0.2.3" +version = "0.2.4" dependencies = [ "arc-swap", "bincode", @@ -3790,7 +3790,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk", - "solana-storage-proto 0.2.3", + "solana-storage-proto 0.2.4", "solana-svm 2.2.1 (git+https://github.com/magicblock-labs/magicblock-svm.git?rev=e480fa2)", "solana-timings", "solana-transaction-status", @@ -3812,7 +3812,7 @@ dependencies = [ [[package]] name = "magicblock-magic-program-api" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "serde", @@ -3821,7 +3821,7 @@ dependencies = [ [[package]] name = "magicblock-metrics" -version = "0.2.3" +version = "0.2.4" dependencies = [ "http-body-util", "hyper 1.6.0", @@ -3835,7 +3835,7 @@ dependencies = [ [[package]] name = "magicblock-processor" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "log", @@ -3869,12 +3869,12 @@ dependencies = [ [[package]] name = "magicblock-program" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "lazy_static", "magicblock-core", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "magicblock-metrics", "num-derive", "num-traits", @@ -3887,7 +3887,7 @@ dependencies = [ [[package]] name = "magicblock-rpc-client" -version = "0.2.3" +version = "0.2.4" dependencies = [ "log", "solana-rpc-client", @@ -3901,7 +3901,7 @@ dependencies = [ [[package]] name = "magicblock-table-mania" -version = "0.2.3" +version = "0.2.4" dependencies = [ "ed25519-dalek", "log", @@ -3919,7 +3919,7 @@ dependencies = [ [[package]] name = "magicblock-task-scheduler" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "chrono", @@ -3944,7 +3944,7 @@ dependencies = [ [[package]] name = "magicblock-validator-admin" -version = "0.2.3" +version = "0.2.4" dependencies = [ "anyhow", "log", @@ -3962,7 +3962,7 @@ dependencies = [ [[package]] name = "magicblock-version" -version = "0.2.3" +version = "0.2.4" dependencies = [ "git-version", "rustc_version", @@ -4851,7 +4851,7 @@ dependencies = [ "bincode", "borsh 1.5.7", "ephemeral-rollups-sdk", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "serde", "solana-program", ] @@ -5831,7 +5831,7 @@ dependencies = [ "integration-test-tools", "log", "magicblock-core", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "program-schedulecommit", "schedulecommit-client", "solana-program", @@ -5847,7 +5847,7 @@ version = "0.0.0" dependencies = [ "integration-test-tools", "magicblock-core", - "magicblock-magic-program-api 0.2.3", + "magicblock-magic-program-api 0.2.4", "program-schedulecommit", "program-schedulecommit-security", "schedulecommit-client", @@ -8988,7 +8988,7 @@ dependencies = [ [[package]] name = "solana-storage-proto" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bincode", "bs58", @@ -10489,7 +10489,7 @@ dependencies = [ [[package]] name = "test-kit" -version = "0.2.3" +version = "0.2.4" dependencies = [ "env_logger 0.11.8", "guinea", diff --git a/test-integration/programs/flexi-counter/src/processor.rs b/test-integration/programs/flexi-counter/src/processor.rs index 8bf826635..54220eeed 100644 --- a/test-integration/programs/flexi-counter/src/processor.rs +++ b/test-integration/programs/flexi-counter/src/processor.rs @@ -37,7 +37,7 @@ use crate::{ }, schedule_intent::process_create_intent, }, - state::FlexiCounter, + state::{FlexiCounter, FAIL_UNDELEGATION_CODE, FAIL_UNDELEGATION_LABEL}, utils::assert_keys_equal, }; @@ -395,6 +395,16 @@ fn process_undelegate_request( system_program, account_seeds, )?; + + { + let data = delegated_account.data.borrow(); + if let Ok(counter) = FlexiCounter::deserialize(&mut data.as_ref()) { + if counter.label == FAIL_UNDELEGATION_LABEL { + return Err(ProgramError::Custom(FAIL_UNDELEGATION_CODE)); + } + } + }; + Ok(()) } diff --git a/test-integration/programs/flexi-counter/src/state.rs b/test-integration/programs/flexi-counter/src/state.rs index 02b286a31..2eb9a5255 100644 --- a/test-integration/programs/flexi-counter/src/state.rs +++ b/test-integration/programs/flexi-counter/src/state.rs @@ -9,6 +9,8 @@ pub struct FlexiCounter { } const FLEXI_COUNTER_SEED: &[u8] = b"flexi_counter"; +pub const FAIL_UNDELEGATION_LABEL: &str = "undelegate_fail"; +pub const FAIL_UNDELEGATION_CODE: u32 = 122; impl FlexiCounter { pub fn new(label: String) -> Self { diff --git a/test-integration/test-committor-service/tests/test_intent_executor.rs b/test-integration/test-committor-service/tests/test_intent_executor.rs index 8e904e902..5d78507d3 100644 --- a/test-integration/test-committor-service/tests/test_intent_executor.rs +++ b/test-integration/test-committor-service/tests/test_intent_executor.rs @@ -35,7 +35,8 @@ use magicblock_program::{ }; use magicblock_table_mania::TableMania; use program_flexi_counter::{ - instruction::FlexiCounterInstruction, state::FlexiCounter, + instruction::FlexiCounterInstruction, + state::{FlexiCounter, FAIL_UNDELEGATION_LABEL}, }; use solana_account::Account; use solana_pubkey::Pubkey; @@ -117,7 +118,7 @@ async fn test_commit_id_error_parsing() { task_info_fetcher, pre_test_tablemania_state: _, } = TestEnv::setup().await; - let (counter_auth, account) = setup_counter(COUNTER_SIZE).await; + let (counter_auth, account) = setup_counter(COUNTER_SIZE, None).await; let intent = create_intent( vec![CommittedAccount { pubkey: FlexiCounter::pda(&counter_auth.pubkey()).0, @@ -157,6 +158,55 @@ async fn test_commit_id_error_parsing() { assert!(err.to_string().contains(EXPECTED_ERR_MSG)); } +#[tokio::test] +async fn test_undelegation_error_parsing() { + const COUNTER_SIZE: u64 = 70; + const EXPECTED_ERR_MSG: &str = "Invalid undelegation: Error processing Instruction 4: custom program error: 0x7a."; + + let TestEnv { + fixture, + intent_executor, + task_info_fetcher, + pre_test_tablemania_state: _, + } = TestEnv::setup().await; + + // Create counter that will force undelegation to fail + let (counter_auth, account) = + setup_counter(COUNTER_SIZE, Some(FAIL_UNDELEGATION_LABEL.to_string())) + .await; + let intent = create_intent( + vec![CommittedAccount { + pubkey: FlexiCounter::pda(&counter_auth.pubkey()).0, + account, + }], + true, + ); + + let mut transaction_strategy = single_flow_transaction_strategy( + &fixture.authority.pubkey(), + &task_info_fetcher, + &intent, + ) + .await; + let execution_result = intent_executor + .prepare_and_execute_strategy( + &mut transaction_strategy, + &None::, + ) + .await; + assert!(execution_result.is_ok(), "Preparation is expected to pass!"); + + // Verify that we got UndelegationError + let execution_result = execution_result.unwrap(); + assert!(execution_result.is_err()); + let err = execution_result.unwrap_err(); + assert!(matches!( + err, + TransactionStrategyExecutionError::UndelegationError(_, _) + )); + assert!(err.to_string().contains(EXPECTED_ERR_MSG)); +} + #[tokio::test] async fn test_action_error_parsing() { const COUNTER_SIZE: u64 = 70; @@ -169,7 +219,7 @@ async fn test_action_error_parsing() { pre_test_tablemania_state: _, } = TestEnv::setup().await; - let (counter_auth, account) = setup_counter(COUNTER_SIZE).await; + let (counter_auth, account) = setup_counter(COUNTER_SIZE, None).await; setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) .await; @@ -230,7 +280,7 @@ async fn test_cpi_limits_error_parsing() { } = TestEnv::setup().await; let counters = (0..COUNTER_NUM).map(|_| async { - let (counter_auth, account) = setup_counter(COUNTER_SIZE).await; + let (counter_auth, account) = setup_counter(COUNTER_SIZE, None).await; setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) .await; @@ -287,7 +337,8 @@ async fn test_commit_id_error_recovery() { let counter_auth = Keypair::new(); let (pubkey, mut account) = - init_and_delegate_account_on_chain(&counter_auth, COUNTER_SIZE).await; + init_and_delegate_account_on_chain(&counter_auth, COUNTER_SIZE, None) + .await; account.owner = program_flexi_counter::id(); let committed_account = CommittedAccount { pubkey, account }; @@ -316,6 +367,8 @@ async fn test_commit_id_error_recovery() { ) }) .collect(); + + tokio::time::sleep(Duration::from_secs(1)).await; verify( &fixture.table_mania, fixture.rpc_client.get_inner(), @@ -326,6 +379,47 @@ async fn test_commit_id_error_recovery() { .await; } +#[tokio::test] +async fn test_undelegation_error_recovery() { + const COUNTER_SIZE: u64 = 70; + + let TestEnv { + fixture, + intent_executor, + task_info_fetcher: _, + pre_test_tablemania_state, + } = TestEnv::setup().await; + + let counter_auth = Keypair::new(); + let (pubkey, mut account) = init_and_delegate_account_on_chain( + &counter_auth, + COUNTER_SIZE, + Some(FAIL_UNDELEGATION_LABEL.to_string()), + ) + .await; + + account.owner = program_flexi_counter::id(); + let committed_account = CommittedAccount { pubkey, account }; + let intent = create_intent(vec![committed_account.clone()], true); + + // Execute intent + let res = intent_executor + .execute(intent, None::) + .await; + assert!(res.is_ok()); + assert!(matches!(res.unwrap(), ExecutionOutput::SingleStage(_))); + + tokio::time::sleep(Duration::from_secs(1)).await; + verify( + &fixture.table_mania, + fixture.rpc_client.get_inner(), + &HashMap::new(), + &pre_test_tablemania_state, + &[committed_account], + ) + .await; +} + #[tokio::test] async fn test_action_error_recovery() { const COUNTER_SIZE: u64 = 100; @@ -339,7 +433,7 @@ async fn test_action_error_recovery() { let payer = setup_payer(fixture.rpc_client.get_inner()).await; let (counter_pubkey, mut account) = - init_and_delegate_account_on_chain(&payer, COUNTER_SIZE).await; + init_and_delegate_account_on_chain(&payer, COUNTER_SIZE, None).await; account.owner = program_flexi_counter::id(); let committed_account = CommittedAccount { @@ -369,6 +463,8 @@ async fn test_action_error_recovery() { &[committed_account], ) .await; + + tokio::time::sleep(Duration::from_secs(1)).await; verify_table_mania_released( &fixture.table_mania, &pre_test_tablemania_state, @@ -389,7 +485,7 @@ async fn test_commit_id_and_action_errors_recovery() { let payer = setup_payer(fixture.rpc_client.get_inner()).await; let (counter_pubkey, mut account) = - init_and_delegate_account_on_chain(&payer, COUNTER_SIZE).await; + init_and_delegate_account_on_chain(&payer, COUNTER_SIZE, None).await; account.owner = program_flexi_counter::id(); let committed_account = CommittedAccount { @@ -426,6 +522,8 @@ async fn test_commit_id_and_action_errors_recovery() { &[committed_account], ) .await; + + tokio::time::sleep(Duration::from_secs(1)).await; verify_table_mania_released( &fixture.table_mania, &pre_test_tablemania_state, @@ -446,7 +544,7 @@ async fn test_cpi_limits_error_recovery() { } = TestEnv::setup().await; let counters = (0..COUNTER_NUM).map(|_| async { - let (counter_auth, account) = setup_counter(COUNTER_SIZE).await; + let (counter_auth, account) = setup_counter(COUNTER_SIZE, None).await; setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) .await; @@ -498,6 +596,7 @@ async fn test_cpi_limits_error_recovery() { } )); + tokio::time::sleep(Duration::from_secs(1)).await; let commit_ids_by_pk: HashMap<_, _> = committed_accounts .iter() .map(|el| { @@ -533,7 +632,7 @@ async fn test_commit_id_actions_cpi_limit_errors_recovery() { // Prepare multiple counters; each needs an escrow (payer) to be able to execute base actions. // We also craft unique on-chain data so we can verify post-commit state exactly. let counters = (0..COUNTER_NUM).map(|_| async { - let (counter_auth, account) = setup_counter(COUNTER_SIZE).await; + let (counter_auth, account) = setup_counter(COUNTER_SIZE, None).await; setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) .await; (counter_auth, account) @@ -610,6 +709,7 @@ async fn test_commit_id_actions_cpi_limit_errors_recovery() { } )); + tokio::time::sleep(Duration::from_secs(1)).await; let commit_ids_by_pk: HashMap<_, _> = committed_accounts .iter() .map(|el| { @@ -728,10 +828,14 @@ async fn setup_payer_with_keypair( ); } -async fn setup_counter(counter_bytes: u64) -> (Keypair, Account) { +async fn setup_counter( + counter_bytes: u64, + label: Option, +) -> (Keypair, Account) { let counter_auth = Keypair::new(); let (_, mut account) = - init_and_delegate_account_on_chain(&counter_auth, counter_bytes).await; + init_and_delegate_account_on_chain(&counter_auth, counter_bytes, label) + .await; account.owner = program_flexi_counter::id(); (counter_auth, account) diff --git a/test-integration/test-committor-service/tests/test_ix_commit_local.rs b/test-integration/test-committor-service/tests/test_ix_commit_local.rs index 4281d3149..c1a74d7c2 100644 --- a/test-integration/test-committor-service/tests/test_ix_commit_local.rs +++ b/test-integration/test-committor-service/tests/test_ix_commit_local.rs @@ -4,6 +4,7 @@ use std::{ time::{Duration, Instant}, }; +use borsh::to_vec; use log::*; use magicblock_committor_service::{ config::ChainConfig, @@ -18,6 +19,7 @@ use magicblock_program::magic_scheduled_base_intent::{ ScheduledBaseIntent, UndelegateType, }; use magicblock_rpc_client::MagicblockRpcClient; +use program_flexi_counter::state::FlexiCounter; use solana_account::{Account, ReadableAccount}; use solana_pubkey::Pubkey; use solana_rpc_client::nonblocking::rpc_client::RpcClient; @@ -112,9 +114,18 @@ async fn commit_single_account( let counter_auth = Keypair::new(); let (pubkey, mut account) = - init_and_delegate_account_on_chain(&counter_auth, bytes as u64).await; + init_and_delegate_account_on_chain(&counter_auth, bytes as u64, None) + .await; + + let counter = FlexiCounter { + label: "Counter".to_string(), + updates: 0, + count: 101, + }; + let mut data = to_vec(&counter).unwrap(); + data.resize(bytes, 0); + account.data = data; account.owner = program_flexi_counter::id(); - account.data = vec![101_u8; bytes]; let account = CommittedAccount { pubkey, account }; let base_intent = if undelegate { @@ -393,9 +404,12 @@ async fn create_bundles( let bytes = *bytes; join_set.spawn(async move { let counter_auth = Keypair::new(); - let (pda, mut pda_acc) = - init_and_delegate_account_on_chain(&counter_auth, bytes as u64) - .await; + let (pda, mut pda_acc) = init_and_delegate_account_on_chain( + &counter_auth, + bytes as u64, + None, + ) + .await; pda_acc.owner = program_flexi_counter::id(); pda_acc.data = vec![0u8; bytes]; diff --git a/test-integration/test-committor-service/tests/utils/instructions.rs b/test-integration/test-committor-service/tests/utils/instructions.rs index 4e90c0b7d..9b7e3ffbd 100644 --- a/test-integration/test-committor-service/tests/utils/instructions.rs +++ b/test-integration/test-committor-service/tests/utils/instructions.rs @@ -20,12 +20,14 @@ pub struct InitAccountAndDelegateIxs { pub fn init_account_and_delegate_ixs( payer: Pubkey, bytes: u64, + label: Option, ) -> InitAccountAndDelegateIxs { const MAX_ALLOC: u64 = magicblock_committor_program::consts::MAX_ACCOUNT_ALLOC_PER_INSTRUCTION_SIZE as u64; use program_flexi_counter::{instruction::*, state::*}; - let init_counter_ix = create_init_ix(payer, "COUNTER".to_string()); + let init_counter_ix = + create_init_ix(payer, label.unwrap_or("COUNTER".to_string())); let rent_exempt = Rent::default().minimum_balance(bytes as usize); let num_reallocs = bytes.div_ceil(MAX_ALLOC); diff --git a/test-integration/test-committor-service/tests/utils/transactions.rs b/test-integration/test-committor-service/tests/utils/transactions.rs index 82440d93c..851be7e39 100644 --- a/test-integration/test-committor-service/tests/utils/transactions.rs +++ b/test-integration/test-committor-service/tests/utils/transactions.rs @@ -127,6 +127,7 @@ pub async fn tx_logs_contain( pub async fn init_and_delegate_account_on_chain( counter_auth: &Keypair, bytes: u64, + label: Option, ) -> (Pubkey, Account) { let rpc_client = RpcClient::new("http://localhost:7799".to_string()); @@ -142,7 +143,7 @@ pub async fn init_and_delegate_account_on_chain( delegate: delegate_ix, pda, rent_excempt, - } = init_account_and_delegate_ixs(counter_auth.pubkey(), bytes); + } = init_account_and_delegate_ixs(counter_auth.pubkey(), bytes, label); let latest_block_hash = rpc_client.get_latest_blockhash().await.unwrap(); // 1. Init account