-
Notifications
You must be signed in to change notification settings - Fork 1k
Poc/simd 0160 static instruction limit feature gated #8162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,8 @@ struct PacketOffsets { | |
| pub msg_start: u32, | ||
| pub pubkey_start: u32, | ||
| pub pubkey_len: u32, | ||
| pub instruction_len: u32, | ||
| pub instruction_start: u32, | ||
| } | ||
|
|
||
| impl PacketOffsets { | ||
|
|
@@ -54,13 +56,17 @@ impl PacketOffsets { | |
| msg_start: u32, | ||
| pubkey_start: u32, | ||
| pubkey_len: u32, | ||
| instruction_len: u32, | ||
| instruction_start: u32, | ||
| ) -> Self { | ||
| Self { | ||
| sig_len, | ||
| sig_start, | ||
| msg_start, | ||
| pubkey_start, | ||
| pubkey_len, | ||
| instruction_len, | ||
| instruction_start, | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -261,7 +267,7 @@ fn do_get_packet_offsets( | |
| .checked_add(pubkey_len_size) | ||
| .ok_or(PacketError::InvalidPubkeyLen)?; | ||
|
|
||
| let _ = pubkey_len | ||
| let blockhash_offset = pubkey_len | ||
| .checked_mul(size_of::<Pubkey>()) | ||
| .and_then(|v| v.checked_add(pubkey_start)) | ||
| .filter(|v| *v <= packet.meta().size) | ||
|
|
@@ -271,6 +277,37 @@ fn do_get_packet_offsets( | |
| return Err(PacketError::InvalidPubkeyLen); | ||
| } | ||
|
|
||
| // read instruction length | ||
| let instructions_len_offset = blockhash_offset | ||
| .checked_add(size_of::<Hash>()) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| // Packet should have at least 1 more byte for instructions.len | ||
| let _ = instructions_len_offset | ||
| .checked_add(1usize) | ||
| .filter(|v| *v <= packet.meta().size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| let (instruction_len, instruction_len_size) = packet | ||
| .data(instructions_len_offset..) | ||
| .and_then(|bytes| decode_shortu16_len(bytes).ok()) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| // SIMD-0160: skip if has more than 64 top instructions | ||
| if instruction_len > 64 { | ||
|
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. this is unconditional. i'm like 99% sure this fn is only run for BP, where that would be fine, but we should verify that's the case.
Author
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. BP == "block producer/packer"? sigverify only feed to leaders, it does not impact consensus, if that is what you mean. I'm thinking changes to sigverify can be o nits own PR without feature gate. |
||
| return Err(PacketError::InvalidProgramLen); | ||
| } | ||
|
|
||
| let instruction_start = instructions_len_offset | ||
| .checked_add(instruction_len_size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| // Packet should have at least 1 more byte for one instructions_program_id | ||
| let _ = instruction_start | ||
| .checked_add(1usize) | ||
| .filter(|v| *v <= packet.meta().size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| let sig_start = current_offset | ||
| .checked_add(sig_size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
@@ -280,13 +317,18 @@ fn do_get_packet_offsets( | |
| let pubkey_start = current_offset | ||
| .checked_add(pubkey_start) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
| let instruction_start = current_offset | ||
| .checked_add(instruction_start) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| Ok(PacketOffsets::new( | ||
| u32::try_from(sig_len_untrusted)?, | ||
| u32::try_from(sig_start)?, | ||
| u32::try_from(msg_start)?, | ||
| u32::try_from(pubkey_start)?, | ||
| u32::try_from(pubkey_len)?, | ||
| u32::try_from(instruction_len)?, | ||
| u32::try_from(instruction_start)?, | ||
| )) | ||
| } | ||
|
|
||
|
|
@@ -303,7 +345,7 @@ fn get_packet_offsets( | |
| } | ||
| } | ||
| // force sigverify to fail by returning zeros | ||
| PacketOffsets::new(0, 0, 0, 0, 0) | ||
| PacketOffsets::new(0, 0, 0, 0, 0, 0, 0) | ||
| } | ||
|
|
||
| fn check_for_simple_vote_transaction( | ||
|
|
@@ -330,35 +372,13 @@ fn check_for_simple_vote_transaction( | |
| .checked_sub(current_offset) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| let instructions_len_offset = (packet_offsets.pubkey_len as usize) | ||
| .checked_mul(size_of::<Pubkey>()) | ||
| .and_then(|v| v.checked_add(pubkey_start)) | ||
| .and_then(|v| v.checked_add(size_of::<Hash>())) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| // Packet should have at least 1 more byte for instructions.len | ||
| let _ = instructions_len_offset | ||
| .checked_add(1usize) | ||
| .filter(|v| *v <= packet.meta().size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| let (instruction_len, instruction_len_size) = packet | ||
| .data(instructions_len_offset..) | ||
| .and_then(|bytes| decode_shortu16_len(bytes).ok()) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
| // skip if has more than 1 instruction | ||
| if instruction_len != 1 { | ||
| if packet_offsets.instruction_len != 1 { | ||
| return Err(PacketError::InvalidProgramLen); | ||
| } | ||
|
|
||
| let instruction_start = instructions_len_offset | ||
| .checked_add(instruction_len_size) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| // Packet should have at least 1 more byte for one instructions_program_id | ||
| let _ = instruction_start | ||
| .checked_add(1usize) | ||
| .filter(|v| *v <= packet.meta().size) | ||
| let instruction_start = (packet_offsets.instruction_start as usize) | ||
| .checked_sub(current_offset) | ||
| .ok_or(PacketError::InvalidLen)?; | ||
|
|
||
| let instruction_program_id_index: usize = usize::from( | ||
|
|
@@ -681,7 +701,9 @@ mod tests { | |
| PACKETS_PER_BATCH, | ||
| }, | ||
| sigverify::{self, PacketOffsets}, | ||
| test_tx::{new_test_vote_tx, test_multisig_tx, test_tx}, | ||
| test_tx::{ | ||
| new_test_tx_with_number_of_ixs, new_test_vote_tx, test_multisig_tx, test_tx, | ||
| }, | ||
| }, | ||
| bincode::{deserialize, serialize}, | ||
| bytes::{BufMut, Bytes, BytesMut}, | ||
|
|
@@ -691,11 +713,12 @@ mod tests { | |
| solana_message::{compiled_instruction::CompiledInstruction, Message, MessageHeader}, | ||
| solana_signature::Signature, | ||
| solana_signer::Signer, | ||
| solana_transaction::Transaction, | ||
| solana_transaction::{versioned::VersionedTransaction, Transaction}, | ||
| std::{ | ||
| iter::repeat_with, | ||
| sync::atomic::{AtomicU64, Ordering}, | ||
| }, | ||
| test_case::test_case, | ||
| }; | ||
|
|
||
| const SIG_OFFSET: usize = 1; | ||
|
|
@@ -996,6 +1019,7 @@ mod tests { | |
| let offsets = sigverify::do_get_packet_offsets(packet.as_ref(), 0).unwrap(); | ||
| let expected_offsets = { | ||
| legacy_offsets.pubkey_start += 1; | ||
| legacy_offsets.instruction_start += 1; | ||
| legacy_offsets | ||
| }; | ||
|
|
||
|
|
@@ -1033,30 +1057,32 @@ mod tests { | |
| packet_offsets.msg_start - packet_offsets.sig_start, | ||
| packet_offsets.pubkey_start - packet_offsets.msg_start, | ||
| packet_offsets.pubkey_len, | ||
| packet_offsets.instruction_len, | ||
| packet_offsets.instruction_start - packet_offsets.pubkey_start, | ||
| ) | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_get_packet_offsets() { | ||
| assert_eq!( | ||
| get_packet_offsets_from_tx(test_tx(), 0), | ||
| PacketOffsets::new(1, 1, 64, 4, 2) | ||
| PacketOffsets::new(1, 1, 64, 4, 2, 1, 97) | ||
| ); | ||
| assert_eq!( | ||
| get_packet_offsets_from_tx(test_tx(), 100), | ||
| PacketOffsets::new(1, 1, 64, 4, 2) | ||
| PacketOffsets::new(1, 1, 64, 4, 2, 1, 97) | ||
| ); | ||
|
|
||
| // Ensure we're not indexing packet by the `current_offset` parameter. | ||
| assert_eq!( | ||
| get_packet_offsets_from_tx(test_tx(), 1_000_000), | ||
| PacketOffsets::new(1, 1, 64, 4, 2) | ||
| PacketOffsets::new(1, 1, 64, 4, 2, 1, 97) | ||
| ); | ||
|
|
||
| // Ensure we're returning sig_len, not sig_size. | ||
| assert_eq!( | ||
| get_packet_offsets_from_tx(test_multisig_tx(), 0), | ||
| PacketOffsets::new(2, 1, 128, 4, 4) | ||
| PacketOffsets::new(2, 1, 128, 4, 4, 1, 161) | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -1799,4 +1825,32 @@ mod tests { | |
| ] | ||
| ) | ||
| } | ||
|
|
||
| #[test_case(false, false; "ok_ixs_legacy")] | ||
| #[test_case(true, false; "too_many_ixs_legacy")] | ||
| #[test_case(false, true; "ok_ixs_versioned")] | ||
| #[test_case(true, true; "too_many_ixs_versioned")] | ||
| fn test_number_of_instructions(too_many_ixs: bool, is_versioned_tx: bool) { | ||
| let mut number_of_ixs = 64; | ||
| if too_many_ixs { | ||
| number_of_ixs += 1; | ||
| } | ||
|
|
||
| let packet = if is_versioned_tx { | ||
| let tx: VersionedTransaction = new_test_tx_with_number_of_ixs(number_of_ixs); | ||
| BytesPacket::from_data(None, tx.clone()).unwrap() | ||
| } else { | ||
| let tx: Transaction = new_test_tx_with_number_of_ixs(number_of_ixs); | ||
| BytesPacket::from_data(None, tx.clone()).unwrap() | ||
| }; | ||
|
|
||
| if too_many_ixs { | ||
| assert_eq!( | ||
| do_get_packet_offsets(packet.as_ref(), 0), | ||
| Err(PacketError::InvalidProgramLen), | ||
| ); | ||
| } else { | ||
| assert!(do_get_packet_offsets(packet.as_ref(), 0).is_ok()); | ||
| } | ||
| } | ||
| } | ||
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.
let's move this out of the loop, otherwise it looks it up everytime