Skip to content
Merged
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
1 change: 1 addition & 0 deletions magicblock-aperture/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fn chainlink(accounts_db: &Arc<AccountsDb>) -> ChainlinkImpl {
None,
Pubkey::new_unique(),
Pubkey::new_unique(),
0,
)
.expect("Failed to create Chainlink")
}
Expand Down
1 change: 1 addition & 0 deletions magicblock-aperture/tests/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ fn chainlink(accounts_db: &Arc<AccountsDb>) -> Arc<ChainlinkImpl> {
None,
Pubkey::new_unique(),
Pubkey::new_unique(),
0,
)
.expect("Failed to create Chainlink"),
)
Expand Down
1 change: 1 addition & 0 deletions magicblock-api/src/magic_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ impl MagicValidator {
validator_pubkey,
faucet_pubkey,
chainlink_config,
config.accounts.clone.auto_airdrop_lamports,
)
.await?;

Expand Down
27 changes: 27 additions & 0 deletions magicblock-chainlink/src/chainlink/fetch_cloner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use log::*;
use magicblock_core::traits::AccountsBank;
use solana_account::{AccountSharedData, ReadableAccount};
use solana_pubkey::Pubkey;
use solana_sdk::system_program;
use tokio::{
sync::{mpsc, oneshot},
task,
Expand Down Expand Up @@ -1230,6 +1231,32 @@ where
) -> ChainlinkResult<mpsc::Receiver<Pubkey>> {
Ok(self.remote_account_provider.try_get_removed_account_rx()?)
}

/// Best-effort airdrop helper: if the account doesn't exist in the bank or has 0 lamports,
/// create/overwrite it as a plain system account with the provided lamports using the cloner path.
pub async fn airdrop_account_if_empty(
&self,
pubkey: Pubkey,
lamports: u64,
) -> ClonerResult<()> {
if lamports == 0 {
return Ok(());
}
if let Some(acc) = self.accounts_bank.get_account(&pubkey) {
if acc.lamports() > 0 {
return Ok(());
}
}
// Build a plain system account with the requested balance
let account =
AccountSharedData::new(lamports, 0, &system_program::id());
debug!(
"Auto-airdropping {} lamports to new/empty account {}",
lamports, pubkey
);
let _sig = self.cloner.clone_account(pubkey, account).await?;
Ok(())
}
}

// -----------------
Expand Down
40 changes: 38 additions & 2 deletions magicblock-chainlink/src/chainlink/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub struct Chainlink<

validator_id: Pubkey,
faucet_id: Pubkey,

/// If > 0, automatically airdrop this many lamports to feepayers when they are new/empty
auto_airdrop_lamports: u64,
}

impl<T: ChainRpcClient, U: ChainPubsubClient, V: AccountsBank, C: Cloner>
Expand All @@ -63,6 +66,7 @@ impl<T: ChainRpcClient, U: ChainPubsubClient, V: AccountsBank, C: Cloner>
fetch_cloner: Option<Arc<FetchCloner<T, U, V, C>>>,
validator_pubkey: Pubkey,
faucet_pubkey: Pubkey,
auto_airdrop_lamports: u64,
) -> ChainlinkResult<Self> {
let removed_accounts_sub = if let Some(fetch_cloner) = &fetch_cloner {
let removed_accounts_rx =
Expand All @@ -80,9 +84,11 @@ impl<T: ChainRpcClient, U: ChainPubsubClient, V: AccountsBank, C: Cloner>
removed_accounts_sub,
validator_id: validator_pubkey,
faucet_id: faucet_pubkey,
auto_airdrop_lamports,
})
}

#[allow(clippy::too_many_arguments)]
pub async fn try_new_from_endpoints(
endpoints: &[Endpoint],
commitment: CommitmentConfig,
Expand All @@ -91,6 +97,7 @@ impl<T: ChainRpcClient, U: ChainPubsubClient, V: AccountsBank, C: Cloner>
validator_pubkey: Pubkey,
faucet_pubkey: Pubkey,
config: ChainlinkConfig,
auto_airdrop_lamports: u64,
) -> ChainlinkResult<
Chainlink<
ChainRpcClientImpl,
Expand Down Expand Up @@ -129,6 +136,7 @@ impl<T: ChainRpcClient, U: ChainPubsubClient, V: AccountsBank, C: Cloner>
fetch_cloner,
validator_pubkey,
faucet_pubkey,
auto_airdrop_lamports,
)
}

Expand Down Expand Up @@ -259,8 +267,36 @@ Kept: {} delegated, {} blacklisted",
}
let mark_empty_if_not_found = (!mark_empty_if_not_found.is_empty())
.then(|| &mark_empty_if_not_found[..]);
self.ensure_accounts(&pubkeys, mark_empty_if_not_found)
.await
let res = self
.ensure_accounts(&pubkeys, mark_empty_if_not_found)
.await?;

// Best-effort auto airdrop for fee payer if configured and still empty locally
if self.auto_airdrop_lamports > 0 {
if let Some(fetch_cloner) = self.fetch_cloner() {
let lamports = self
.accounts_bank
.get_account(feepayer)
.map(|a| a.lamports())
.unwrap_or(0);
if lamports == 0 {
if let Err(err) = fetch_cloner
.airdrop_account_if_empty(
*feepayer,
self.auto_airdrop_lamports,
)
.await
{
warn!(
"Auto airdrop for feepayer {} failed: {:?}",
feepayer, err
);
}
}
}
}

Ok(res)
}

/// Same as fetch accounts, but does not return the accounts, just
Expand Down
1 change: 1 addition & 0 deletions magicblock-chainlink/tests/utils/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl TestContext {
fetch_cloner,
validator_pubkey,
faucet_pubkey,
0,
)
.unwrap();
Self {
Expand Down
1 change: 1 addition & 0 deletions test-integration/test-chainlink/src/ixtest_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl IxtestContext {
fetch_cloner,
validator_kp.pubkey(),
faucet_kp.pubkey(),
0,
)
.unwrap();

Expand Down
1 change: 1 addition & 0 deletions test-integration/test-chainlink/src/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl TestContext {
fetch_cloner,
validator_pubkey,
faucet_pubkey,
0,
)
.unwrap();
Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use magicblock_config::{
use solana_sdk::{signature::Keypair, signer::Signer, system_instruction};
use test_kit::init_logger;

#[ignore = "Auto airdrop is not generally supported at this point, we will add this back as needed"]
#[test]
fn test_auto_airdrop_feepayer_balance_after_tx() {
init_logger!();
Expand Down
Loading