Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
acd1621
wip: simplified version of v2 intents reusing old types
taco-paco Jan 19, 2026
4161ecb
feat: integrate new intent version in magic program
taco-paco Jan 19, 2026
c39d826
fix: magic-program compilation
taco-paco Jan 19, 2026
423658f
refactor: rename ScheduledBaseIntent -> ScheduledIntentBundle
taco-paco Jan 19, 2026
b5524d3
refactor: ranamings
taco-paco Jan 19, 2026
90aa5fb
refactor: renamings
taco-paco Jan 19, 2026
4a7b02a
feat: integrate IntentBundle into Persistor
taco-paco Jan 19, 2026
26e2cca
fix: commit tasks building
taco-paco Jan 20, 2026
c9fe2c0
refactor: commit tasks builder
taco-paco Jan 20, 2026
f2b4974
feat: update finalize_tasks creation
taco-paco Jan 20, 2026
d6d8c94
feat: enforce uniqueness of committed account across bundle
taco-paco Jan 20, 2026
739dd0b
feat: dedup committed account in magic program
taco-paco Jan 20, 2026
fdbf0e6
wip: fix some compilation errors
taco-paco Jan 20, 2026
6b5897a
refactor: remove bank from ScheduledCommitsProcessor
taco-paco Jan 20, 2026
d1df7d6
wip
taco-paco Jan 20, 2026
a3f4257
fix: unit tests
taco-paco Jan 20, 2026
c580c37
feat: add new tests for scheduler
taco-paco Jan 20, 2026
2973cee
feat: add intent bundle test into IntentExecutionEngine
taco-paco Jan 20, 2026
86a2237
fix: integration tests compilation
taco-paco Jan 20, 2026
a9973c5
feat: add integration tests
taco-paco Jan 20, 2026
99b4640
feat: remove ScheduleIntentBundleWrapper
taco-paco Jan 20, 2026
c9f86c4
fix: lint
taco-paco Jan 20, 2026
d9e067c
Merge branch 'master' into feat/intents/v2-simplified
taco-paco Jan 21, 2026
c226198
fix: code rabbit comments
taco-paco Jan 21, 2026
1d233a5
fix: sdk version
taco-paco Jan 21, 2026
2bf9cd9
feat: claude code added tests for me :) Not working tho
taco-paco Jan 21, 2026
6463066
fix: test
taco-paco Jan 21, 2026
4767956
fix: tests
taco-paco Jan 21, 2026
ec4fad9
fix: bug
taco-paco Jan 21, 2026
747f518
fix: coderabbit coments
taco-paco Jan 22, 2026
e8837ca
feat: update sdk revision
taco-paco Jan 22, 2026
0cf1c0b
Update test-integration/test-schedule-intent/tests/test_schedule_inte…
GabrielePicco Jan 22, 2026
078edaa
Merge branch 'master' into feat/intents/v2-simplified
GabrielePicco Jan 22, 2026
ce01ce5
fix: address comments
taco-paco Jan 25, 2026
05d3e0a
Merge remote-tracking branch 'origin/feat/intents/v2-simplified' into…
taco-paco Jan 25, 2026
673d948
Merge branch 'master' into feat/intents/v2-simplified
taco-paco Jan 25, 2026
348783e
fix: fmt
taco-paco Jan 25, 2026
4d8cded
fix: backwards compatible ix
taco-paco Jan 25, 2026
30bfa72
fix: pass --ignore-rust-version to build-sbf for bincode 2 MSRV
taco-paco Jan 27, 2026
9495c99
feat: added todo
taco-paco Jan 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 75 additions & 41 deletions Cargo.lock

Large diffs are not rendered by default.

124 changes: 26 additions & 98 deletions magicblock-accounts/src/scheduled_commits_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{

use async_trait::async_trait;
use magicblock_account_cloner::ChainlinkCloner;
use magicblock_accounts_db::{traits::AccountsBank, AccountsDb};
use magicblock_accounts_db::AccountsDb;
use magicblock_chainlink::{
remote_account_provider::{
chain_rpc_client::ChainRpcClientImpl,
Expand All @@ -16,13 +16,11 @@ use magicblock_chainlink::{
};
use magicblock_committor_service::{
intent_execution_manager::BroadcastedIntentExecutionResult,
intent_executor::ExecutionOutput,
types::{ScheduledBaseIntentWrapper, TriggerType},
BaseIntentCommittor, CommittorService,
intent_executor::ExecutionOutput, BaseIntentCommittor, CommittorService,
};
use magicblock_core::link::transactions::TransactionSchedulerHandle;
use magicblock_program::{
magic_scheduled_base_intent::ScheduledBaseIntent,
magic_scheduled_base_intent::ScheduledIntentBundle,
register_scheduled_commit_sent, SentCommit, TransactionScheduler,
};
use solana_hash::Hash;
Expand Down Expand Up @@ -50,7 +48,6 @@ pub type ChainlinkImpl = Chainlink<
>;

pub struct ScheduledCommitsProcessorImpl {
accounts_bank: Arc<AccountsDb>,
committor: Arc<CommittorService>,
chainlink: Arc<ChainlinkImpl>,
cancellation_token: CancellationToken,
Expand All @@ -60,7 +57,6 @@ pub struct ScheduledCommitsProcessorImpl {

impl ScheduledCommitsProcessorImpl {
pub fn new(
accounts_bank: Arc<AccountsDb>,
committor: Arc<CommittorService>,
chainlink: Arc<ChainlinkImpl>,
internal_transaction_scheduler: TransactionSchedulerHandle,
Expand All @@ -76,7 +72,6 @@ impl ScheduledCommitsProcessorImpl {
));

Self {
accounts_bank,
committor,
chainlink,
cancellation_token,
Expand All @@ -85,55 +80,6 @@ impl ScheduledCommitsProcessorImpl {
}
}

fn preprocess_intent(
&self,
mut base_intent: ScheduledBaseIntent,
) -> (ScheduledBaseIntentWrapper, Vec<Pubkey>) {
let is_undelegate = base_intent.is_undelegate();
let Some(committed_accounts) = base_intent.get_committed_accounts_mut()
else {
let intent = ScheduledBaseIntentWrapper {
inner: base_intent,
trigger_type: TriggerType::OnChain,
};
return (intent, vec![]);
};

// Filter duplicate accounts
let mut seen = HashSet::with_capacity(committed_accounts.len());
committed_accounts.retain(|account| seen.insert(account.pubkey));

// dump undelegated pubkeys
let pubkeys_being_undelegated: Vec<_> = committed_accounts
.iter()
.inspect(|account| {
let pubkey = account.pubkey;
if self.accounts_bank.get_account(&pubkey).is_none() {
// This doesn't affect intent validity
// We assume that intent is correct at the moment of scheduling
// All the checks are performed by runtime & magic-program at the moment of scheduling
// This log could be a sign of eviction or a bug in implementation
info!("Account got evicted from AccountsDB after intent was scheduled!");
}
})
.filter_map(|account| {
if is_undelegate {
Some(account.pubkey)
} else {
None
}
})
.collect();

let intent = ScheduledBaseIntentWrapper {
inner: base_intent,
trigger_type: TriggerType::OnChain,
};

(intent, pubkeys_being_undelegated)
}

#[instrument(skip(self))]
async fn process_undelegation_requests(&self, pubkeys: Vec<Pubkey>) {
let mut join_set = task::JoinSet::new();
for pubkey in pubkeys.into_iter() {
Expand Down Expand Up @@ -192,7 +138,7 @@ impl ScheduledCommitsProcessorImpl {
let execution_result = tokio::select! {
biased;
_ = cancellation_token.cancelled() => {
info!("Shutting down result processor");
info!("Shutting down");
return;
}
execution_result = result_receiver.recv() => {
Expand All @@ -213,14 +159,6 @@ impl ScheduledCommitsProcessorImpl {
};

let intent_id = execution_result.id;
let trigger_type = execution_result.trigger_type;
// Here we handle on OnChain triggered intent
// TODO: should be removed once crank supported
if matches!(trigger_type, TriggerType::OffChain) {
info!(intent_id, trigger_type = ?trigger_type, "intent executed");
continue;
}

// Remove intent from metas
let intent_meta = if let Some(intent_meta) = intents_meta_map
.lock()
Expand Down Expand Up @@ -331,44 +269,36 @@ impl ScheduledCommitsProcessorImpl {
impl ScheduledCommitsProcessor for ScheduledCommitsProcessorImpl {
#[instrument(skip(self))]
async fn process(&self) -> ScheduledCommitsProcessorResult<()> {
let scheduled_base_intents =
self.transaction_scheduler.take_scheduled_actions();
let intent_bundles =
self.transaction_scheduler.take_scheduled_intent_bundles();

if scheduled_base_intents.is_empty() {
if intent_bundles.is_empty() {
return Ok(());
}

let intents = scheduled_base_intents
.into_iter()
.map(|intent| self.preprocess_intent(intent));

// Add metas for intent we schedule
let (intents, pubkeys_being_undelegated) = {
let pubkeys_being_undelegated = {
let mut intent_metas =
self.intents_meta_map.lock().expect(POISONED_MUTEX_MSG);
let mut pubkeys_being_undelegated = HashSet::new();

let intents = intents
.map(|(intent, undelegated)| {
intent_metas.insert(
intent.id,
ScheduledBaseIntentMeta::new(&intent),
);
pubkeys_being_undelegated.extend(undelegated);

intent
})
.collect::<Vec<_>>();
let mut pubkeys_being_undelegated = HashSet::<Pubkey>::new();

intent_bundles.iter().for_each(|intent| {
intent_metas
.insert(intent.id, ScheduledBaseIntentMeta::new(intent));
if let Some(undelegate) = intent.get_undelegate_intent_pubkeys()
{
pubkeys_being_undelegated.extend(undelegate);
}
});

(
intents,
pubkeys_being_undelegated.into_iter().collect::<Vec<_>>(),
)
pubkeys_being_undelegated.into_iter().collect::<Vec<_>>()
};

self.process_undelegation_requests(pubkeys_being_undelegated)
.await;
self.committor.schedule_base_intent(intents).await??;
self.committor
.schedule_intent_bundles(intent_bundles)
.await??;
Ok(())
}

Expand All @@ -395,16 +325,14 @@ struct ScheduledBaseIntentMeta {
}

impl ScheduledBaseIntentMeta {
fn new(intent: &ScheduledBaseIntent) -> Self {
fn new(intent: &ScheduledIntentBundle) -> Self {
Self {
slot: intent.slot,
blockhash: intent.blockhash,
payer: intent.payer,
included_pubkeys: intent
.get_committed_pubkeys()
.unwrap_or_default(),
intent_sent_transaction: intent.action_sent_transaction.clone(),
requested_undelegation: intent.is_undelegate(),
included_pubkeys: intent.get_all_committed_pubkeys(),
intent_sent_transaction: intent.sent_transaction.clone(),
requested_undelegation: intent.has_undelegate_intent(),
}
}
}
1 change: 0 additions & 1 deletion magicblock-api/src/magic_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ impl MagicValidator {
let scheduled_commits_processor =
committor_service.as_ref().map(|committor_service| {
Arc::new(ScheduledCommitsProcessorImpl::new(
accountsdb.clone(),
committor_service.clone(),
chainlink.clone(),
dispatch.transaction_scheduler.clone(),
Expand Down
16 changes: 6 additions & 10 deletions magicblock-committor-service/src/committor_processor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashSet, path::Path, sync::Arc};

use magicblock_program::magic_scheduled_base_intent::ScheduledIntentBundle;
use magicblock_rpc_client::MagicblockRpcClient;
use magicblock_table_mania::{GarbageCollectorConfig, TableMania};
use solana_keypair::Keypair;
Expand All @@ -19,7 +20,6 @@ use crate::{
CommitStatusRow, IntentPersister, IntentPersisterImpl,
MessageSignatures,
},
types::ScheduledBaseIntentWrapper,
};

pub(crate) struct CommittorProcessor {
Expand Down Expand Up @@ -121,24 +121,20 @@ impl CommittorProcessor {
Ok(signatures)
}

#[instrument(skip(self, base_intents))]
pub async fn schedule_base_intents(
#[instrument(skip(self, intent_bundles))]
pub async fn schedule_intent_bundle(
&self,
base_intents: Vec<ScheduledBaseIntentWrapper>,
intent_bundles: Vec<ScheduledIntentBundle>,
) -> CommittorServiceResult<()> {
let intents = base_intents
.iter()
.map(|base_intent| base_intent.inner.clone())
.collect::<Vec<_>>();
if let Err(err) = self.persister.start_base_intents(&intents) {
if let Err(err) = self.persister.start_base_intents(&intent_bundles) {
// We will still try to perform the commits, but the fact that we cannot
// persist the intent is very serious and we should probably restart the
// valiator
error!(error = ?err, "DB EXCEPTION: Failed to persist changeset");
};

self.commits_scheduler
.schedule(base_intents)
.schedule(intent_bundles)
.await
.inspect_err(|err| {
error!(error = ?err, "Failed to schedule intent");
Expand Down
12 changes: 6 additions & 6 deletions magicblock-committor-service/src/intent_execution_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod intent_scheduler;
use std::sync::Arc;

pub use intent_execution_engine::BroadcastedIntentExecutionResult;
use magicblock_program::magic_scheduled_base_intent::ScheduledIntentBundle;
use magicblock_rpc_client::MagicblockRpcClient;
use magicblock_table_mania::TableMania;
use tokio::sync::{broadcast, mpsc, mpsc::error::TrySendError};
Expand All @@ -19,14 +20,13 @@ use crate::{
task_info_fetcher::CacheTaskInfoFetcher,
},
persist::IntentPersister,
types::ScheduledBaseIntentWrapper,
ComputeBudgetConfig,
};

pub struct IntentExecutionManager<D: DB> {
db: Arc<D>,
result_subscriber: ResultSubscriber,
intent_sender: mpsc::Sender<ScheduledBaseIntentWrapper>,
intent_sender: mpsc::Sender<ScheduledIntentBundle>,
}

impl<D: DB> IntentExecutionManager<D> {
Expand Down Expand Up @@ -69,18 +69,18 @@ impl<D: DB> IntentExecutionManager<D> {
/// Intents will be extracted and handled in the [`IntentExecutionEngine`]
pub async fn schedule(
&self,
base_intents: Vec<ScheduledBaseIntentWrapper>,
intent_bundles: Vec<ScheduledIntentBundle>,
) -> Result<(), IntentExecutionManagerError> {
// If db not empty push el-t there
// This means that at some point channel got full
// Worker first will clean-up channel, and then DB.
// Pushing into channel would break order of commits
if !self.db.is_empty() {
self.db.store_base_intents(base_intents).await?;
self.db.store_intent_bundles(intent_bundles).await?;
return Ok(());
}

for el in base_intents {
for el in intent_bundles {
let err = if let Err(err) = self.intent_sender.try_send(el) {
err
} else {
Expand All @@ -93,7 +93,7 @@ impl<D: DB> IntentExecutionManager<D> {
}
TrySendError::Full(el) => self
.db
.store_base_intent(el)
.store_intent_bundle(el)
.await
.map_err(IntentExecutionManagerError::from),
}?;
Expand Down
Loading