Index loaders / executable accounts (#19469)

* Appends loaders / executable_accounts to accounts in transaction loading.

* Adds indices to loaders / executable_accounts.

* Moves MessageProcessor::create_keyed_accounts() into InvokeContext::push().

* Removes "executable_accounts",
now referenced by transaction wide index into "accounts".

* Removes create_pre_accounts() from InstructionProcessor,
as it is already in MessageProcessor.

* Collect program account indices directly in load_executable_accounts().
This commit is contained in:
Alexander Meißner
2021-09-10 08:36:21 +02:00
committed by GitHub
parent 4386e09710
commit 88c1b8f047
7 changed files with 344 additions and 384 deletions

View File

@@ -101,11 +101,11 @@ pub struct Accounts {
// for the load instructions
pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>;
pub type TransactionRent = u64;
pub type TransactionLoaders = Vec<Vec<(Pubkey, AccountSharedData)>>;
pub type TransactionProgramIndices = Vec<Vec<usize>>;
#[derive(PartialEq, Debug, Clone)]
pub struct LoadedTransaction {
pub accounts: TransactionAccounts,
pub loaders: TransactionLoaders,
pub program_indices: TransactionProgramIndices,
pub rent: TransactionRent,
pub rent_debits: RentDebits,
}
@@ -244,7 +244,10 @@ impl Accounts {
let is_upgradeable_loader_present = is_upgradeable_loader_present(message);
for (i, key) in message.account_keys_iter().enumerate() {
let account = if message.is_non_loader_key(i) {
let account = if !message.is_non_loader_key(i) {
// Fill in an empty account for the program slots.
AccountSharedData::default()
} else {
if payer_index.is_none() {
payer_index = Some(i);
}
@@ -289,12 +292,12 @@ impl Accounts {
programdata_address,
}) = account.state()
{
if let Some(account) = self
if let Some((programdata_account, _)) = self
.accounts_db
.load_with_fixed_root(ancestors, &programdata_address)
.map(|(account, _)| account)
{
account_deps.push((programdata_address, account));
account_deps
.push((programdata_address, programdata_account));
} else {
error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound);
@@ -317,9 +320,6 @@ impl Accounts {
account
}
} else {
// Fill in an empty account for the program slots.
AccountSharedData::default()
};
accounts.push((*key, account));
}
@@ -338,44 +338,46 @@ impl Accounts {
let payer_account = &mut accounts[payer_index].1;
if payer_account.lamports() == 0 {
error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound)
} else {
let min_balance =
match get_system_account_kind(payer_account).ok_or_else(|| {
error_counters.invalid_account_for_fee += 1;
TransactionError::InvalidAccountForFee
})? {
SystemAccountKind::System => 0,
SystemAccountKind::Nonce => {
// Should we ever allow a fees charge to zero a nonce account's
// balance. The state MUST be set to uninitialized in that case
rent_collector.rent.minimum_balance(nonce::State::size())
}
};
if payer_account.lamports() < fee + min_balance {
error_counters.insufficient_funds += 1;
Err(TransactionError::InsufficientFundsForFee)
} else {
payer_account
.checked_sub_lamports(fee)
.map_err(|_| TransactionError::InsufficientFundsForFee)?;
let message = tx.message();
let loaders = message
.program_instructions_iter()
.map(|(program_id, _ix)| {
self.load_executable_accounts(ancestors, program_id, error_counters)
})
.collect::<Result<TransactionLoaders>>()?;
Ok(LoadedTransaction {
accounts,
loaders,
rent: tx_rent,
rent_debits,
})
}
return Err(TransactionError::AccountNotFound);
}
let min_balance = match get_system_account_kind(payer_account).ok_or_else(|| {
error_counters.invalid_account_for_fee += 1;
TransactionError::InvalidAccountForFee
})? {
SystemAccountKind::System => 0,
SystemAccountKind::Nonce => {
// Should we ever allow a fees charge to zero a nonce account's
// balance. The state MUST be set to uninitialized in that case
rent_collector.rent.minimum_balance(nonce::State::size())
}
};
if payer_account.lamports() < fee + min_balance {
error_counters.insufficient_funds += 1;
return Err(TransactionError::InsufficientFundsForFee);
}
payer_account
.checked_sub_lamports(fee)
.map_err(|_| TransactionError::InsufficientFundsForFee)?;
let program_indices = message
.instructions()
.iter()
.map(|instruction| {
self.load_executable_accounts(
ancestors,
&mut accounts,
instruction.program_id_index as usize,
error_counters,
)
})
.collect::<Result<Vec<Vec<usize>>>>()?;
Ok(LoadedTransaction {
accounts,
program_indices,
rent: tx_rent,
rent_debits,
})
} else {
error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound)
@@ -386,35 +388,35 @@ impl Accounts {
fn load_executable_accounts(
&self,
ancestors: &Ancestors,
program_id: &Pubkey,
accounts: &mut Vec<(Pubkey, AccountSharedData)>,
mut program_account_index: usize,
error_counters: &mut ErrorCounters,
) -> Result<Vec<(Pubkey, AccountSharedData)>> {
let mut accounts = Vec::new();
) -> Result<Vec<usize>> {
let mut account_indices = Vec::new();
let mut program_id = accounts[program_account_index].0;
let mut depth = 0;
let mut program_id = *program_id;
loop {
if native_loader::check_id(&program_id) {
// At the root of the chain, ready to dispatch
break;
}
while !native_loader::check_id(&program_id) {
if depth >= 5 {
error_counters.call_chain_too_deep += 1;
return Err(TransactionError::CallChainTooDeep);
}
depth += 1;
let program = match self
program_account_index = match self
.accounts_db
.load_with_fixed_root(ancestors, &program_id)
.map(|(account, _)| account)
{
Some(program) => program,
Some((program_account, _)) => {
let account_index = accounts.len();
accounts.push((program_id, program_account));
account_index
}
None => {
error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound);
}
};
let program = &accounts[program_account_index].1;
if !program.executable() {
error_counters.invalid_program_for_execution += 1;
return Err(TransactionError::InvalidProgramForExecution);
@@ -429,26 +431,31 @@ impl Accounts {
programdata_address,
}) = program.state()
{
if let Some(program) = self
let programdata_account_index = match self
.accounts_db
.load_with_fixed_root(ancestors, &programdata_address)
.map(|(account, _)| account)
{
accounts.insert(0, (programdata_address, program));
} else {
error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound);
}
Some((programdata_account, _)) => {
let account_index = accounts.len();
accounts.push((programdata_address, programdata_account));
account_index
}
None => {
error_counters.account_not_found += 1;
return Err(TransactionError::ProgramAccountNotFound);
}
};
account_indices.insert(0, programdata_account_index);
} else {
error_counters.invalid_program_for_execution += 1;
return Err(TransactionError::InvalidProgramForExecution);
}
}
accounts.insert(0, (program_id, program));
account_indices.insert(0, program_account_index);
program_id = program_owner;
}
Ok(accounts)
Ok(account_indices)
}
pub fn load_accounts(
@@ -1468,8 +1475,8 @@ mod tests {
(Ok(loaded_transaction), _nonce_rollback) => {
assert_eq!(loaded_transaction.accounts.len(), 3);
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
assert_eq!(loaded_transaction.loaders.len(), 1);
assert_eq!(loaded_transaction.loaders[0].len(), 0);
assert_eq!(loaded_transaction.program_indices.len(), 1);
assert_eq!(loaded_transaction.program_indices[0].len(), 0);
}
(Err(e), _nonce_rollback) => Err(e).unwrap(),
}
@@ -1654,15 +1661,22 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1);
match &loaded_accounts[0] {
(Ok(loaded_transaction), _nonce_rollback) => {
assert_eq!(loaded_transaction.accounts.len(), 3);
assert_eq!(loaded_transaction.accounts.len(), 6);
assert_eq!(loaded_transaction.accounts[0].1, accounts[0].1);
assert_eq!(loaded_transaction.loaders.len(), 2);
assert_eq!(loaded_transaction.loaders[0].len(), 1);
assert_eq!(loaded_transaction.loaders[1].len(), 2);
for loaders in loaded_transaction.loaders.iter() {
for (i, accounts_subset) in loaders.iter().enumerate() {
assert_eq!(loaded_transaction.program_indices.len(), 2);
assert_eq!(loaded_transaction.program_indices[0].len(), 1);
assert_eq!(loaded_transaction.program_indices[1].len(), 2);
for program_indices in loaded_transaction.program_indices.iter() {
for (i, program_index) in program_indices.iter().enumerate() {
// +1 to skip first not loader account
assert_eq!(*accounts_subset, accounts[i + 1]);
assert_eq!(
loaded_transaction.accounts[*program_index].0,
accounts[i + 1].0
);
assert_eq!(
loaded_transaction.accounts[*program_index].1,
accounts[i + 1].1
);
}
}
}
@@ -1752,7 +1766,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[2].clone()]);
assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]);
}
#[test]
@@ -1835,7 +1849,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[5].clone()]);
assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]);
// Solution 2: mark programdata as readonly
message.account_keys = vec![key0, key1, key2]; // revert key change
@@ -1847,14 +1861,9 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(
result.loaders[0],
vec![
accounts[5].clone(),
accounts[3].clone(),
accounts[4].clone()
]
);
assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]);
assert_eq!(result.accounts[result.program_indices[0][1]], accounts[3]);
assert_eq!(result.accounts[result.program_indices[0][2]], accounts[4]);
}
#[test]
@@ -1923,8 +1932,8 @@ mod tests {
let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts_with_upgradeable_loader[..2]);
assert_eq!(
result.loaders[0],
vec![accounts_with_upgradeable_loader[2].clone()]
result.accounts[result.program_indices[0][0]],
accounts_with_upgradeable_loader[2]
);
// Solution 2: mark programdata as readonly
@@ -1937,7 +1946,7 @@ mod tests {
assert_eq!(loaded_accounts.len(), 1);
let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.loaders[0], vec![accounts[2].clone()]);
assert_eq!(result.accounts[result.program_indices[0][0]], accounts[2]);
}
#[test]
@@ -1952,11 +1961,17 @@ mod tests {
let mut error_counters = ErrorCounters::default();
let ancestors = vec![(0, 0)].into_iter().collect();
let keypair = Keypair::new();
let mut account = AccountSharedData::new(1, 0, &Pubkey::default());
account.set_executable(true);
accounts.store_slow_uncached(0, &keypair.pubkey(), &account);
assert_eq!(
accounts.load_executable_accounts(
&ancestors,
&solana_sdk::pubkey::new_rand(),
&mut error_counters
&mut vec![(keypair.pubkey(), account)],
0,
&mut error_counters,
),
Err(TransactionError::ProgramAccountNotFound)
);
@@ -2282,27 +2297,21 @@ mod tests {
];
let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default());
let loaders = vec![(Ok(()), None), (Ok(()), None)];
let transaction_loaders0 = vec![];
let transaction_rent0 = 0;
let loaded0 = (
Ok(LoadedTransaction {
accounts: transaction_accounts0,
loaders: transaction_loaders0,
rent: transaction_rent0,
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
}),
None,
);
let transaction_loaders1 = vec![];
let transaction_rent1 = 0;
let loaded1 = (
Ok(LoadedTransaction {
accounts: transaction_accounts1,
loaders: transaction_loaders1,
rent: transaction_rent1,
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
}),
None,
@@ -2325,9 +2334,10 @@ mod tests {
.insert_new_readonly(&pubkey);
}
let txs = vec![tx0, tx1];
let programs = vec![(Ok(()), None), (Ok(()), None)];
let collected_accounts = accounts.collect_accounts_to_store(
&txs,
&loaders,
&programs,
loaded.as_mut_slice(),
&rent_collector,
&(Hash::default(), FeeCalculator::default()),
@@ -2674,24 +2684,15 @@ mod tests {
nonce_account_pre.clone(),
Some(from_account_pre.clone()),
));
let loaders = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback.clone(),
)];
let transaction_loaders = vec![];
let transaction_rent = 0;
let loaded = (
Ok(LoadedTransaction {
accounts: transaction_accounts,
loaders: transaction_loaders,
rent: transaction_rent,
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
}),
nonce_rollback,
nonce_rollback.clone(),
);
let mut loaded = vec![loaded];
@@ -2705,9 +2706,16 @@ mod tests {
AccountShrinkThreshold::default(),
);
let txs = vec![tx];
let programs = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback,
)];
let collected_accounts = accounts.collect_accounts_to_store(
&txs,
&loaders,
&programs,
loaded.as_mut_slice(),
&rent_collector,
&(next_blockhash, FeeCalculator::default()),
@@ -2792,24 +2800,15 @@ mod tests {
nonce_account_pre.clone(),
None,
));
let loaders = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback.clone(),
)];
let transaction_loaders = vec![];
let transaction_rent = 0;
let loaded = (
Ok(LoadedTransaction {
accounts: transaction_accounts,
loaders: transaction_loaders,
rent: transaction_rent,
program_indices: vec![],
rent: 0,
rent_debits: RentDebits::default(),
}),
nonce_rollback,
nonce_rollback.clone(),
);
let mut loaded = vec![loaded];
@@ -2823,9 +2822,16 @@ mod tests {
AccountShrinkThreshold::default(),
);
let txs = vec![tx];
let programs = vec![(
Err(TransactionError::InstructionError(
1,
InstructionError::InvalidArgument,
)),
nonce_rollback,
)];
let collected_accounts = accounts.collect_accounts_to_store(
&txs,
&loaders,
&programs,
loaded.as_mut_slice(),
&rent_collector,
&(next_blockhash, FeeCalculator::default()),