Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
815 changes: 614 additions & 201 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ 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 = "2cb491032f372", features = [
"no-entrypoint",
magicblock-delegation-program = { path = "../delegation-program", default-features = false, features = [
"sdk",
] }
Comment on lines +105 to 107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Local path dependency ../delegation-program will break CI and isolated checkouts.

path = "../delegation-program" references a sibling directory outside this repository. Any developer or CI runner without that exact directory layout will get a build failure. The previous git-based reference (with rev) was portable. This should either stay as a git dependency (possibly pointing to a local branch) or the delegation-program should be vendored/subtree'd into this repo before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` around lines 105 - 107, The dependency entry for
magicblock-delegation-program currently uses a local path ("path =
\"../delegation-program\"") which will break CI and isolated checkouts; change
it back to a VCS-based dependency (restore the previous git + rev or git +
tag/branch) or vendor/subtree the delegation-program into this repo and update
the Cargo.toml entry accordingly so CI can fetch it; locate and update the
magicblock-delegation-program dependency line in Cargo.toml to use the git URL
and fixed rev (or replace the path with the vendored crate name) instead of the
local path.

dlp-api = { package = "magicblock-delegation-program-api", path = "../delegation-program/dlp-api" }
magicblock-ledger = { path = "./magicblock-ledger" }
magicblock-magic-program-api = { path = "./magicblock-magic-program-api" }
magicblock-metrics = { path = "./magicblock-metrics" }
Expand Down
33 changes: 29 additions & 4 deletions magicblock-account-cloner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use magicblock_committor_service::{
BaseIntentCommittor, CommittorService,
};
use magicblock_config::config::ChainLinkConfig;
use magicblock_core::link::transactions::TransactionSchedulerHandle;
use magicblock_core::link::transactions::{
SanitizeableTransaction, TransactionSchedulerHandle,
};
use magicblock_ledger::LatestBlock;
use magicblock_magic_program_api::instruction::AccountModification;
use magicblock_program::{
Expand All @@ -38,7 +40,7 @@ use solana_sdk_ids::loader_v4;
use solana_signature::Signature;
use solana_signer::{Signer, SignerError};
use solana_sysvar::rent::Rent;
use solana_transaction::Transaction;
use solana_transaction::{sanitized::SanitizedTransaction, Transaction};
use tokio::sync::oneshot;
use tracing::*;

Expand Down Expand Up @@ -84,6 +86,15 @@ impl ChainlinkCloner {
Ok(sig)
}

async fn send_sanitized_transaction(
&self,
tx: SanitizedTransaction,
sig: Signature,
) -> ClonerResult<Signature> {
self.tx_scheduler.execute(tx).await?;
Ok(sig)
}

fn build_clone_message(request: &AccountCloneRequest) -> Option<String> {
if request.account.delegated() {
// Account is delegated to us
Expand Down Expand Up @@ -122,7 +133,7 @@ impl ChainlinkCloner {
message,
);
// Defined positive commit frequency means commits should be scheduled
let ixs = match request.commit_frequency_ms {
let mut ixs = match request.commit_frequency_ms {
// TODO(GabrielePicco): Hotfix. Do not schedule frequency commits until we impose limits.
// 1. Allow configuring a higher minimum.
// 2. Stop committing accounts if they have been committed more than X times,
Expand Down Expand Up @@ -162,6 +173,9 @@ impl ChainlinkCloner {
}
_ => vec![modify_ix],
};
if !request.delegation_actions.is_empty() {
ixs.extend(request.delegation_actions.clone());
}
Comment on lines +176 to +178
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

No transaction size guard before appending delegation_actions.

Appending arbitrary instructions from delegation_actions to the existing ixs slice can push the serialized Transaction past Solana's 1232-byte limit, which will cause a runtime send failure. There is no size check before Transaction::new_with_payer or send_transaction.

Consider computing the serialized size of ixs after extension and returning an error (or chunking into a separate transaction) when the limit would be exceeded.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-account-cloner/src/lib.rs` around lines 165 - 167, Before
extending ixs with request.delegation_actions, compute the serialized size that
would result (e.g., temporarily clone ixs, extend with
request.delegation_actions.clone(), serialize a Transaction built with those
instructions or compute serialized instruction bytes) and if it exceeds Solana's
1232-byte limit return an error or split the instructions into a separate
transaction; specifically update the block that currently does
ixs.extend(request.delegation_actions.clone()) to perform this size check prior
to calling Transaction::new_with_payer or send_transaction and either reject the
request with a clear error or chunk delegation_actions into additional
transactions so Transaction::new_with_payer/send_transaction never receives an
oversized transaction.

🧹 Nitpick | 🔵 Trivial

Unnecessary full-Vec clone; prefer extend_from_slice.

request.delegation_actions.clone() allocates a full Vec copy before iterating. Since request is already a reference, extend_from_slice avoids the intermediate allocation.

♻️ Proposed refactor
-        if !request.delegation_actions.is_empty() {
-            ixs.extend(request.delegation_actions.clone());
-        }
+        ixs.extend_from_slice(&request.delegation_actions);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !request.delegation_actions.is_empty() {
ixs.extend(request.delegation_actions.clone());
}
ixs.extend_from_slice(&request.delegation_actions);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@magicblock-account-cloner/src/lib.rs` around lines 165 - 167, The code
currently clones the entire Vec with request.delegation_actions.clone() before
extending ixs, which causes an unnecessary allocation; change the extension to
use ixs.extend_from_slice(&request.delegation_actions) (or
ixs.extend(request.delegation_actions.iter().cloned()) if types differ) so you
avoid the intermediate Vec allocation—update the block that checks
request.delegation_actions and replace the clone-based extend with
extend_from_slice referencing ixs and request.delegation_actions.


let mut tx =
Transaction::new_with_payer(&ixs, Some(&validator_authority_id()));
Expand Down Expand Up @@ -407,13 +421,24 @@ impl Cloner for ChainlinkCloner {
let recent_blockhash = self.block.load().blockhash;
let tx = self
.transaction_to_clone_regular_account(&request, recent_blockhash)?;
let bypass_sigverify_for_replay_actions =
!request.delegation_actions.is_empty();
let sig = tx.signatures[0];

let send_res = if bypass_sigverify_for_replay_actions {
let sanitized_tx = tx.sanitize(false)?;
self.send_sanitized_transaction(sanitized_tx, sig).await
} else {
self.send_transaction(tx).await
};

if request.account.delegated() {
self.maybe_prepare_lookup_tables(
request.pubkey,
*request.account.owner(),
);
}
self.send_transaction(tx).await.map_err(|err| {
send_res.map_err(|err| {
ClonerError::FailedToCloneRegularAccount(
request.pubkey,
Box::new(err),
Expand Down
2 changes: 1 addition & 1 deletion magicblock-accounts/src/scheduled_commits_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl ScheduledCommitsProcessorImpl {
.map(|finalize| vec![commit, finalize])
.unwrap_or(vec![commit])
})
.unwrap_or(vec![])
.unwrap_or_default()
}
};
let patched_errors = result
Expand Down
2 changes: 1 addition & 1 deletion magicblock-api/src/magic_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ impl MagicValidator {
commitment_config,
&accounts_bank,
&cloner,
config.validator.keypair.pubkey(),
config.validator.keypair.insecure_clone(),
faucet_pubkey,
chainlink_config,
&config.chainlink,
Expand Down
2 changes: 2 additions & 0 deletions magicblock-chainlink/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition.workspace = true
arc-swap = "1.7"
async-trait = { workspace = true }
bincode = { workspace = true }
borsh = { workspace = true }
futures-util = { workspace = true }
helius-laserstream = { workspace = true }
tracing = { workspace = true }
Expand All @@ -18,6 +19,7 @@ parking_lot = { workspace = true }
magicblock-magic-program-api = { workspace = true }
magicblock-metrics = { workspace = true }
magicblock-delegation-program = { workspace = true }
dlp-api = { workspace = true }
solana-account = { workspace = true }
solana-account-decoder = { workspace = true }
solana-account-decoder-client-types = { workspace = true }
Expand Down
5 changes: 5 additions & 0 deletions magicblock-chainlink/src/chainlink/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum ChainlinkError {

#[error("Delegation record could not be decoded: {0} ({1:?})")]
InvalidDelegationRecord(Pubkey, ProgramError),
#[error("Delegation actions could not be decoded: {0} ({1})")]
InvalidDelegationActions(Pubkey, String),

#[error("Failed to resolve one or more accounts {0} when getting delegation records")]
DelegatedAccountResolutionsFailed(String),
Expand All @@ -41,4 +43,7 @@ pub enum ChainlinkError {

#[error("Unexpected number of accounts returned when fetching account with companion: {0}")]
UnexpectedAccountCount(String),

#[error("Missing accounts required by delegation actions: {0:?}")]
MissingDelegationActionAccounts(Vec<Pubkey>),
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tracing::*;

use super::{delegation, types::AccountWithCompanion, FetchCloner};
use crate::{
cloner::{AccountCloneRequest, Cloner},
cloner::{AccountCloneRequest, Cloner, DelegationActions},
remote_account_provider::{ChainPubsubClient, ChainRpcClient},
};

Expand Down Expand Up @@ -127,15 +127,16 @@ where
)
.await
{
let (deleg_record, _delegation_actions) = deleg;
delegated_to_other =
delegation::get_delegated_to_other(this, &deleg);
commit_frequency_ms = Some(deleg.commit_frequency_ms);
delegation::get_delegated_to_other(this, &deleg_record);
commit_frequency_ms = Some(deleg_record.commit_frequency_ms);

if let Some(projected_ata) = this
.maybe_project_delegated_ata_from_eata(
ata_account.account_shared_data(),
&eata_shared,
&deleg,
&deleg_record,
)
{
account_to_clone = projected_ata;
Expand All @@ -147,6 +148,7 @@ where
pubkey: ata_pubkey,
account: account_to_clone,
commit_frequency_ms,
delegation_actions: DelegationActions::default(),
delegated_to_other,
});
}
Expand Down
Loading
Loading