|
| 1 | +use solana_svm_transaction::svm_message::SVMMessage; |
| 2 | +use solana_transaction_error::{TransactionError, TransactionResult}; |
| 3 | + |
| 4 | +use crate::account_loader::LoadedTransaction; |
| 5 | + |
| 6 | +// NOTE: |
| 7 | +// this impl is kept separately to simplify synchoronization with upstream |
| 8 | +impl LoadedTransaction { |
| 9 | + /// Validates that a transaction does not attempt to write to non-delegated accounts. |
| 10 | + /// |
| 11 | + /// This is a critical security check to prevent privilege escalation by ensuring |
| 12 | + /// account modifications are restricted to accounts explicitly delegated to the |
| 13 | + /// validator node. |
| 14 | + /// |
| 15 | + /// ## Logic |
| 16 | + /// This function enforces a security rule with a key exception: **if the fee payer |
| 17 | + /// has privileged access, this check is bypassed entirely.** |
| 18 | + /// |
| 19 | + /// For standard, non-privileged transactions, it enforces that **any account |
| 20 | + /// marked as writable (excluding the fee payer) must be a delegated account.** |
| 21 | + /// |
| 22 | + /// Read-only accounts are ignored. The fee payer's writability is handled in |
| 23 | + /// separate validation logic. |
| 24 | + pub(crate) fn validate_accounts_access( |
| 25 | + &self, |
| 26 | + message: &impl SVMMessage, |
| 27 | + ) -> TransactionResult<()> { |
| 28 | + let payer = self.accounts.first().map(|(_, acc)| acc); |
| 29 | + if payer.map(|p| p.privileged()).unwrap_or_default() { |
| 30 | + // Payer has privileged access, so we can skip the validation. |
| 31 | + return Ok(()); |
| 32 | + } |
| 33 | + |
| 34 | + // For non-privileged payers, validate the rest of the accounts. |
| 35 | + // Skip the fee payer (index 0), as its writability is validated elsewhere. |
| 36 | + for (i, (_, acc)) in self.accounts.iter().enumerate().skip(1) { |
| 37 | + // Enforce that any account intended to be writable must be a delegated account. |
| 38 | + if message.is_writable(i) && !acc.delegated() { |
| 39 | + return Err(TransactionError::InvalidWritableAccount); |
| 40 | + } |
| 41 | + } |
| 42 | + Ok(()) |
| 43 | + } |
| 44 | +} |
0 commit comments