-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Fix compute budget bump #21238
Fix compute budget bump #21238
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
|
@@ -8,8 +8,8 @@ use solana_sdk::{ | ||
account::{AccountSharedData, ReadableAccount}, | account::{AccountSharedData, ReadableAccount}, | ||
compute_budget::ComputeBudget, | compute_budget::ComputeBudget, | ||
feature_set::{ | feature_set::{ | ||
demote_program_write_locks, do_support_realloc, remove_native_loader, tx_wide_compute_cap, | demote_program_write_locks, do_support_realloc, neon_evm_compute_budget, | ||
FeatureSet, | remove_native_loader, requestable_heap_size, tx_wide_compute_cap, FeatureSet, | ||
}, | }, | ||
hash::Hash, | hash::Hash, | ||
ic_logger_msg, ic_msg, | ic_logger_msg, ic_msg, | ||
|
@@ -103,6 +103,7 @@ pub struct ThisInvokeContext<'a> { | ||
sysvars: &'a [(Pubkey, Vec<u8>)], | sysvars: &'a [(Pubkey, Vec<u8>)], | ||
logger: Rc<RefCell<dyn Logger>>, | logger: Rc<RefCell<dyn Logger>>, | ||
compute_budget: ComputeBudget, | compute_budget: ComputeBudget, | ||
current_compute_budget: ComputeBudget, | |||
compute_meter: Rc<RefCell<dyn ComputeMeter>>, | compute_meter: Rc<RefCell<dyn ComputeMeter>>, | ||
executors: Rc<RefCell<Executors>>, | executors: Rc<RefCell<Executors>>, | ||
instruction_recorders: Option<&'a [InstructionRecorder]>, | instruction_recorders: Option<&'a [InstructionRecorder]>, | ||
|
@@ -137,6 +138,7 @@ impl<'a> ThisInvokeContext<'a> { | ||
programs, | programs, | ||
sysvars, | sysvars, | ||
logger: ThisLogger::new_ref(log_collector), | logger: ThisLogger::new_ref(log_collector), | ||
current_compute_budget: compute_budget, | |||
compute_budget, | compute_budget, | ||
compute_meter, | compute_meter, | ||
executors, | executors, | ||
|
@@ -195,7 +197,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { | ||
return Err(InstructionError::CallDepth); | return Err(InstructionError::CallDepth); | ||
} | } | ||
|
|
||
if let Some(index_of_program_id) = program_indices.last() { | let program_id = if let Some(index_of_program_id) = program_indices.last() { | ||
let program_id = &self.accounts[*index_of_program_id].0; | let program_id = &self.accounts[*index_of_program_id].0; | ||
let contains = self | let contains = self | ||
.invoke_stack | .invoke_stack | ||
|
@@ -210,11 +212,32 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { | ||
// Reentrancy not allowed unless caller is calling itself | // Reentrancy not allowed unless caller is calling itself | ||
return Err(InstructionError::ReentrancyNotAllowed); | return Err(InstructionError::ReentrancyNotAllowed); | ||
} | } | ||
} | *program_id | ||
} else { | |||
return Err(InstructionError::UnsupportedProgramId); | |||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jackcmay @Lichtso it looks like this new err has made master and v1.8 incompatible. On v1.8 it's possible to invoke the native loader directly from a tx in which case Here is an example tx that showcases the different behavior. The tx invokes the native loader in a way that runs the code path for a 0 lamport system ix transfer with the program_id set to the native loader id: https://explorer.solana.com/tx/inspector?message=AQABAxqaPUT28ZZYeGqKkcOfzL7aLBsifPzASKGnxP4QwCbm%252Bk%252BRkBm4gzLzoCATvtlllmIDwh%252B8XErqP9gYsdFvpjUFh4S%252FFIukKC%252BwEldIiKnxU6B9rfdlwEVcmpcDgAAAAL1QHO0HDnEf1DbxkmiM2jg37QuLnqbQDF9g5Y7HZgN3AQICAAEMAgAAAAAAAAAAAAAA It was constructed with the following JS: // create this to copy out the ix data
const ix = SystemProgram.transfer({
fromPubkey: MY_KEY.publicKey,
toPubkey: NEW_KEY.publicKey,
lamports: 0,
});
const tx = new Transaction().add(new TransactionInstruction({
programId: new PublicKey("NativeLoader1111111111111111111111111111111"),
data: ix.data,
keys: [{
pubkey: MY_KEY.publicKey,
isSigner: false,
isWritable: false,
}, {
pubkey: NEW_KEY.publicKey,
isSigner: false,
isWritable: true,
}],
})); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned here I think we need to either add an empty transaction to the test corpus or feature gate and forbid them. Because they are breaking the assumption that there is at least one executable account loaded to provide a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm in favor of feature gating and forbidding them. But we will still need to revert behavior and add a test case for this in the meantime until we enable that feature switch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed in both, the temporary test and the plan to remove them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change that are causing this behavior were an optimization to get the program_id in push. We don't have to do that and instead allow empty indices and get the program id from the message instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are tracking this in a separate issue: #21346 |
|||
}; | |||
|
|
||
if self.invoke_stack.is_empty() { | if self.invoke_stack.is_empty() { | ||
let mut compute_budget = self.compute_budget; | |||
if !self.is_feature_active(&tx_wide_compute_cap::id()) | |||
&& self.is_feature_active(&neon_evm_compute_budget::id()) | |||
&& program_id == crate::neon_evm_program::id() | |||
{ | |||
// Bump the compute budget for neon_evm | |||
compute_budget.max_units = compute_budget.max_units.max(500_000); | |||
} | |||
if !self.is_feature_active(&requestable_heap_size::id()) | |||
&& self.is_feature_active(&neon_evm_compute_budget::id()) | |||
&& program_id == crate::neon_evm_program::id() | |||
{ | |||
// Bump the compute budget for neon_evm | |||
compute_budget.heap_size = Some(256_usize.saturating_mul(1024)); | |||
} | |||
self.current_compute_budget = compute_budget; | |||
|
|||
if !self.feature_set.is_active(&tx_wide_compute_cap::id()) { | if !self.feature_set.is_active(&tx_wide_compute_cap::id()) { | ||
self.compute_meter = ThisComputeMeter::new_ref(self.compute_budget.max_units); | self.compute_meter = | ||
ThisComputeMeter::new_ref(self.current_compute_budget.max_units); | |||
} | } | ||
|
|
||
self.pre_accounts = Vec::with_capacity(instruction.accounts.len()); | self.pre_accounts = Vec::with_capacity(instruction.accounts.len()); | ||
|
@@ -484,7 +507,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> { | ||
self.sysvars | self.sysvars | ||
} | } | ||
fn get_compute_budget(&self) -> &ComputeBudget { | fn get_compute_budget(&self) -> &ComputeBudget { | ||
&self.compute_budget | &self.current_compute_budget | ||
} | } | ||
fn set_blockhash(&mut self, hash: Hash) { | fn set_blockhash(&mut self, hash: Hash) { | ||
self.blockhash = hash; | self.blockhash = hash; | ||
|
@@ -1087,4 +1110,77 @@ mod tests { | ||
invoke_context.pop(); | invoke_context.pop(); | ||
} | } | ||
} | } | ||
|
|||
#[test] | |||
fn test_invoke_context_compute_budget() { | |||
let accounts = vec![ | |||
( | |||
solana_sdk::pubkey::new_rand(), | |||
Rc::new(RefCell::new(AccountSharedData::default())), | |||
), | |||
( | |||
crate::neon_evm_program::id(), | |||
Rc::new(RefCell::new(AccountSharedData::default())), | |||
), | |||
]; | |||
|
|||
let noop_message = Message::new( | |||
&[Instruction::new_with_bincode( | |||
accounts[0].0, | |||
&MockInstruction::NoopSuccess, | |||
vec![AccountMeta::new_readonly(accounts[0].0, false)], | |||
)], | |||
None, | |||
); | |||
let neon_message = Message::new( | |||
&[Instruction::new_with_bincode( | |||
crate::neon_evm_program::id(), | |||
&MockInstruction::NoopSuccess, | |||
vec![AccountMeta::new_readonly(accounts[0].0, false)], | |||
)], | |||
None, | |||
); | |||
|
|||
let mut feature_set = FeatureSet::all_enabled(); | |||
feature_set.deactivate(&tx_wide_compute_cap::id()); | |||
feature_set.deactivate(&requestable_heap_size::id()); | |||
let mut invoke_context = ThisInvokeContext::new_mock_with_sysvars_and_features( | |||
&accounts, | |||
&[], | |||
&[], | |||
Arc::new(feature_set), | |||
); | |||
|
|||
invoke_context | |||
.push(&noop_message, &noop_message.instructions[0], &[0], None) | |||
.unwrap(); | |||
assert_eq!( | |||
*invoke_context.get_compute_budget(), | |||
ComputeBudget::default() | |||
); | |||
invoke_context.pop(); | |||
|
|||
invoke_context | |||
.push(&neon_message, &neon_message.instructions[0], &[1], None) | |||
.unwrap(); | |||
let expected_compute_budget = ComputeBudget { | |||
max_units: 500_000, | |||
heap_size: Some(256_usize.saturating_mul(1024)), | |||
..ComputeBudget::default() | |||
}; | |||
assert_eq!( | |||
*invoke_context.get_compute_budget(), | |||
expected_compute_budget | |||
); | |||
invoke_context.pop(); | |||
|
|||
invoke_context | |||
.push(&noop_message, &noop_message.instructions[0], &[0], None) | |||
.unwrap(); | |||
assert_eq!( | |||
*invoke_context.get_compute_budget(), | |||
ComputeBudget::default() | |||
); | |||
invoke_context.pop(); | |||
} | |||
} | } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is
current_compute_budget
and how is it different fromcompute_budget
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the bump is only for a single instruction
compute_budget
which is the original is used to resetcurrent_compute_budget
for additional instructions that don't meet the bump criteria.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no further concerns I'll commit it