diff --git a/Cargo.lock b/Cargo.lock index c657502e9..0923d2bdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -495,6 +506,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.8.2" @@ -641,6 +664,28 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta 0.1.4", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.23.1" @@ -1326,7 +1371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6503af7917fea18ffef8f7e8553fb8dff89e2e6837e94e09dd7fb069c82d62c" dependencies = [ "bytes", - "rkyv", + "rkyv 0.8.11", "serde", "simdutf8", ] @@ -1473,6 +1518,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -1722,6 +1773,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -1729,7 +1783,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -2897,21 +2951,23 @@ dependencies = [ [[package]] name = "magicblock-delegation-program" -version = "1.1.0" -source = "git+https://github.com/magicblock-labs/delegation-program.git?rev=aa1de56d90c#aa1de56d90c8a242377accd59899f272f0131f8c" +version = "1.1.3" +source = "git+https://github.com/magicblock-labs/delegation-program.git?rev=735640a#735640a1260f5bf77d907b5fd46ca50dd162afbd" dependencies = [ "bincode", "borsh 1.5.7", "bytemuck", "num_enum", - "paste", "pinocchio", "pinocchio-log", "pinocchio-pubkey", "pinocchio-system", + "rkyv 0.7.45", "solana-curve25519", "solana-program", "solana-security-txt", + "static_assertions", + "strum", "thiserror 1.0.69", ] @@ -3965,13 +4021,33 @@ dependencies = [ "autotools", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive 0.1.4", +] + [[package]] name = "ptr_meta" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ - "ptr_meta_derive", + "ptr_meta_derive 0.3.0", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -4020,13 +4096,19 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rancor" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" dependencies = [ - "ptr_meta", + "ptr_meta 0.3.0", ] [[package]] @@ -4214,6 +4296,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "rend" version = "0.5.2" @@ -4296,6 +4387,24 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta 0.1.4", + "rend 0.4.2", + "rkyv_derive 0.7.45", + "seahash", + "tinyvec", + "uuid", +] + [[package]] name = "rkyv" version = "0.8.11" @@ -4306,14 +4415,25 @@ dependencies = [ "hashbrown 0.15.4", "indexmap 2.10.0", "munge", - "ptr_meta", + "ptr_meta 0.3.0", "rancor", - "rend", - "rkyv_derive", + "rend 0.5.2", + "rkyv_derive 0.8.11", "tinyvec", "uuid", ] +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rkyv_derive" version = "0.8.11" @@ -4502,6 +4622,12 @@ version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.11.1" @@ -5045,7 +5171,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4ee734c35b736e632aa3b1367f933d93ee7b4129dd1e20ca942205d4834054e" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "log", "qualifier_attr", @@ -5358,7 +5484,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e1d3b52b4a014efeaaab67f14e40af3972a4be61c523d612860db8e3145529" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "solana-epoch-schedule", "solana-hash", @@ -6590,7 +6716,7 @@ name = "solana-svm" version = "2.2.1" source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=3e9456ec4#3e9456ec4d5798ad8281537501c1e777d6888ba3" dependencies = [ - "ahash", + "ahash 0.8.12", "log", "percentage", "qualifier_attr", @@ -7033,7 +7159,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd1adc42def3cb101f3ebef3cd2d642f9a21072bbcd4ec9423343ccaa6afa596" dependencies = [ - "ahash", + "ahash 0.8.12", "bumpalo", "bytes", "cfg-if", @@ -7472,6 +7598,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "subtle" version = "2.6.1" @@ -7553,6 +7700,12 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "task-local-extensions" version = "0.1.4" @@ -8740,6 +8893,15 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 7a9b5caa4..17a1ad17d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ magicblock-committor-program = { path = "./magicblock-committor-program", featur magicblock-committor-service = { path = "./magicblock-committor-service" } magicblock-config = { path = "./magicblock-config" } magicblock-core = { path = "./magicblock-core" } -magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "aa1de56d90c", features = [ +magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "735640a", features = [ "no-entrypoint", ] } magicblock-ledger = { path = "./magicblock-ledger" } diff --git a/magicblock-committor-service/src/intent_executor/error.rs b/magicblock-committor-service/src/intent_executor/error.rs index 9484be711..1ecb45a2e 100644 --- a/magicblock-committor-service/src/intent_executor/error.rs +++ b/magicblock-committor-service/src/intent_executor/error.rs @@ -143,6 +143,8 @@ pub enum TransactionStrategyExecutionError { CommitIDError(#[source] TransactionError, Option), #[error("Max instruction trace length exceeded: {0}. {:?}", .1)] CpiLimitError(#[source] TransactionError, Option), + #[error("Unfinalized account error: {0}, {:?}", .1)] + UnfinalizedAccountError(#[source] TransactionError, Option), #[error("InternalError: {0}")] InternalError(#[from] InternalError), } @@ -154,16 +156,47 @@ impl From for TransactionStrategyExecutionError { } impl TransactionStrategyExecutionError { + /// Number of compute budget instructions prepended to every transaction. + /// Used to map instruction indices back to task indices. + const TASK_OFFSET: u8 = 2; + pub fn is_cpi_limit_error(&self) -> bool { matches!(self, Self::CpiLimitError(_, _)) } + pub fn task_index(&self) -> Option { + match self { + Self::CommitIDError( + TransactionError::InstructionError(index, _), + _, + ) + | Self::ActionsError( + TransactionError::InstructionError(index, _), + _, + ) + | Self::UndelegationError( + TransactionError::InstructionError(index, _), + _, + ) + | Self::UnfinalizedAccountError( + TransactionError::InstructionError(index, _), + _, + ) + | Self::CpiLimitError( + TransactionError::InstructionError(index, _), + _, + ) => index.checked_sub(Self::TASK_OFFSET), + _ => None, + } + } + pub fn signature(&self) -> Option { match self { Self::InternalError(err) => err.signature(), Self::CommitIDError(_, signature) | Self::ActionsError(_, signature) | Self::UndelegationError(_, signature) + | Self::UnfinalizedAccountError(_, signature) | Self::CpiLimitError(_, signature) => *signature, } } @@ -176,10 +209,18 @@ impl TransactionStrategyExecutionError { signature: Option, tasks: &[Box], ) -> Result { - // There's always 2 budget instructions in front - const OFFSET: u8 = 2; + // Commit Nonce order error const NONCE_OUT_OF_ORDER: u32 = dlp::error::DlpError::NonceOutOfOrder as u32; + // Errors when commit state already exists + const COMMIT_STATE_INVALID_ACCOUNT_OWNER: u32 = + dlp::error::DlpError::CommitStateInvalidAccountOwner as u32; + const COMMIT_STATE_ALREADY_INITIALIZED: u32 = + dlp::error::DlpError::CommitStateAlreadyInitialized as u32; + const COMMIT_RECORD_INVALID_ACCOUNT_OWNER: u32 = + dlp::error::DlpError::CommitRecordInvalidAccountOwner as u32; + const COMMIT_RECORD_ALREADY_INITIALIZED: u32 = + dlp::error::DlpError::CommitRecordAlreadyInitialized as u32; match err { // Some tx may use too much CPIs and we can handle it in certain cases @@ -195,7 +236,8 @@ impl TransactionStrategyExecutionError { let tx_err_helper = |instruction_err| -> TransactionError { TransactionError::InstructionError(index, instruction_err) }; - let Some(action_index) = index.checked_sub(OFFSET) else { + let Some(action_index) = index.checked_sub(Self::TASK_OFFSET) + else { return Err(tx_err_helper(instruction_err)); }; @@ -207,15 +249,35 @@ impl TransactionStrategyExecutionError { }; 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, - )), + (TaskType::Commit, instruction_err) => match instruction_err + { + InstructionError::Custom(NONCE_OUT_OF_ORDER) => Ok( + TransactionStrategyExecutionError::CommitIDError( + tx_err_helper(InstructionError::Custom( + NONCE_OUT_OF_ORDER, + )), + signature, + ), + ), + instruction_err @ (InstructionError::Custom( + COMMIT_STATE_INVALID_ACCOUNT_OWNER, + ) + | InstructionError::Custom( + COMMIT_STATE_ALREADY_INITIALIZED, + ) + | InstructionError::Custom( + COMMIT_RECORD_INVALID_ACCOUNT_OWNER, + ) + | InstructionError::Custom( + COMMIT_RECORD_ALREADY_INITIALIZED, + )) => { + Ok(TransactionStrategyExecutionError::UnfinalizedAccountError( + tx_err_helper(instruction_err), + signature + )) + } + err => Err(tx_err_helper(err)), + }, (TaskType::Action, instruction_err) => { Ok(TransactionStrategyExecutionError::ActionsError( tx_err_helper(instruction_err), 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 44edac30f..8142ffcc0 100644 --- a/magicblock-committor-service/src/intent_executor/single_stage_executor.rs +++ b/magicblock-committor-service/src/intent_executor/single_stage_executor.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use log::error; use solana_pubkey::Pubkey; +use solana_signature::Signature; use crate::{ intent_executor::{ @@ -12,8 +13,13 @@ use crate::{ task_info_fetcher::TaskInfoFetcher, ExecutionOutput, IntentExecutorImpl, }, - persist::IntentPersister, - tasks::task_strategist::TransactionStrategy, + persist::{IntentPersister, IntentPersisterImpl}, + tasks::{ + args_task::{ArgsTask, ArgsTaskType}, + task_strategist::TransactionStrategy, + task_visitors::utility_visitor::TaskVisitorUtils, + BaseTask, FinalizeTask, + }, transaction_preparator::TransactionPreparator, }; @@ -139,6 +145,27 @@ where .await?; Ok(ControlFlow::Continue(to_cleanup)) } + err + @ TransactionStrategyExecutionError::UnfinalizedAccountError( + _, + signature, + ) => { + if let Some(task) = err.task_index().and_then(|index| { + transaction_strategy + .optimized_tasks + .as_slice() + .get(index as usize) + }) { + Self::handle_unfinalized_account_error( + inner, + signature, + task.as_ref(), + ) + .await + } else { + Ok(ControlFlow::Break(())) + } + } 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 @@ -157,4 +184,44 @@ where } } } + + /// Handles unfinalized account error + /// Sends a separate tx to finalize account and then continues execution + async fn handle_unfinalized_account_error( + inner: &IntentExecutorImpl, + failed_signature: &Option, + task: &dyn BaseTask, + ) -> IntentExecutorResult> { + let mut visitor = TaskVisitorUtils::GetCommitMeta(None); + task.visit(&mut visitor); + + let TaskVisitorUtils::GetCommitMeta(Some(commit_meta)) = visitor else { + // Can't recover - break execution + return Ok(ControlFlow::Break(())); + }; + let finalize_task = + Box::new(ArgsTask::new(ArgsTaskType::Finalize(FinalizeTask { + delegated_account: commit_meta.committed_pubkey, + }))) as Box; + inner + .prepare_and_execute_strategy( + &mut TransactionStrategy { + optimized_tasks: vec![finalize_task], + lookup_tables_keys: vec![], + }, + &None::, + ) + .await + .map_err(IntentExecutorError::FailedFinalizePreparationError)? + .map_err(|err| IntentExecutorError::FailedToFinalizeError { + err, + commit_signature: None, + finalize_signature: *failed_signature, + })?; + + Ok(ControlFlow::Continue(TransactionStrategy { + optimized_tasks: vec![], + lookup_tables_keys: vec![], + })) + } } 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 30d462096..3b25ae364 100644 --- a/magicblock-committor-service/src/intent_executor/two_stage_executor.rs +++ b/magicblock-committor-service/src/intent_executor/two_stage_executor.rs @@ -14,8 +14,13 @@ use crate::{ two_stage_executor::sealed::Sealed, IntentExecutorImpl, }, - persist::IntentPersister, - tasks::task_strategist::TransactionStrategy, + persist::{IntentPersister, IntentPersisterImpl}, + tasks::{ + args_task::{ArgsTask, ArgsTaskType}, + task_strategist::TransactionStrategy, + task_visitors::utility_visitor::TaskVisitorUtils, + BaseTask, FinalizeTask, + }, transaction_preparator::TransactionPreparator, }; @@ -150,6 +155,27 @@ where .await?; Ok(ControlFlow::Continue(to_cleanup)) } + err + @ TransactionStrategyExecutionError::UnfinalizedAccountError( + _, + signature, + ) => { + if let Some(task) = err.task_index().and_then(|index| { + commit_strategy + .optimized_tasks + .as_slice() + .get(index as usize) + }) { + Self::handle_unfinalized_account_error( + inner, + signature, + task.as_ref(), + ) + .await + } else { + Ok(ControlFlow::Break(())) + } + } TransactionStrategyExecutionError::ActionsError(_, _) => { // Unexpected in Two Stage commit // That would mean that Two Stage executes Standalone commit @@ -173,6 +199,45 @@ where } } } + + /// Handles unfinalized account error + /// Sends a separate tx to finalize account and then continues execution + async fn handle_unfinalized_account_error( + inner: &IntentExecutorImpl, + failed_signature: &Option, + task: &dyn BaseTask, + ) -> IntentExecutorResult> { + let mut visitor = TaskVisitorUtils::GetCommitMeta(None); + task.visit(&mut visitor); + + let TaskVisitorUtils::GetCommitMeta(Some(commit_meta)) = visitor else { + // Can't recover - break execution + return Ok(ControlFlow::Break(())); + }; + let finalize_task = + Box::new(ArgsTask::new(ArgsTaskType::Finalize(FinalizeTask { + delegated_account: commit_meta.committed_pubkey, + }))) as Box; + inner + .prepare_and_execute_strategy( + &mut TransactionStrategy { + optimized_tasks: vec![finalize_task], + lookup_tables_keys: vec![], + }, + &None::, + ) + .await + .map_err(IntentExecutorError::FailedCommitPreparationError)? + .map_err(|err| IntentExecutorError::FailedToCommitError { + err, + signature: *failed_signature, + })?; + + Ok(ControlFlow::Continue(TransactionStrategy { + optimized_tasks: vec![], + lookup_tables_keys: vec![], + })) + } } impl<'a, T, F> TwoStageExecutor<'a, T, F, Committed> @@ -261,7 +326,11 @@ where finalize_strategy: &mut TransactionStrategy, ) -> IntentExecutorResult> { match err { - TransactionStrategyExecutionError::CommitIDError(_, _) => { + TransactionStrategyExecutionError::CommitIDError(_, _) + | TransactionStrategyExecutionError::UnfinalizedAccountError( + _, + _, + ) => { // Unexpected error in Two Stage commit error!("Unexpected error in two stage finalize flow: {}", err); Ok(ControlFlow::Break(())) diff --git a/test-integration/Cargo.lock b/test-integration/Cargo.lock index 65bb664be..233d2b2e2 100644 --- a/test-integration/Cargo.lock +++ b/test-integration/Cargo.lock @@ -79,6 +79,17 @@ dependencies = [ "solana-svm-transaction", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -573,6 +584,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.8.2" @@ -719,6 +742,28 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta 0.1.4", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.23.1" @@ -1523,7 +1568,7 @@ dependencies = [ "ephemeral-rollups-sdk-attribute-commit", "ephemeral-rollups-sdk-attribute-delegate", "ephemeral-rollups-sdk-attribute-ephemeral", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.0", "magicblock-magic-program-api 0.2.1", "solana-program", ] @@ -1637,7 +1682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6503af7917fea18ffef8f7e8553fb8dff89e2e6837e94e09dd7fb069c82d62c" dependencies = [ "bytes", - "rkyv", + "rkyv 0.8.11", "serde", "simdutf8", ] @@ -1821,6 +1866,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -2090,6 +2141,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -2097,7 +2151,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -2600,7 +2654,7 @@ dependencies = [ "log", "magicblock-config", "magicblock-core", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "random-port", "rayon", "serde", @@ -3160,7 +3214,7 @@ dependencies = [ "lru", "magicblock-config", "magicblock-core", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-magic-program-api 0.4.1", "magicblock-metrics", "solana-account", @@ -3219,7 +3273,7 @@ dependencies = [ "log", "lru", "magicblock-committor-program", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-metrics", "magicblock-program", "magicblock-rpc-client", @@ -3305,6 +3359,28 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "magicblock-delegation-program" +version = "1.1.3" +source = "git+https://github.com/magicblock-labs/delegation-program.git?rev=735640a#735640a1260f5bf77d907b5fd46ca50dd162afbd" +dependencies = [ + "bincode", + "borsh 1.5.7", + "bytemuck", + "num_enum", + "pinocchio", + "pinocchio-log", + "pinocchio-pubkey", + "pinocchio-system", + "rkyv 0.7.45", + "solana-curve25519", + "solana-program", + "solana-security-txt", + "static_assertions", + "strum 0.27.2", + "thiserror 1.0.69", +] + [[package]] name = "magicblock-ledger" version = "0.4.1" @@ -3522,7 +3598,7 @@ name = "magicblock-validator-admin" version = "0.4.1" dependencies = [ "log", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-program", "magicblock-rpc-client", "solana-commitment-config", @@ -4395,7 +4471,7 @@ version = "0.0.0" dependencies = [ "borsh 1.5.7", "ephemeral-rollups-sdk", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-magic-program-api 0.4.1", "solana-program", ] @@ -4494,13 +4570,33 @@ dependencies = [ "autotools", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive 0.1.4", +] + [[package]] name = "ptr_meta" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" dependencies = [ - "ptr_meta_derive", + "ptr_meta_derive 0.3.0", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -4621,13 +4717,19 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rancor" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" dependencies = [ - "ptr_meta", + "ptr_meta 0.3.0", ] [[package]] @@ -4849,6 +4951,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "rend" version = "0.5.2" @@ -4928,6 +5039,24 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta 0.1.4", + "rend 0.4.2", + "rkyv_derive 0.7.45", + "seahash", + "tinyvec", + "uuid", +] + [[package]] name = "rkyv" version = "0.8.11" @@ -4938,14 +5067,25 @@ dependencies = [ "hashbrown 0.15.4", "indexmap 2.10.0", "munge", - "ptr_meta", + "ptr_meta 0.3.0", "rancor", - "rend", - "rkyv_derive", + "rend 0.5.2", + "rkyv_derive 0.8.11", "tinyvec", "uuid", ] +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rkyv_derive" version = "0.8.11" @@ -5205,7 +5345,7 @@ dependencies = [ "integration-test-tools", "log", "magicblock-core", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "program-schedulecommit", "solana-program", "solana-rpc-client", @@ -5223,7 +5363,7 @@ dependencies = [ "log", "magicblock-committor-program", "magicblock-committor-service", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-program", "magicblock-rpc-client", "magicblock-table-mania", @@ -5316,6 +5456,12 @@ version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "3.2.0" @@ -5627,7 +5773,7 @@ dependencies = [ [[package]] name = "solana-account" version = "2.2.1" -source = "git+https://github.com/magicblock-labs/solana-account.git?rev=1beed4c#1beed4c0bc368bc0bed819fbdf551d1a4bf00001" +source = "git+https://github.com/magicblock-labs/solana-account.git?rev=57158728#571587284a525b66666291aee5174783139a1595" dependencies = [ "bincode", "qualifier_attr", @@ -5716,7 +5862,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65a1a23a53cae19cb92bab2cbdd9e289e5210bb12175ce27642c94adf74b220" dependencies = [ - "ahash", + "ahash 0.8.12", "bincode", "blake3", "bv", @@ -6017,7 +6163,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4ee734c35b736e632aa3b1367f933d93ee7b4129dd1e20ca942205d4834054e" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "log", "qualifier_attr", @@ -6243,7 +6389,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a675ead1473b32a7a5735801608b35cbd8d3f5057ca8dbafdd5976146bb7e9e4" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "log", "solana-bincode", @@ -6429,7 +6575,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e1d3b52b4a014efeaaab67f14e40af3972a4be61c523d612860db8e3145529" dependencies = [ - "ahash", + "ahash 0.8.12", "lazy_static", "solana-epoch-schedule", "solana-hash", @@ -6908,7 +7054,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f0962d3818fc942a888f7c2d530896aeaf6f2da2187592a67bbdc8cf8a54192" dependencies = [ - "ahash", + "ahash 0.8.12", "bincode", "bv", "caps", @@ -7463,7 +7609,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5335e7925f6dc8d2fdcdc6ead3b190aca65f191a11cef74709a7a6ab5d0d5877" dependencies = [ - "ahash", + "ahash 0.8.12", "aquamarine", "arrayref", "base64 0.22.1", @@ -7534,8 +7680,8 @@ dependencies = [ "solana-vote", "solana-vote-program", "static_assertions", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "symlink", "tar", "tempfile", @@ -8015,7 +8161,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0850baf834aba4a94a7558fa6cf6ca93fad284abf0363dec5fb9cab173a11fc4" dependencies = [ - "ahash", + "ahash 0.8.12", "itertools 0.12.1", "log", "percentage", @@ -8059,7 +8205,7 @@ name = "solana-svm" version = "2.2.1" source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=3e9456ec4#3e9456ec4d5798ad8281537501c1e777d6888ba3" dependencies = [ - "ahash", + "ahash 0.8.12", "log", "percentage", "qualifier_attr", @@ -8718,7 +8864,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd1adc42def3cb101f3ebef3cd2d642f9a21072bbcd4ec9423343ccaa6afa596" dependencies = [ - "ahash", + "ahash 0.8.12", "bumpalo", "bytes", "cfg-if", @@ -9152,7 +9298,16 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros", + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", ] [[package]] @@ -9168,6 +9323,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "subtle" version = "2.6.1" @@ -9259,6 +9426,12 @@ dependencies = [ "solana-program", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" version = "0.4.44" @@ -9358,7 +9531,7 @@ dependencies = [ "log", "magicblock-chainlink", "magicblock-config", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "program-flexi-counter", "program-mini", "solana-account", @@ -9440,7 +9613,7 @@ dependencies = [ "log", "magicblock-accounts-db", "magicblock-config", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "program-flexi-counter", "solana-rpc-client", "solana-sdk", @@ -9461,7 +9634,7 @@ dependencies = [ "magic-domain-program", "magicblock-api", "magicblock-config", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "magicblock-program", "magicblock-validator-admin", "solana-rpc-client", @@ -9501,7 +9674,7 @@ version = "0.0.0" dependencies = [ "integration-test-tools", "log", - "magicblock-delegation-program", + "magicblock-delegation-program 1.1.3", "program-flexi-counter", "solana-rpc-client-api", "solana-sdk", @@ -10740,6 +10913,15 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-parser" version = "0.14.0" diff --git a/test-integration/Cargo.toml b/test-integration/Cargo.toml index 6dd2ea45e..52c359fad 100644 --- a/test-integration/Cargo.toml +++ b/test-integration/Cargo.toml @@ -57,7 +57,7 @@ magicblock-config = { path = "../magicblock-config" } magicblock-core = { path = "../magicblock-core" } magic-domain-program = { git = "https://github.com/magicblock-labs/magic-domain-program.git", rev = "ea04d46", default-features = false } magicblock_magic_program_api = { package = "magicblock-magic-program-api", path = "../magicblock-magic-program-api" } -magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "aa1de56d90c", features = [ +magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "735640a", features = [ "no-entrypoint", ] } magicblock-program = { path = "../programs/magicblock" } diff --git a/test-integration/schedulecommit/elfs/dlp.so b/test-integration/schedulecommit/elfs/dlp.so index f07df31f3..534ccf787 100755 Binary files a/test-integration/schedulecommit/elfs/dlp.so and b/test-integration/schedulecommit/elfs/dlp.so differ 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 912668019..33741ddff 100644 --- a/test-integration/test-committor-service/tests/test_intent_executor.rs +++ b/test-integration/test-committor-service/tests/test_intent_executor.rs @@ -10,7 +10,7 @@ use std::{ }; use borsh::to_vec; -use dlp::pda::ephemeral_balance_pda_from_payer; +use dlp::{args::CommitStateArgs, pda::ephemeral_balance_pda_from_payer}; use futures::future::join_all; use magicblock_committor_program::pdas; use magicblock_committor_service::{ @@ -35,6 +35,7 @@ use magicblock_program::{ }, validator::validator_authority_id, }; +use magicblock_rpc_client::MagicBlockSendTransactionConfig; use magicblock_table_mania::TableMania; use program_flexi_counter::{ instruction::FlexiCounterInstruction, @@ -824,6 +825,171 @@ async fn test_commit_id_actions_cpi_limit_errors_recovery() { .await; } +#[tokio::test] +async fn test_commit_unfinalized_account_recovery() { + let TestEnv { + fixture, + mut intent_executor, + task_info_fetcher: _, + pre_test_tablemania_state: _, + } = TestEnv::setup().await; + + // 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 (counter_auth, account) = setup_counter(40, None).await; + setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) + .await; + let pda = FlexiCounter::pda(&counter_auth.pubkey()).0; + + // Commit account without finalization + // This simulates finalization stage failure + { + let commit_allow_undelegation_ix = + dlp::instruction_builder::commit_state( + fixture.authority.pubkey(), + pda, + account.owner, + CommitStateArgs { + nonce: 1, + lamports: account.lamports, + allow_undelegation: false, + data: account.data.clone(), + }, + ); + + let blockhash = + fixture.rpc_client.get_latest_blockhash().await.unwrap(); + let tx = Transaction::new_signed_with_payer( + &[commit_allow_undelegation_ix], + Some(&fixture.authority.pubkey()), + &[&fixture.authority], + blockhash, + ); + + let result = fixture + .rpc_client + .send_transaction( + &tx, + &MagicBlockSendTransactionConfig::ensure_committed(), + ) + .await; + assert!(result.is_ok()); + } + + // Now simulate user sending new intent + let committed_account = CommittedAccount { + pubkey: pda, + account, + }; + let intent = create_intent(vec![committed_account], false); + let result = intent_executor + .execute(intent, None::) + .await; + assert!(result.inner.is_ok()); + assert!(matches!( + result.inner.unwrap(), + ExecutionOutput::SingleStage(_) + )); + + assert_eq!(result.patched_errors.len(), 2); + assert!(matches!( + result.patched_errors[0], + TransactionStrategyExecutionError::UnfinalizedAccountError(_, _) + )); + assert!(matches!( + result.patched_errors[1], + TransactionStrategyExecutionError::CommitIDError(_, _) + )) +} + +#[tokio::test] +async fn test_commit_unfinalized_account_recovery_two_stage() { + let TestEnv { + fixture, + mut intent_executor, + task_info_fetcher: _, + pre_test_tablemania_state: _, + } = TestEnv::setup().await; + + // 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..8).map(async |_| { + let (counter_auth, account) = setup_counter(40, None).await; + setup_payer_with_keypair(&counter_auth, fixture.rpc_client.get_inner()) + .await; + let pda = FlexiCounter::pda(&counter_auth.pubkey()).0; + (counter_auth, pda, account) + }); + let counters: Vec<(_, _, _)> = join_all(counters).await; + + // Commit account without finalization + // This simulates finalization stage failure + { + let commit_allow_undelegation_ix = + dlp::instruction_builder::commit_state( + fixture.authority.pubkey(), + counters[0].1, + counters[0].2.owner, + CommitStateArgs { + nonce: 1, + lamports: counters[0].2.lamports, + allow_undelegation: false, + data: counters[0].2.data.clone(), + }, + ); + + let blockhash = + fixture.rpc_client.get_latest_blockhash().await.unwrap(); + let tx = Transaction::new_signed_with_payer( + &[commit_allow_undelegation_ix], + Some(&fixture.authority.pubkey()), + &[&fixture.authority], + blockhash, + ); + + let result = fixture + .rpc_client + .send_transaction( + &tx, + &MagicBlockSendTransactionConfig::ensure_committed(), + ) + .await; + assert!(result.is_ok()); + } + + // Now simulate user sending new intent + let committed_accounts = counters + .into_iter() + .map(|el| CommittedAccount { + pubkey: el.1, + account: el.2, + }) + .collect(); + let intent = create_intent(committed_accounts, true); + + let result = intent_executor + .execute(intent, None::) + .await; + assert!(result.inner.is_ok()); + assert!(matches!( + result.inner.unwrap(), + ExecutionOutput::TwoStage { + commit_signature: _, + finalize_signature: _ + } + )); + + assert_eq!(result.patched_errors.len(), 2); + assert!(matches!( + result.patched_errors[0], + TransactionStrategyExecutionError::UnfinalizedAccountError(_, _) + )); + assert!(matches!( + result.patched_errors[1], + TransactionStrategyExecutionError::CommitIDError(_, _) + )) +} + fn failing_undelegate_action( escrow_authority: Pubkey, undelegated_account: Pubkey,