async fn seal_inner(
        &self,
        strategy: &mut SealStrategy<'_>,
        is_fictive: bool,
    ) -> anyhow::Result<()> {
        let started_at = Instant::now();
        self.ensure_valid_l2_block(is_fictive)
            .context("L2 block is invalid")?;

        let (l1_tx_count, l2_tx_count) = l1_l2_tx_count(&self.l2_block.executed_transactions);
        tracing::info!(
            "Sealing L2 block {l2_block_number} with timestamp {ts} (L1 batch {l1_batch_number}) \
             with {total_tx_count} ({l2_tx_count} L2 + {l1_tx_count} L1) txs, {event_count} events",
            l2_block_number = self.l2_block.number,
            l1_batch_number = self.l1_batch_number,
            ts = display_timestamp(self.l2_block.timestamp),
            total_tx_count = l1_tx_count + l2_tx_count,
            event_count = self.l2_block.events.len()
        );

        // Run sub-tasks in parallel.
        L2BlockSealProcess::run_subtasks(self, strategy).await?;

        let progress = L2_BLOCK_METRICS.start(L2BlockSealStage::CalculateLogsBloom, is_fictive);
        let iter = self.l2_block.events.iter().flat_map(|event| {
            event
                .indexed_topics
                .iter()
                .map(|topic| BloomInput::Raw(topic.as_bytes()))
                .chain([BloomInput::Raw(event.address.as_bytes())])
        });
        let logs_bloom = build_bloom(iter);
        progress.observe(Some(self.l2_block.events.len()));

        // Seal block header at the last step.
        let progress = L2_BLOCK_METRICS.start(L2BlockSealStage::InsertL2BlockHeader, is_fictive);
        let definite_vm_version = self
            .protocol_version
            .unwrap_or_else(ProtocolVersionId::last_potentially_undefined)
            .into();

        let l2_block_header = L2BlockHeader {
            number: self.l2_block.number,
            timestamp: self.l2_block.timestamp,
            hash: self.l2_block.get_l2_block_hash(),
            l1_tx_count: l1_tx_count as u16,
            l2_tx_count: l2_tx_count as u16,
            fee_account_address: self.fee_account_address,
            base_fee_per_gas: self.base_fee_per_gas,
            batch_fee_input: self.fee_input,
            base_system_contracts_hashes: self.base_system_contracts_hashes,
            protocol_version: self.protocol_version,
            gas_per_pubdata_limit: get_max_gas_per_pubdata_byte(definite_vm_version),
            virtual_blocks: self.l2_block.virtual_blocks,
            gas_limit: get_max_batch_gas_limit(definite_vm_version),
            logs_bloom,
            pubdata_params: self.pubdata_params,
        };

        let mut connection = strategy.connection().await?;
        connection
            .blocks_dal()
            .insert_l2_block(&l2_block_header)
            .await?;
        progress.observe(None);

        // Report metrics.
        let progress = L2_BLOCK_METRICS.start(L2BlockSealStage::ReportTxMetrics, is_fictive);
        self.report_transaction_metrics();
        progress.observe(Some(self.l2_block.executed_transactions.len()));

        self.report_l2_block_metrics(started_at);
        Ok(())
    }
Seals an L2 block with the given number.

If `is_fictive` flag is set to true, then it is assumed that we should seal a fictive L2 block
with no transactions in it. It is needed because there might be some storage logs / events
that are created after the last processed tx in the L1 batch: after the last transaction is processed,
the bootloader enters the "tip" phase in which it can still generate events (e.g.,
one for sending fees to the operator).
core/node/state_keeper/src/io/seal_logic/mod.rs