Skip to content

Commit d305a60

Browse files
authored
fix: check for confined accounts modification (#747)
1 parent f459d2d commit d305a60

File tree

8 files changed

+208
-120
lines changed

8 files changed

+208
-120
lines changed

Cargo.lock

Lines changed: 26 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ resolver = "2"
3838

3939
[workspace.package]
4040
# Solana Version (2.2.x)
41-
version = "0.4.1"
41+
version = "0.4.2"
4242
authors = ["MagicBlock Maintainers <maintainers@magicblock.xyz>"]
4343
repository = "https://github.com/magicblock-labs/ephemeral-validator"
4444
homepage = "https://www.magicblock.xyz"
@@ -131,7 +131,7 @@ serde_json = "1.0"
131131
serde_with = "3.16"
132132
serial_test = "3.2"
133133
sha3 = "0.10.8"
134-
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "1beed4c" }
134+
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "57158728" }
135135
solana-account-decoder = { version = "2.2" }
136136
solana-account-decoder-client-types = { version = "2.2" }
137137
solana-account-info = { version = "2.2" }
@@ -216,11 +216,10 @@ features = ["lz4"]
216216
# some solana dependencies have solana-storage-proto as dependency
217217
# we need to patch them with our version, because they use protobuf-src v1.1.0
218218
# and we use protobuf-src v2.1.1. Otherwise compilation fails
219-
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "1beed4c" }
219+
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "57158728" }
220220
solana-storage-proto = { path = "./storage-proto" }
221221
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "3e9456ec4" }
222222
# Fork is used to enable `disable_manual_compaction` usage
223223
# Fork is based on commit d4e9e16 of rocksdb (parent commit of 0.23.0 release)
224224
# without patching update isn't possible due to conflict with solana deps
225-
# TODO(edwin): remove once solana deps upgraded and are using rust-rocksdb 0.25.0(likely)
226-
rocksdb = { git = "https://github.com/magicblock-labs/rust-rocksdb.git", rev = "6d975197" }
225+
rocksdb = { git = "https://github.com/magicblock-labs/rust-rocksdb.git", rev = "6d975197" }

magicblock-accounts-db/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ impl AccountsDb {
101101
// atomic counter. New readers will see the latest update.
102102
acc.commit();
103103
// check whether the account's owner has changed
104-
if !acc.owner_changed {
104+
if !acc.owner_changed() {
105105
return;
106106
}
107-
// and perform some index bookkeeping to ensure correct owner
107+
// and perform some index bookkeeping to ensure a correct owner
108108
let _ = self
109109
.index
110110
.ensure_correct_owner(pubkey, account.owner())

magicblock-processor/src/executor/processing.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,23 @@ impl super::TransactionExecutor {
341341

342342
/// Ensure that no post execution account state violations occurred:
343343
/// 1. No modification of the non-delegated feepayer in gasless mode
344-
/// 2. No illegal account resizing when the balance is zero
344+
/// 2. No lamport modification of confined accounts
345345
fn verify_account_states(&self, processed: &mut ProcessedTransaction) {
346346
let ProcessedTransaction::Executed(executed) = processed else {
347347
return;
348348
};
349349
let txn = &executed.loaded_transaction;
350350
let feepayer = txn.accounts.first();
351-
let rollback_lamports =
352-
rollback_feepayer_lamports(&txn.rollback_accounts);
351+
// If the feepayer is priveleged we don't enforce any checks, as those
352+
// are internal operations, that might violate some of those rules
353+
if feepayer.as_ref().map(|a| a.1.privileged()).unwrap_or(false) {
354+
return;
355+
}
353356

357+
let logs = executed
358+
.execution_details
359+
.log_messages
360+
.get_or_insert_default();
354361
let gasless = self.environment.fee_lamports_per_signature == 0;
355362
if gasless {
356363
// If we are running in the gasless mode, we should not allow
@@ -359,40 +366,49 @@ impl super::TransactionExecutor {
359366
// from undelegated feepayers to delegated accounts, which would
360367
// result in validator loosing funds upon balance settling.
361368
let undelegated_feepayer_was_modified = feepayer
362-
.map(|acc| {
363-
(acc.1.is_dirty()
364-
&& !self.is_auto_airdrop_lamports_enabled
365-
&& (acc.1.lamports() != 0 || rollback_lamports != 0))
366-
&& !acc.1.delegated()
367-
&& !acc.1.privileged()
369+
.map(|(_, acc)| {
370+
let mutated = acc
371+
.as_borrowed()
372+
.map(|a| a.lamports_changed())
373+
// NOTE: this branch can be taken only, if the account
374+
// has been upgraded to the Owned variant, which indicates
375+
// that it has been resized, which in turn is a clear
376+
// violation of the feepayer immutability rule in this mode
377+
.unwrap_or(true);
378+
!self.is_auto_airdrop_lamports_enabled
379+
&& mutated
380+
&& !acc.delegated()
368381
})
369382
.unwrap_or_default();
370383
if undelegated_feepayer_was_modified {
371384
executed.execution_details.status =
372385
Err(TransactionError::InvalidAccountForFee);
373-
let logs = executed
374-
.execution_details
375-
.log_messages
376-
.get_or_insert_default();
377-
let msg = "Feepayer balance has been modified illegally".into();
386+
let msg = "Feepayer balance has been illegally modified".into();
378387
logs.push(msg);
388+
return;
379389
}
380390
}
381-
}
382-
}
391+
for (pubkey, acc) in &txn.accounts {
392+
if !acc.confined() {
393+
continue;
394+
}
395+
// If the confined account was modified in any way that affected its lamport
396+
// balance, then an corresponding marker must have been set, in which case we
397+
// fail the transaction, since this is regarded as a validator draining attack
398+
let balance_changed = acc
399+
.as_borrowed()
400+
.map(|a| a.lamports_changed())
401+
.unwrap_or(true);
383402

384-
// A utility to extract the rollback lamports of the feepayer
385-
fn rollback_feepayer_lamports(rollback: &RollbackAccounts) -> u64 {
386-
match rollback {
387-
RollbackAccounts::FeePayerOnly { fee_payer_account } => {
388-
fee_payer_account.lamports()
389-
}
390-
RollbackAccounts::SameNonceAndFeePayer { nonce } => {
391-
nonce.account().lamports()
403+
if balance_changed {
404+
executed.execution_details.status =
405+
Err(TransactionError::UnbalancedTransaction);
406+
let msg = format!(
407+
"Confined account {pubkey} has been illegally modified"
408+
);
409+
logs.push(msg);
410+
break;
411+
}
392412
}
393-
RollbackAccounts::SeparateNonceAndFeePayer {
394-
fee_payer_account,
395-
..
396-
} => fee_payer_account.lamports(),
397413
}
398414
}

0 commit comments

Comments
 (0)