diff --git a/etherlink/CHANGES_KERNEL.md b/etherlink/CHANGES_KERNEL.md index ce12e88a0ac2c0afaefadc25f40cce921e7e0ec9..0570ad4c703d832ba75c56294f22acbde319b311 100644 --- a/etherlink/CHANGES_KERNEL.md +++ b/etherlink/CHANGES_KERNEL.md @@ -21,6 +21,7 @@ - Avoid writing gas price twice per block by adding it to block-in-progress. (!15574) - Transaction validation support in simulation is dropped, the validation must be done outside of the kernel. (!15958) +- Parsing a DAL slot is now resumed after invalid inputs. (!15926) ### Bug fixes diff --git a/etherlink/kernel_evm/kernel/src/dal.rs b/etherlink/kernel_evm/kernel/src/dal.rs index 5c538fa184ca380a0a387fde3f1828b583bf51db..7c5fb2d7389c6bcb4c28e81a6a5fbcbdfb2c6073 100644 --- a/etherlink/kernel_evm/kernel/src/dal.rs +++ b/etherlink/kernel_evm/kernel/src/dal.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -use crate::parsing::parse_unsigned_blueprint_chunk; +use crate::parsing::{parse_unsigned_blueprint_chunk, SequencerBlueprintRes}; use crate::sequencer_blueprint::UnsignedSequencerBlueprint; use rlp::{DecoderError, PayloadInfo}; use tezos_evm_logging::{log, Level::*}; @@ -18,6 +18,7 @@ const DAL_PADDING_TAG: u8 = 0; enum ParsedInput { UnsignedSequencerBlueprint(UnsignedSequencerBlueprint), + InvalidInput, Padding, } @@ -73,11 +74,18 @@ fn parse_unsigned_sequencer_blueprint( bytes: &[u8], ) -> (Option, usize) { if let Result::Ok(chunk_length) = rlp_length(bytes) { - let unsigned_chunk = parse_unsigned_blueprint_chunk(&bytes[..chunk_length]); - ( - unsigned_chunk.map(ParsedInput::UnsignedSequencerBlueprint), - chunk_length + TAG_SIZE, - ) + match parse_unsigned_blueprint_chunk(&bytes[..chunk_length]) { + SequencerBlueprintRes::SequencerBlueprint(unsigned_chunk) => ( + Some(ParsedInput::UnsignedSequencerBlueprint(unsigned_chunk)), + chunk_length + TAG_SIZE, + ), + SequencerBlueprintRes::InvalidNumberOfChunks + | SequencerBlueprintRes::InvalidSignature + | SequencerBlueprintRes::InvalidNumber => { + (Some(ParsedInput::InvalidInput), chunk_length + TAG_SIZE) + } + SequencerBlueprintRes::Unparsable => (None, chunk_length + TAG_SIZE), + } } else { log!(host, Debug, "Read an invalid chunk from slot."); (None, TAG_SIZE) @@ -139,6 +147,8 @@ fn parse_slot( None => return buffer, // Once an unparsable input has been read, // stop reading and return the list of chunks read. Some(ParsedInput::UnsignedSequencerBlueprint(b)) => buffer.push(b), + // Invalid inputs are ignored. + Some(ParsedInput::InvalidInput) => {} Some(ParsedInput::Padding) => return buffer, } @@ -415,6 +425,45 @@ pub mod tests { assert_eq!(Some(vec![]), parsed_chunks) } + #[test] + fn test_parse_slot_resume_after_invalid_chunk() { + let mut host = MockKernelHost::default(); + + let valid_blueprint_chunks_1 = chunk_blueprint(dummy_big_blueprint(1)); + + let invalid_blueprint_chunks = { + let mut chunks = chunk_blueprint(dummy_big_blueprint(1)); + for chunk in chunks.iter_mut() { + chunk.nb_chunks = crate::blueprint_storage::MAXIMUM_NUMBER_OF_CHUNKS + 1 + } + chunks + }; + + let valid_blueprint_chunks_2 = chunk_blueprint(dummy_big_blueprint(1)); + + let mut chunks = vec![]; + chunks.extend(valid_blueprint_chunks_1.clone()); + chunks.extend(invalid_blueprint_chunks); + chunks.extend(valid_blueprint_chunks_2.clone()); + + let mut expected_chunks = vec![]; + expected_chunks.extend(valid_blueprint_chunks_1); + expected_chunks.extend(valid_blueprint_chunks_2); + + let dal_parameters = host.reveal_dal_parameters(); + let published_level = host.host.level() - (dal_parameters.attestation_lag as u32); + prepare_dal_slot(&mut host, &chunks, published_level as i32, 0); + + let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( + &mut host, + &dal_parameters, + 0, + published_level, + ); + + assert_eq!(Some(expected_chunks), chunks_from_slot) + } + #[test] fn test_parse_slot_with_invalid_first_chunk() { // The tag announces a chunk, the data is not an RLP encoded chunk diff --git a/etherlink/kernel_evm/kernel/src/inbox.rs b/etherlink/kernel_evm/kernel/src/inbox.rs index 659aeee1ad9a8d91b18b0c31046d0d6e2ec75467..ddf7b9a50b714636e04969d682258c3ccd66fef4 100644 --- a/etherlink/kernel_evm/kernel/src/inbox.rs +++ b/etherlink/kernel_evm/kernel/src/inbox.rs @@ -340,7 +340,7 @@ impl InputHandler for SequencerInput { delayed_inbox.save_transaction(host, *tx, previous_timestamp, level) } Self::SequencerBlueprint(SequencerBlueprint(seq_blueprint)) => { - handle_blueprint_chunk(host, seq_blueprint.blueprint) + handle_blueprint_chunk(host, seq_blueprint) } Self::SequencerBlueprint( InvalidNumberOfChunks | InvalidSignature | InvalidNumber | Unparsable, diff --git a/etherlink/kernel_evm/kernel/src/parsing.rs b/etherlink/kernel_evm/kernel/src/parsing.rs index def3584379a9ebd553957be34b4bc3e01a3159be..30a0d1d3dc380039c32e1c4373dd0931c042521a 100644 --- a/etherlink/kernel_evm/kernel/src/parsing.rs +++ b/etherlink/kernel_evm/kernel/src/parsing.rs @@ -113,7 +113,7 @@ pub enum ProxyInput { #[derive(Debug, PartialEq, Clone)] pub enum SequencerBlueprintRes { - SequencerBlueprint(SequencerBlueprint), + SequencerBlueprint(UnsignedSequencerBlueprint), InvalidNumberOfChunks, InvalidSignature, InvalidNumber, @@ -309,18 +309,18 @@ pub struct SequencerParsingContext { pub head_level: Option, } -pub fn parse_unsigned_blueprint_chunk( - bytes: &[u8], -) -> Option { +pub fn parse_unsigned_blueprint_chunk(bytes: &[u8]) -> SequencerBlueprintRes { // Parse an unsigned sequencer blueprint - let unsigned_seq_blueprint: UnsignedSequencerBlueprint = - parsable!(FromRlpBytes::from_rlp_bytes(bytes).ok()); + match UnsignedSequencerBlueprint::from_rlp_bytes(bytes).ok() { + None => SequencerBlueprintRes::Unparsable, + Some(unsigned_seq_blueprint) => { + if MAXIMUM_NUMBER_OF_CHUNKS < unsigned_seq_blueprint.nb_chunks { + return SequencerBlueprintRes::InvalidNumberOfChunks; + } - if MAXIMUM_NUMBER_OF_CHUNKS < unsigned_seq_blueprint.nb_chunks { - return None; + SequencerBlueprintRes::SequencerBlueprint(unsigned_seq_blueprint) + } } - - Some(unsigned_seq_blueprint) } pub fn parse_blueprint_chunk( @@ -352,7 +352,7 @@ pub fn parse_blueprint_chunk( .unwrap_or(false); if correctly_signed { - SequencerBlueprintRes::SequencerBlueprint(seq_blueprint) + SequencerBlueprintRes::SequencerBlueprint(unsigned_seq_blueprint) } else { SequencerBlueprintRes::InvalidSignature }