Skip to content

chore: make sure rules validation returns a structured result #186

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

Merged
merged 26 commits into from
Apr 28, 2025

Conversation

jeluard
Copy link
Contributor

@jeluard jeluard commented Apr 11, 2025

Let rules::validate_block return validation results as structured data and not as an error. Result::Err should be used only for runtime errors and not validation failures.

Before

pub fn execute(...) ->anyhow::Result<InvalidBlock>;

After

pub enum BlockValidation {
    Valid,
    Invalid(InvalidBlock),
}

pub fn execute(...) -> BlockValidation;

A common signature has been introduced for block level rule validation. This allows to share common logic to deal with the invalid case.
Note that introducing a trait to have stronger guarantees could be considered.

Execution of block validation fun is now abstracted in a trait:

pub trait RuleValidationExecutor<C: ValidationContext<FinalState = S>, S: From<C>> {
    fn execute(
        &mut self,
        context: &mut C,
        protocol_params: &ProtocolParameters,
        block: &MintedBlock<'_>,
        block_validation_fns: &[fn(
            &mut C,
            &MintedBlock<'_>,
            &ProtocolParameters,
        ) -> BlockValidation],
    ) -> BlockValidation;
}

This open the door down the line for more advanced way to deal with their execution than the simplistic linear execution (e.g. parallel execution, ordered execution, ...).

Next steps

The same changes could be applied to transaction level rule validations.

Summary by CodeRabbit

  • New Features

    • Introduced explicit block validation results with detailed error variants for block validation.
    • Enhanced error reporting for block size, header size, and execution unit validation.
  • Refactor

    • Updated validation functions to return structured validation results instead of generic errors.
    • Simplified and clarified validation control flow across block processing and tests.
    • Improved test assertions to match new validation result patterns.
    • Refactored ledger stage to handle validation results explicitly and streamline block processing.
  • Chores

    • Updated Rust toolchain to a specific nightly version and revised minimum required Rust version.
    • Added ignore rule for coverage files and improved Makefile CI targets.
    • Enabled required Rust feature gates and suppressed specific Clippy lints.

Copy link
Contributor

coderabbitai bot commented Apr 11, 2025

Walkthrough

Alright mate, here's the lowdown: This batch of changes is a big ol' refactor of the block validation logic across the ledger and related bits. The validation flow now uses explicit enums—BlockValidation and InvalidBlockDetails—instead of Result types for richer error handling and clearer control flow. The Try trait is leveraged for early returns, and function signatures have been updated to pass contexts and blocks by reference. There's also a bit of toolchain wrangling, some Makefile and .gitignore tidying, and a couple of Clippy lints silenced for peace of mind. All up, it's a solid spring clean, with a dash of Rust nightly magic.

Changes

File(s) Change Summary
crates/amaru-ledger/src/rules.rs, crates/amaru-ledger/src/rules/block.rs Refactored block validation to use new enums BlockValidation and InvalidBlockDetails, replaced Result-based error handling with explicit validation states, updated test imports and assertions, and implemented the Try trait for custom control flow. Function signatures now take mutable references for context and block.
crates/amaru-ledger/src/rules/block/body_size.rs, crates/amaru-ledger/src/rules/block/ex_units.rs, crates/amaru-ledger/src/rules/block/header_size.rs Refactored validation functions to accept context and block by reference, return BlockValidation instead of Result, and use new error variants from InvalidBlockDetails. Updated tests and removed old error enums.
crates/amaru/src/stages/ledger.rs Refactored roll_forward to use new validation flow, added helper for context creation, removed error downcasting, updated control flow to match on BlockValidation, and updated imports. Removed roll_forward_wrapper.
examples/shared/src/lib.rs Updated block validation logic to use new context-based API and BlockValidation matching. Renamed variables for clarity.
crates/amaru-consensus/src/consensus/chain_selection.rs Suppressed Clippy lint and improved iterator usage in tests by replacing .last() with .next_back(). Added clarifying comments.
crates/amaru-ledger/src/lib.rs Added #![feature(try_trait_v2)] to enable the unstable Try trait for custom control flow in block validation.
rust-toolchain.toml, examples/ledger-in-nodejs/rust-toolchain.toml Updated Rust toolchain versions to specific nightly releases for consistency and feature support.
Cargo.toml Bumped minimum Rust version from 1.84 to 1.88 in workspace config.
.gitignore Added lcov.info to ignore list for LLVM coverage reports.
Makefile Simplified build-examples target by using a single command with `
crates/amaru/src/stages/consensus/forward_chain.rs Added attribute to suppress Clippy's large enum variant warning on Unit enum.

Sequence Diagram(s)

sequenceDiagram
    participant Worker
    participant LedgerStage
    participant BlockValidator
    participant Context

    Worker->>LedgerStage: roll_forward(point, raw_block)
    LedgerStage->>LedgerStage: parse raw_block -> MintedBlock
    LedgerStage->>LedgerStage: create_validation_context(MintedBlock)
    LedgerStage->>BlockValidator: validate_block(context, MintedBlock)
    BlockValidator->>BlockValidator: run header/body/ex_units checks
    BlockValidator->>BlockValidator: for each tx: execute(context, tx)
    BlockValidator-->>LedgerStage: BlockValidation::{Valid|Invalid}
    alt Valid
        LedgerStage->>Context: convert to VolatileState
        LedgerStage->>Worker: return BlockValidation::Valid
    else Invalid
        LedgerStage->>Worker: return BlockValidation::Invalid
    end
Loading

Poem

🍀
Oh, the ledger’s had a tidy,
With enums bold and flow less flighty!
No more Results to make us blue,
Just Try and Invalid shining through.
From nightly Rust to Clippy’s hush,
The code’s as clean as a Guinness flush.
So raise a glass—refactor’s done,
The block parade has just begun!
🍻


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between ec96b89 and 2e82638.

📒 Files selected for processing (4)
  • crates/amaru-ledger/src/rules/block.rs (2 hunks)
  • crates/amaru-ledger/src/rules/block/body_size.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block/ex_units.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block/header_size.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/amaru-ledger/src/rules/block/header_size.rs
  • crates/amaru-ledger/src/rules/block/ex_units.rs
  • crates/amaru-ledger/src/rules/block/body_size.rs
  • crates/amaru-ledger/src/rules/block.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@jeluard jeluard requested review from KtorZ and yHSJ April 11, 2025 12:06
@yHSJ
Copy link
Contributor

yHSJ commented Apr 11, 2025

Result::Err should be used only for runtime errors and not validation failures.

I'm not sure I agree with this statement. Perhaps it's not a system failure, but it's a failure to satisfy a predicate, which is a part of the happy-path. I have less rust experience than others working on Amaru so I may be wrong, but it feels very standard to use a Result::Err when there is a predicate failure in the validate_block function. I think returning a BlockValidation (which could be easily named BlockValidationResult) is communicating the exact same thing as a Result.

I'll need some convincing to agree that this PR is needed at all.

@jeluard
Copy link
Contributor Author

jeluard commented Apr 11, 2025

Errors accessing the db must be treated differently than failure to validate a block. Failure to validate a block is part of the nominal running of amaru, while Errors accessing the db will probably lead to process crash and require manual intervention.
It looks sensible to distinguish both and in general we are moving in the direction of using Result::Err only for those cases.

It also feels more functional to deal with validation errors this way. We then have a pure function calls returning data that can then be acted upon. A concrete difference is that now ledger::roll_forward returns something while it is currently ().
It allows for use-cases like having a wasm based browser tool that can display validation failures for a considered block.

@yHSJ
Copy link
Contributor

yHSJ commented Apr 11, 2025

It looks sensible to distinguish both and in general we are moving in the direction of using Result::Err only for those cases.

I understand the idea here, it just feels unnatural to me. The Ok variant in every case can just be a Result since they're just enums with Valid and Invalid variants. I always though the point of anyhow was to make error reporting simpler, i.e. I don't have to define a type for every error. But we already are AND, handling them. Why not just make the unrecoverable errors (or recoverable but unexpected errors) a variant of the Error type that we are returning?

Or, perhaps even better, instead of returning a Result at all (either anyhow::Result or the rust Result), return a custom BlockValidationResult that has a Valid, Invalid, and Error variant, so that we don't have to do extra maps and matches to get the same behavior.

I was just looking through some other rust implementations of blockchains (notably reth as we learned about it from an Ethereum dev in Paris), and they use a mix of basic Result values and the custom type with three variants.

All this to say that I get the idea and will use it if it's what we want throughout the rest of Amaru, it just feels a bit strange to me.

Comment on lines 71 to 75
if let Err(err) =
header_size::block_header_size_valid(block.header.raw_cbor(), &protocol_params)
{
return BlockValidation::Invalid(err.into());
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd eventually bite the bullet and do it for all rules. The inconsistency (some rules returning errors, and some returning results) is going to drive us crazy in the long run 😰.

@KtorZ
Copy link
Contributor

KtorZ commented Apr 14, 2025

@yHSJ this comes from a couple of discussions we had with @jeluard and @rkuhn about error handling. While we still don't generally agree about everything, I think the ledger rules is one particular location where the distinction between errors (as in, an application failure) and "invalid user inputs" is worth separating.

One way to look at it is that from the perspective of Amaru (a block producer), an invalid block is part of the normal flow of execution and is just bound to a different logic flow. This is unlike a chain-indexer, who can reasonably expect all blocks it sees to be valid and if presented with an invalid one, it truly is an exception case. Amaru is different since we expect to sometimes run into invalid blocks if only because they are invalid with respects to our local state, but aren't "generally invalid".

On the other hand, there are cases in the validation sequence that are truly invalid. For example, being unable to decode raw stake addresses bytes into a proper stake address. This error only occurs because of the partial de-serialization that happens in Pallas; and it is to be treated as an exception in this scenario.

@yHSJ
Copy link
Contributor

yHSJ commented Apr 14, 2025

I think the ledger rules is one particular location where the distinction between errors (as in, an application failure) and "invalid user inputs" is worth separating.

I totally get it and agree that this makes sense. I just think the way we're wrapping stuff and handling the results is strange now. If we want to have three different variants, we should create a new enum that has three variants, no?

@KtorZ
Copy link
Contributor

KtorZ commented Apr 15, 2025

I just think the way we're wrapping stuff and handling the results is strange now.

I do agree. I see mostly two points of improvements from here:

  1. Use results and disambiguate errors from predicate failures consistently across all rules and sub-rules. Doing it only at the top is kinda weird.

  2. Possibly implement a Try instance ( https://doc.rust-lang.org/std/ops/trait.Try.html ) for predicate failures, so that we can recover using ? for control-flow. Given that errors are more rare than predicate failures, it makes more sense to me to leverage ? for predicate failures, and use a if let Err(...) = ...; return ... for actual exceptions.

@jeluard jeluard force-pushed the jeluard/rules-validation-errors branch 3 times, most recently from 7d901ad to bf33a12 Compare April 16, 2025 14:50
@rkuhn
Copy link
Contributor

rkuhn commented Apr 16, 2025

One very important about anyhow: its main utility is not to play it fast and loose regarding error types, its main utility is the ability to add context as an error is bubbling up the call chain. Without this, error reporting is almost guaranteed to be utterly useless, I’ve been frustrated by this when operating Rust code in production and getting panics or error logs in the past.

So, please never use anyhow::anyhow!(), ever (instead use thiserror to create good error enums). And use anyhow::Context generously. You will thank yourself later.


On the topic at hand: it would also be sensible to return anyhow::Result<Result<(), ValidationError>>, I’ve seen that in other places and probably used it myself somewhere. This is useful if you want to use the machinery that comes packaged with Result<T, E> in working further with the validation result — but not for reacting to runtime failures. The envisioned BlockValidationResult enum is isomorphic to Result<(), ValidationError>, so this is mostly a question of personal style. One argument for using a specific type is better IDE support for finding all usages.

@@ -124,21 +137,22 @@ pub fn execute<C: ValidationContext<FinalState = S>, S: From<C>>(
transaction_index: i as usize, // From u32
};

transaction::execute(
&mut context,
if let Err(err) = transaction::execute(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transaction rules haven't been changed; this could be done in a follow up PR.

let bh_size = block_header.block_body_size as usize;
let actual_block_size = calculate_block_body_size(block);

if bh_size != actual_block_size {
Err(InvalidBlockSize::SizeMismatch {
BlockValidation::Invalid(InvalidBlock::Size(InvalidBlockSize::SizeMismatch {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely we can do better

@jeluard jeluard force-pushed the jeluard/rules-validation-errors branch 2 times, most recently from 6110b65 to 777740d Compare April 17, 2025 12:25
Copy link

codecov bot commented Apr 17, 2025

Codecov Report

Attention: Patch coverage is 66.12903% with 21 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/amaru/src/stages/ledger.rs 0.00% 18 Missing ⚠️
crates/amaru-ledger/src/rules/block.rs 81.25% 3 Missing ⚠️
Files with missing lines Coverage Δ
...s/amaru-consensus/src/consensus/chain_selection.rs 91.79% <100.00%> (+0.02%) ⬆️
crates/amaru-ledger/src/lib.rs 100.00% <ø> (ø)
crates/amaru-ledger/src/rules.rs 90.74% <100.00%> (+0.74%) ⬆️
crates/amaru-ledger/src/rules/block/body_size.rs 100.00% <100.00%> (ø)
crates/amaru-ledger/src/rules/block/ex_units.rs 100.00% <100.00%> (ø)
crates/amaru-ledger/src/rules/block/header_size.rs 100.00% <100.00%> (ø)
crates/amaru/src/stages/consensus/forward_chain.rs 72.08% <ø> (-1.09%) ⬇️
crates/amaru-ledger/src/rules/block.rs 59.09% <81.25%> (+59.09%) ⬆️
crates/amaru/src/stages/ledger.rs 0.00% <0.00%> (ø)

... and 49 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@@ -1,3 +1,3 @@
[toolchain]
channel = "1.84" # ⚠️ Also change in top-level Cargo.toml
channel = "nightly-2025-04-16" # ⚠️ Also change in top-level Cargo.toml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't match Cargo.toml's ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Cargo.toml you can't use the nightly syntax so I used the closest stable version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😬

@jeluard jeluard force-pushed the jeluard/rules-validation-errors branch 2 times, most recently from 6173c21 to 6867dd5 Compare April 18, 2025 09:05
@jeluard jeluard force-pushed the jeluard/rules-validation-errors branch from e6939e3 to f769bd4 Compare April 23, 2025 02:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
crates/amaru-consensus/src/consensus/chain_selection.rs (1)

500-504: Ah, a potential side-effect conundrum in the test!

You've added the clippy::double_ended_iterator_last suppression and a helpful TODO comment. This is a fair call-out - like that scene in "Inception" where actions in one layer affect all the others. The test might be relying on select_roll_forward being called for every element, not just to get the last result.

Consider using .for_each() followed by getting the last result, or .fold() to ensure all operations are performed:

let result = chain2.iter().fold(None, |_, header| {
    Some(chain_selector.select_roll_forward(&bob, *header))
}).unwrap();
examples/shared/src/lib.rs (2)

72-80: Don’t panic! on user input – bubble it up for better UX

In this sample we slam a panic!("Failed to validate block") the moment a block is dodgy.
That’s fine for a throw‑away example, but for real callers you probably want to propagate the InvalidBlockDetails so higher layers (CLI, web, whatever) can display a friendly message instead of an abrupt stack trace.

Something like:

-if let BlockValidation::Invalid(err) = rules::validate_block(
-    &mut DefaultRuleValidationExecutor::new(),
-    &mut context,
-    ProtocolParameters::default(),
-    &block,
-) {
-    panic!("Failed to validate block")
-};
+match rules::validate_block(
+    &mut DefaultRuleValidationExecutor::new(),
+    &mut context,
+    ProtocolParameters::default(),
+    &block,
+) {
+    BlockValidation::Valid => {}
+    BlockValidation::Invalid(detail) => {
+        eprintln!("⚠️  Block rejected: {detail:?}");
+        return; // or Result::Err(detail)
+    }
+}

Keeps the example snappy yet shows best practice.


82-85: unwrap() here could still kaboom – maybe swap for a ?

state.forward(...).unwrap() will nuke the process if the ledger refuses to roll forward (storage error, unexpected era, etc.).
Because the surrounding function already returns (), consider converting forward_ledger into -> anyhow::Result<()> (or similar) and use state.forward(...)?;.

That lets callers decide whether to retry or bail out in style.

crates/amaru-ledger/src/rules/block/header_size.rs (1)

31-35: Infallible u32 → usize conversion doesn’t need a panic cloak

On 32‑ and 64‑bit targets usize::try_from(u32) cannot fail, so the panic arm is dead code and triggers Clippy’s unnecessary_lazy_evaluations.
A simple cast keeps things tidy:

-    let max_header_size = protocol_params
-        .max_header_size
-        .try_into()
-        .unwrap_or_else(|_| panic!("Failed to convert u32 to usize"));
+    let max_header_size = protocol_params.max_header_size as usize;

Less noise, same safety.

crates/amaru-ledger/src/rules/block/body_size.rs (1)

42-43: Consider optimizing serialization in calculate_block_body_size

G'day, this FIXME looks like a golden opportunity for a performance boost!

Calculating block body size by re-serializing the block is a bit like rebuilding your entire Minecraft house just to count how many blocks you used. The comment suggests relying on the original bytes instead, which would be much more efficient.

Would you like me to look into a more efficient implementation that doesn't require re-serialization?

crates/amaru-ledger/src/rules/block.rs (3)

31-57: Add Clone/PartialEq derives & mark enum as #[non_exhaustive] for future‑proofing

Right now InvalidBlockDetails / BlockValidation can’t be compared or cheaply copied, and any new variant addition will be a breaking change.
A tiny derive + attribute buys us ergonomics (unit tests, pattern‑matching comparisons) and semver wiggle‑room, with zero runtime cost.

-#[derive(Debug)]
-pub enum InvalidBlockDetails {
+#[non_exhaustive]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum InvalidBlockDetails {
   …
 }

-#[derive(Debug)]
-pub enum BlockValidation {
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum BlockValidation {
   …
 }

167-176: Promote “missing witness set” to a dedicated enum variant

Mapping a protocol‑level invariant breach to UncategorizedError loses valuable signal.
Consider a specific variant, e.g.:

MissingWitnessSet { transaction_index: u32 }

That way consumers can match on it explicitly instead of string‑parsing.


198-213: Leverage map_err ergonomics to trim boilerplate

The manual if let Err(err) dance works but feels a tad clunky.
You could compress it with:

transaction::execute()
    .map_err(|violation| InvalidBlockDetails::Transaction {
        transaction_hash,
        transaction_index: i,
        violation,
    })?;

The ? will bubble up BlockValidation::Invalid thanks to your Try impl, keeping the happy‑path tidy like Mario’s mustache.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 911ecea and f769bd4.

⛔ Files ignored due to path filters (1)
  • examples/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • Cargo.toml (1 hunks)
  • crates/amaru-consensus/src/consensus/chain_selection.rs (2 hunks)
  • crates/amaru-ledger/src/lib.rs (1 hunks)
  • crates/amaru-ledger/src/rules.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block.rs (2 hunks)
  • crates/amaru-ledger/src/rules/block/body_size.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block/ex_units.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block/header_size.rs (2 hunks)
  • crates/amaru/src/stages/ledger.rs (4 hunks)
  • examples/ledger-in-nodejs/rust-toolchain.toml (1 hunks)
  • examples/shared/src/lib.rs (3 hunks)
  • rust-toolchain.toml (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
crates/amaru-ledger/src/rules/block/header_size.rs (3)
crates/amaru-ledger/src/rules/block.rs (2)
  • new (93-95)
  • default (99-101)
crates/amaru-ledger/src/context/default/validation.rs (1)
  • new (40-47)
crates/amaru-kernel/src/protocol_parameters.rs (1)
  • default (101-283)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Build on ubuntu-latest with target riscv32im-risc0-zkvm-elf
  • GitHub Check: Build on ubuntu-24.04 with target aarch64-unknown-linux-musl
  • GitHub Check: Build on windows-latest with target x86_64-pc-windows-msvc
  • GitHub Check: Build on ubuntu-latest with target x86_64-unknown-linux-gnu
  • GitHub Check: Build on macos-latest with target aarch64-apple-darwin
  • GitHub Check: coverage
  • GitHub Check: Sanity
  • GitHub Check: Snapshots (preprod, 10.1.4)
🔇 Additional comments (21)
.gitignore (1)

41-43: Mask llvm-cov coverage file
Top-notch move, mate! By adding the # llvm-cov generated file comment and ignoring lcov.info, you’re keeping those bulky coverage artifacts out of commits—like slipping past security in Deus Ex.

crates/amaru-ledger/src/lib.rs (1)

15-15: You've enabled the try_trait_v2 unstable feature - nice one!

This is a solid move to support implementing the Try trait for your custom BlockValidation enum. With this feature, you'll be able to use the ? operator with your new validation result type, creating smoother control flow for validation chains. The changes align perfectly with the PR's goal of distinguishing validation failures from runtime errors.

Cargo.toml (1)

10-10: Updated the rust-version requirement - fair dinkum!

You've bumped the minimum Rust version from 1.84 to 1.88, which makes perfect sense given you're implementing the Try trait for your new validation types. This aligns with the toolchain updates elsewhere. Reminds me of upgrading gear in an RPG - gotta keep those dependencies fresh!

crates/amaru-consensus/src/consensus/chain_selection.rs (1)

538-538: Good catch replacing .last() with .next_back()!

You've swapped .last() for .next_back() which is more explicit about how you're accessing the final element from the double-ended iterator. It's the same effect but avoids the Clippy warning. This change is like choosing the stiletto over the broadsword - more precise for the job at hand.

rust-toolchain.toml (1)

2-2: Nightly surf’s up – double‑check the board wax, mate

Switching the whole repo to nightly-2025‑04‑16 is grand for unlocking try_trait_v2, but nightly waves can dump breaking changes on you without warning.
👀 Make sure:

  1. Every workspace Cargo.toml now opts into the same toolchain (or has an explicit rust-version gate).
  2. CI caches are flushed so you don’t keep compiling with a stale nightly.
  3. rustfmt.toml / Clippy lints still behave – some rules drift between nightlies.

If any downstream crates must stay on stable you might need a rust-toolchain file per crate or rustup override dance.
Just a gentle heads‑up before the build pipeline goes full “Mad Max”.

crates/amaru-ledger/src/rules/block/ex_units.rs (4)

15-21: Added imports to support validation restructuring

Nice one updating the imports to align with the new structured validation approach. The shift from error handling to the explicit BlockValidation enum makes your code's intent crystal clear - reminds me of how Dead Space separates the health bar from the suit integrity meter rather than smooshing them together.


23-27: Function signature refactored for validation clarity

Crikey, this is a proper improvement! Taking a mutable validation context and returning the new BlockValidation enum makes the distinction between validation failures and runtime errors dead obvious. It's like how in Fallout games, failing a speech check isn't an error - it's just one of the expected outcomes of the conversation.


36-46: Return structured validation results explicitly

Brilliant work here, mate! The explicit BlockValidation::Valid and BlockValidation::Invalid returns make the code flow dead obvious - it's not trying to shoehorn validation failures into error types anymore. This matches perfectly with the PR objective of distinguishing between validation failures (expected workflow) and runtime errors (exceptional cases).


51-52: Test updates reflect new validation pattern

Grand job updating the tests to match the new validation pattern. Tests are now properly checking for specific validation results rather than generic errors, which makes them much more meaningful - like how good unit tests in The Last of Us Part II test specific gameplay mechanics rather than just "did the game crash?".

Also applies to: 80-87

crates/amaru-ledger/src/rules/block/body_size.rs (4)

15-20: Reorganized imports to support validation restructuring

Spot on with the imports updates here - pulling in the ValidationContext and the new BlockValidation enum. Clean as a whistle!


23-29: Function signature refactored for validation clarity

Ace work on the signature change! Taking a mutable validation context and returning the new BlockValidation enum is a slick move. Getting the block header directly from the block parameter is also cleaner than having it passed separately.


32-39: Return structured validation results explicitly

Beauty! The explicit BlockValidation::Valid and BlockValidation::Invalid returns make this function's intentions crystal clear. No more wondering if an error is a validation failure or a runtime issue - it's as clear as the difference between a parry and a miss in Dark Souls.


57-58: Test updates reflect new validation pattern

Deadly work on the test updates! I especially like how the assertions are now checking for specific validation details rather than just generic errors. Makes the tests much more meaningful - like checking if your Pokémon fainted from poison versus a critical hit, rather than just "did it faint?".

Also applies to: 60-61, 72-79

crates/amaru/src/stages/ledger.rs (4)

1-11: Updated imports to support new validation flow

Proper job with the imports! Bringing in BlockValidation and DefaultRuleValidationExecutor sets the stage for the cleaner validation approach throughout the code.


62-92: Extracted validation context creation into a dedicated method

Smart move breaking out the validation context creation into its own method! Makes the code much more readable and maintainable - like how Breaking Bad separated its storylines into focused episodes rather than trying to cram everything into a single narrative thread.


99-123: Refactored roll_forward to use new validation flow

This refactoring is absolutely brilliant! The function now:

  1. Returns a meaningful BlockValidation result instead of ()
  2. Handles validation failures explicitly
  3. Has a clear separation between validation and state updates

It's like how Mad Max: Fury Road clearly distinguished the "everything is grand" path from the "we've got baddies on our tail" path. Very clean implementation!


171-182: Execution result mapping updated to handle validation outcomes

Nice update to the execute method! Mapping the BlockValidation results to the appropriate BlockValidationResult variants is clean and expressive. The pattern match makes it impossible to forget handling either case - a classic example of making illegal states unrepresentable, like how Breath of the Wild won't let you climb when it's raining.

crates/amaru-ledger/src/rules.rs (3)

131-135: Updated test imports to support new validation pattern

Top-notch import updates! Bringing in the BlockValidation and DefaultRuleValidationExecutor types sets up the tests to work with the new validation pattern.


178-186: Test updated to use new validation executor pattern

Deadly work updating the test to use the new validation approach! The mutable executor and context pattern is consistently applied, and the assertion checks specifically for BlockValidation::Valid instead of a generic success result. Makes the test much more expressive - like how a good unit test in The Witcher 3 would check if Geralt successfully cast Igni rather than just "did something happen?".


206-217: Error case test updated to use new validation approach

Beauty! The error test case is properly updated to use the new validation approach and assert on the specific InvalidBlockDetails variant. This makes the test much more robust, as it's checking not just that validation failed, but how it failed - like checking if you died in Dark Souls because you ran out of stamina during a roll, not just that you died.

crates/amaru-ledger/src/rules/block.rs (1)

59-79: Nightly Try usage – double‑check CI toolchain & gate features

The custom Try impl is slick, mate, but it relies on #![feature(try_trait_v2)].
Make sure:

  1. cargo +nightly check is part of the CI matrix.
  2. All downstream crates either gate the feature behind cfg(feature = "nightly") or have a clear MSRV policy in README.md.

Otherwise stable consumers will get a face‑full of compiler errors faster than Han shot Greedo.

toolchain = { channel = "nightly-2025-04-16"}
Copy link
Contributor

@coderabbitai coderabbitai bot Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Crikey, mate! You're time traveling to fetch this toolchain!

You've pinned the Rust toolchain to a specific nightly version dated "2025-04-16", which is in the future (as of April 2024). While pinning to a specific nightly is a grand idea for stability, especially when using unstable features like try_trait_v2, the date should probably be a valid past or current date.

-toolchain = { channel = "nightly-2025-04-16"}
+toolchain = { channel = "nightly-2024-04-16"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
toolchain = { channel = "nightly-2025-04-16"}
toolchain = { channel = "nightly-2024-04-16"}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you are in the past, mate!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeluard Strewth, you caught me time traveling in the wrong direction! You're absolutely right, cobber - I was living in 2024 while reviewing code from 2025. That's more confusing than the timeline in "Back to the Future"!

The "nightly-2025-04-16" is bang on for the current date. Thanks for the reality check - I'll be sure to check my temporal coordinates next time before making suggestions. My DeLorean must've had a dodgy flux capacitor!

Comment on lines +21 to 24
#[derive(Debug)]
pub enum InvalidBlockHeader {
#[error("block header size too big: supplied {supplied}, max {max}")]
SizeTooBig { supplied: usize, max: usize },
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

InvalidBlockHeader is now a ronin – delete or reintegrate

The enum InvalidBlockHeader isn’t used anywhere after the refactor (the function returns InvalidBlockDetails instead).
Leaving it orphaned will trigger a dead‑code warning and confuse future spelunkers.

Either wire it back into InvalidBlockDetails or cull it altogether.

Comment on lines 28 to 30
// TODO: rewrite this to use iterators defined on `Redeemers` and `MaybeIndefArray`, ideally
let ex_units = block.ex_units();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

TODO: Consider optimizing execution units extraction

Fair play flagging this with a TODO. Using iterators for the execution units extraction would make the code more idiomatic and potentially more efficient.


🏁 Script executed:

#!/bin/bash
# Let's see if there are any other similar patterns in the codebase
rg -A 2 "TODO: rewrite this to use iterators" --type rust

Length of output: 338


Buff up ex_units extraction with iterators

Ahoy! I spotted the lone TODO at crates/amaru-ledger/src/rules/block/ex_units.rs:28—time to level up this code with a slick iterator combo. Zip together your block.redeemers() and block.maybe_indef_array() iterators, map them into ExUnits, then collect. It’s more idiomatic Rust and might shave off a few cycles, like landing a perfect dodge in your fave action RPG.

Locations to tweak:

  • crates/amaru-ledger/src/rules/block/ex_units.rs:28

Suggested diff:

- // TODO: rewrite this to use iterators defined on `Redeemers` and `MaybeIndefArray`, ideally
- let ex_units = block.ex_units();
+ let ex_units = block.redeemers()
+     .zip(block.maybe_indef_array())
+     .map(|(redeemer, data)| {
+         // construct ExUnit from `redeemer` and `data`
+     })
+     .collect::<Vec<_>>();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 158 to 165
let witness_sets = block.transaction_witness_sets.deref().to_vec();

let transactions = block.transaction_bodies.to_vec();
let transactions = block.transaction_bodies.deref().to_vec();

// using `zip` here instead of enumerate as it is safer to cast from u32 to usize than usize to u32
// Realistically, we're never gonna hit the u32 limit with the number of transactions in a block (a boy can dream)
for (i, transaction) in (0u32..).zip(transactions.into_iter()) {
let transaction_hash = transaction.original_hash();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid full Vec clones – iterate by reference

to_vec() allocates & copies every transaction/witness set, which is a bit of a wallet‑drain when blocks get chunky.

-let witness_sets = block.transaction_witness_sets.deref().to_vec();
-let transactions  = block.transaction_bodies.deref().to_vec();
+let witness_sets = &block.transaction_witness_sets;
+let transactions = &block.transaction_bodies;

Then rewrite the loop:

-for (i, transaction) in (0u32..).zip(transactions.into_iter()) {
+for (i, (transaction, witness_set)) in transactions.iter().zip(witness_sets).enumerate() {
+    let i = i as u32;

This keeps memory usage lean and sidesteps needless copies like Neo dodging bullets.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let witness_sets = block.transaction_witness_sets.deref().to_vec();
let transactions = block.transaction_bodies.to_vec();
let transactions = block.transaction_bodies.deref().to_vec();
// using `zip` here instead of enumerate as it is safer to cast from u32 to usize than usize to u32
// Realistically, we're never gonna hit the u32 limit with the number of transactions in a block (a boy can dream)
for (i, transaction) in (0u32..).zip(transactions.into_iter()) {
let transaction_hash = transaction.original_hash();
let witness_sets = &block.transaction_witness_sets;
let transactions = &block.transaction_bodies;
// using `zip` here instead of enumerate as it is safer to cast from u32 to usize than usize to u32
// Realistically, we're never gonna hit the u32 limit with the number of transactions in a block (a boy can dream)
for (i, (transaction, witness_set)) in transactions.iter().zip(witness_sets).enumerate() {
let i = i as u32;
let transaction_hash = transaction.original_hash();

@jeluard jeluard force-pushed the jeluard/rules-validation-errors branch from cb57f6a to a45baef Compare April 23, 2025 09:18
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/amaru-ledger/src/rules/block.rs (1)

111-113: ⚠️ Potential issue

Performance hit with vector cloning

Crikey! You're still doing full vector clones here with to_vec(), which can be a real resource drain for large blocks. This was flagged in a previous review but hasn't been addressed.

Let's fix this to avoid unnecessary copying:

-let witness_sets = block.transaction_witness_sets.deref().to_vec();
-let transactions = block.transaction_bodies.deref().to_vec();
+let witness_sets = block.transaction_witness_sets.deref();
+let transactions = block.transaction_bodies.deref();

Then update the loop to match:

-for (i, transaction) in (0u32..).zip(transactions.into_iter()) {
+for (i, transaction) in (0u32..).zip(transactions.iter()) {
🧹 Nitpick comments (1)
crates/amaru-ledger/src/rules/block.rs (1)

31-51: Top-notch structured error type!

Beauty of a job replacing the old error type with this more detailed InvalidBlockDetails enum. Each variant gives specific context about what went wrong, which is pure gold for debugging. This reminds me of how Witcher 3's quest system gives you just the right amount of info without overwhelming you.

One small suggestion though - might be worth adding Clone, PartialEq and maybe Eq derives to make testing easier down the road.

-#[derive(Debug)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum InvalidBlockDetails {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between cb57f6a and a45baef.

📒 Files selected for processing (3)
  • crates/amaru-ledger/src/rules.rs (3 hunks)
  • crates/amaru-ledger/src/rules/block.rs (2 hunks)
  • crates/amaru/src/stages/ledger.rs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/amaru/src/stages/ledger.rs
  • crates/amaru-ledger/src/rules.rs
🔇 Additional comments (12)
crates/amaru-ledger/src/rules/block.rs (12)

25-26: Clean import update, nice one!

The import structure is nicely aligned with the new approach, bringing in ProtocolParameters directly. Tidy organization is the foundation of a good codebase, like how a neat inventory system makes RPGs so much more satisfying.


28-28: Added imports for the Try trait - deadly choice!

Good on ya for bringing in the necessary imports to support the new control flow approach. The Try trait is a ripper feature that makes the code flow as smooth as butter in a hot pan.


53-57: Crisp new BlockValidation enum - absolute winner!

This new enum makes the distinction between valid and invalid blocks crystal clear. It's like when Neo finally sees the Matrix - suddenly everything makes sense! This aligns perfectly with separating validation failures from runtime errors.


59-73: Clever implementation of the Try trait!

You've done a brilliant job implementing the Try trait here, mate! This enables the ? operator to work with your custom validation type, making the control flow as elegant as a parkour sequence in Mirror's Edge. The branch method creates a clean separation between the success and failure paths.


75-79: Solid FromResidual implementation!

This implementation completes the Try trait requirement and allows for smooth propagation of validation errors. Nice and concise - does exactly what it needs to without any faff.


81-88: Handy conversion to standard Result!

This conversion function is a deadly idea - provides interoperability with the rest of the Rust ecosystem that expects standard Results. It's like having that universal adapter when traveling abroad - works with everything!


91-95: Better ownership model in function signature - good thinking!

Taking references instead of ownership is a top improvement! This change should reduce unnecessary cloning and make the code more efficient. It's like upgrading from a rusty sword to a legendary blade in Skyrim - same function but much more effective.


99-107: Slick validation functions collection!

Collecting validation functions in a vector and applying them sequentially is a stroke of genius! This approach makes the code more maintainable and extensible. The TODO about applying them in parallel or priority order shows good forward thinking too.

This pattern opens the door to more advanced execution strategies, just like the PR description mentioned. Dead on!


120-128: Clearer error handling for witness sets!

This explicit match statement is more readable than the previous approach with ok_or and ?. It provides a clear path for both success and failure cases, making the code as easy to follow as a well-designed quest marker.


151-165: Explicit pattern matching for transaction validation errors!

The switch from map_err and ? to explicit if let Err makes the error handling more direct and readable. It's like when a game tutorial shows you exactly what button to press instead of being all cryptic - much appreciated by everyone involved!


168-168: Clean return of validation result!

Returning BlockValidation::Valid directly is much clearer than the previous approach. It's like the satisfying "quest completed" notification - you know exactly what happened!


90-169:

✅ Verification successful

Overall code structure improvement

The whole execute function has had a ripper of a makeover! The new structure with consistent error handling, borrowing instead of taking ownership, and the collection of validation functions makes this function much more maintainable.

Just one thing to note - there's an inconsistency mentioned in previous reviews about some rules returning errors and others returning results. As KtorZ said, might be worth "biting the bullet" and updating all rules to follow this pattern.


🏁 Script executed:

#!/bin/bash
# Check if there are still inconsistencies in rule return types
echo "Searching for transaction rule functions that might still return Result..."
rg "pub fn .+ -> (Result|anyhow::Result)" crates/amaru-ledger/src/rules/transaction/ -A 1

Length of output: 250


Alright, mate—let’s take another peek at those transaction rules and list out every function signature to spot any odd ducks still returning the wrong type. 🕵️‍♂️


🏁 Script executed:

#!/bin/bash
echo "Listing all transaction rule functions and their return types..."
rg -n "pub fn .+->" crates/amaru-ledger/src/rules/transaction/ -A 1

Length of output: 208


Sure thing, mate—let’s give those transaction rules a proper once‑over, like tracing loot in a Diablo dungeon. We’ll list every pub fn signature and then sniff out any stragglers still returning Result.


🏁 Script executed:

#!/bin/bash
echo "1) Listing all transaction rule function signatures with a bit of context..."
rg -n -t rust "pub fn" crates/amaru-ledger/src/rules/transaction/ -A 2

echo -e "\n2) Checking specifically for any lingering '-> ...Result' return types..."
rg -n -t rust "->.*Result" crates/amaru-ledger/src/rules/transaction/ -A 1

Length of output: 2568


No worries, let’s refine our grep spell to dodge that leading hyphen ghost and sniff out any stragglers returning Result in the transaction rules.


🏁 Script executed:

#!/bin/bash
echo "Looking for any `pub fn` in transaction rules that return a *Result..."
rg -n -t rust -e 'pub fn .*->.*Result' crates/amaru-ledger/src/rules/transaction/ -A2

Length of output: 326


Alright, let’s dodge those pesky backticks and hyphens—no more shell-level gremlins. We’ll do a quick two‑step scan: first list any pub fn with an arrow, then filter those for Result returns.


🏁 Script executed:

#!/bin/bash
echo '1) Find all transaction rule functions with a return arrow…'
rg -n -t rust 'pub fn .*->' crates/amaru-ledger/src/rules/transaction/ -A2

echo -e '\n2) From those arrows, filter for Result return types…'
rg -n -t rust -F '->' crates/amaru-ledger/src/rules/transaction/ -A1 \
  | grep -R 'Result' || echo '✔ No Result<> return types found'

Length of output: 44563


No return type inconsistency found in transaction rules

All pub fn execute functions under crates/amaru-ledger/src/rules/transaction/ now uniformly return Result<(), Invalid*>. The earlier note about mixed “errors vs. results” is outdated—no further changes needed here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite

📥 Commits

Reviewing files that changed from the base of the PR and between a45baef and 9237a86.

📒 Files selected for processing (2)
  • Makefile (1 hunks)
  • examples/shared/src/lib.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/shared/src/lib.rs
🔇 Additional comments (1)
Makefile (1)

113-113: Love the lean exit hack!
Swapping out the old multi‑line check for $(MAKE) -C $$dir || exit is slick—your CI pipeline now halts on the first stumble, just like hitting the eject button when a B‑movie goes off the rails.

Makefile Outdated
Comment on lines 117 to 122
all-core-ci-checks:
@cargo fmt-amaru
@cargo clippy-amaru
@cargo test-amaru
@$(MAKE) build-examples
@$(MAKE) coverage-lconv

all-ci-checks: all-core-ci-checks build-examples coverage-lconv ## Run all CI checks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mark new CI targets as phony & document them
The fresh all-core-ci-checks and all-ci-checks chores need a touch of makefile love—declare them in .PHONY and give all-core-ci-checks its own ## docstring. Also, that stray blank at line 121 can vanish quicker than DLC vaporware.

# At top of file, extend the .PHONY list
 .PHONY: help bootstrap run import-snapshots import-headers import-nonces download-haskell-config coverage-html coverage-lconv check-llvm-cov
+.PHONY: all-core-ci-checks all-ci-checks

# Add a description on the new target
-all-core-ci-checks:
+all-core-ci-checks: ## Run core CI checks

# Delete the extra blank line at 121
-  
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
all-core-ci-checks:
@cargo fmt-amaru
@cargo clippy-amaru
@cargo test-amaru
@$(MAKE) build-examples
@$(MAKE) coverage-lconv
all-ci-checks: all-core-ci-checks build-examples coverage-lconv ## Run all CI checks
# At the top of the Makefile, extend the .PHONY list
.PHONY: help bootstrap run import-snapshots import-headers import-nonces download-haskell-config coverage-html coverage-lconv check-llvm-cov
.PHONY: all-core-ci-checks all-ci-checks
# Lines ~117–122: document and tighten up
all-core-ci-checks: ## Run core CI checks
@cargo fmt-amaru
@cargo clippy-amaru
@cargo test-amaru
all-ci-checks: all-core-ci-checks build-examples coverage-lconv ## Run all CI checks

@jeluard jeluard requested a review from KtorZ April 23, 2025 12:04
let result = chain2
.iter()
// TODO looks like this test relies on the fact that `select_roll_forward` is called on every element. `map` might not be correct then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand this comment about map ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last can be replaced by next_back in theory here but in practice it fails some test. This implies that this call expects map to traverse all element by calling a function with side-effects (select_roll_forward). map should be called with pure functions as a way to construct another iterator. for_each might help clarify the expectation here (but at the price of a more contrived usage as the last element should be tracked).

@jeluard jeluard merged commit 8ed1b02 into main Apr 28, 2025
13 checks passed
@jeluard jeluard deleted the jeluard/rules-validation-errors branch April 28, 2025 08:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants