Skip to content

Query utxos on cardano cli#831

Closed
sofia-bobbiesi wants to merge 13 commits intotxpipe:mainfrom
nicolasLuduena:query-utxos-on-cardano-cli
Closed

Query utxos on cardano cli#831
sofia-bobbiesi wants to merge 13 commits intotxpipe:mainfrom
nicolasLuduena:query-utxos-on-cardano-cli

Conversation

@sofia-bobbiesi
Copy link
Copy Markdown
Contributor

@sofia-bobbiesi sofia-bobbiesi commented Jan 7, 2026

Summary by CodeRabbit

  • New Features
    • Added a state-query server exposing ledger queries: system start, chain block numbers/points, era history, current tip, epoch numbers, and UTxO-by-address lookups.
    • Returns Plutus-aware transaction outputs and a compact encoded output format for external clients.
    • Chain synchronization and state-query handling now run concurrently with cancellation support.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds a new o7s_unix statequery module and updates handle_session to run chainsync and statequery tasks in parallel, awaiting both with tokio::try_join, preserving cancellation propagation and aborting the plexer after completion.

Changes

Cohort / File(s) Summary
Concurrency Integration
src/serve/o7s_unix/mod.rs
NodeServer destructured to include statequery; handle_session now spawns chainsync and statequery tasks in parallel, awaits both with tokio::try_join, maps join failures to ServeError::Internal, propagates individual task errors, clones CancelToken into both tasks, and aborts the plexer after tasks complete.
State Query Server
src/serve/o7s_unix/statequery.rs
New module implementing Session<D> and handle_session; adds q16 request handling (system start, chain block no, chain point, interpreter/era history, ledger tip, epoch no, UTxO-by-address), CBOR encoding helpers, UTxO/output conversion and datum handling, session lifecycle (acquire/reacquire/release/tip cursor), and a cancellation-aware async entrypoint wired into the localstate protocol.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Mod as o7s_unix::handle_session
    participant Plexer as Plexer
    participant Chainsync as ChainsyncTask
    participant StateQ as StateQueryTask
    participant Session as statequery::Session
    participant Domain as Domain

    Client->>Mod: open connection
    Mod->>Plexer: create plexer
    Mod->>Chainsync: spawn ChainsyncTask (CancelToken, plexer)
    Mod->>StateQ: spawn StateQueryTask (CancelToken, plexer)
    par Concurrent
        Chainsync->>Domain: request blocks / sync
        Domain-->>Chainsync: block data
        StateQ->>Session: initialize session
        Session->>Domain: acquire/reacquire point, query tip/UTxO/era
        Domain-->>Session: ledger/UTxO/era data
        Session->>StateQ: encode CBOR response
        StateQ->>Client: send response
    end
    Chainsync-->>Mod: task result
    StateQ-->>Mod: task result
    Mod->>Mod: tokio::try_join both tasks
    alt join failure
        Mod->>Mod: map to ServeError::Internal
    else join success
        Mod->>Mod: propagate each task's Result
    end
    Mod->>Plexer: abort
    Mod-->>Client: close session
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through code where new paths meet,

Two tasks in tandem, plexer keeps the beat,
CBOR crumbs and UTxOs arranged just right,
Era echoes shimmer in the night,
I twitch my nose and celebrate the flight.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Query utxos on cardano cli' is vague and does not accurately reflect the actual changes, which involve adding a complete state-query server module (statequery.rs) with session handling and CBOR-encoded protocol implementation. Consider a more descriptive title that reflects the main changes, such as 'Add state-query server module for o7s_unix protocol' or 'Implement statequery handler with UTxO and era history support'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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: 2

🤖 Fix all issues with AI agents
In @src/serve/o7s_unix/statequery.rs:
- Around line 474-484: The match for computing era_index (used when constructing
AnyCbor::from_encode(era_index as u16)) incorrectly groups protocol 8 into the
Conway fallback; update the match on edge.protocol in the era_index binding to
explicitly map Babbage variants to 5 and Conway variants to 6 (e.g. change the
arms to include 7..=8 => 5 for Babbage and 9..=10 => 6 for Conway or extend
ranges as needed) and remove the wildcard catch-all that currently maps
future/unknown versions to Conway so the mapping is explicit.
- Around line 166-174: The code currently hardcodes script_ref: None when
building q16::PostAlonsoTransactionOutput; call output.script_ref() to extract
the Option and convert it to the q16 type (e.g., using .map(|sr| sr.into()) or
the same conversion helper used elsewhere) and set script_ref to that value so
reference scripts are returned; update the block constructing
q16::PostAlonsoTransactionOutput (the code that sets address, amount,
inline_datum) to use output.script_ref().map(...) instead of None.
🧹 Nitpick comments (1)
src/serve/o7s_unix/statequery.rs (1)

274-294: Consider avoiding the clone of refs_vec.

The refs_vec.clone() is necessary here because get_utxos takes ownership and refs_vec is reused in the loop. However, you could iterate over utxos.keys() instead if the return order doesn't matter, or restructure to avoid the clone:

♻️ Suggested refactor to avoid clone
-    let refs_vec: Vec<_> = all_refs.into_iter().collect();
-    let utxos = domain
-        .state()
-        .get_utxos(refs_vec.clone())
-        .map_err(|e| Error::server(format!("failed to get utxos: {}", e)))?;
-
-    debug!(fetched_utxos = utxos.len(), "fetched utxo data");
-
-    for utxo_ref in refs_vec {
-        if let Some(era_cbor) = utxos.get(&utxo_ref) {
+    let refs_vec: Vec<_> = all_refs.into_iter().collect();
+    let utxos = domain
+        .state()
+        .get_utxos(refs_vec)
+        .map_err(|e| Error::server(format!("failed to get utxos: {}", e)))?;
+
+    debug!(fetched_utxos = utxos.len(), "fetched utxo data");
+
+    for (utxo_ref, era_cbor) in utxos {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bba161 and c3ed42c.

📒 Files selected for processing (2)
  • src/serve/o7s_unix/mod.rs
  • src/serve/o7s_unix/statequery.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/serve/o7s_unix/mod.rs (3)
src/serve/o7s_unix/statequery.rs (1)
  • handle_session (596-623)
src/relay/mod.rs (1)
  • handle_session (19-41)
src/serve/o7s_unix/chainsync.rs (1)
  • handle_session (188-210)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check Build
🔇 Additional comments (11)
src/serve/o7s_unix/mod.rs (3)

10-10: LGTM!

The new statequery module is properly declared alongside the existing chainsync module.


21-26: LGTM!

Clean destructuring of NodeServer to extract the required protocol handlers.


28-48: LGTM!

The concurrent task handling pattern is sound. Both sessions run independently with proper error propagation. The approach differs from src/relay/mod.rs which uses try_join! directly on futures (no spawning) and ignores results - this implementation is more robust by propagating individual task errors.

Note that if one task errors, the other continues running until natural completion since there's no cross-cancellation. This is acceptable for independent protocol sessions.

src/serve/o7s_unix/statequery.rs (8)

1-10: LGTM!

Imports are appropriate for the statequery protocol implementation.


50-76: Verify the EraParams CBOR structure.

The safe_from_tip value is encoded twice: once within the SafeZone block (lines 58-73) and again at line 75 after the SafeZone. This appears to encode a 4-element array (encoder.array(4) at line 51) with elements: epoch_length, slot_length, SafeZone, and safe_from_tip.

Please confirm this matches the expected CBOR schema for era parameters.


186-229: LGTM!

Comprehensive recursive conversion of all PlutusData variants.


368-377: Verify the ChainPoint::Slot(_) existence check.

Line 376 assumes that any ChainPoint::Slot exists on the chain (=> true). However, tip_cursor() at line 364 can return a ChainPoint::Slot which might not have a corresponding block. If the slot was never filled (a slot without a block), this could lead to inconsistent behavior downstream.

Consider validating slot existence explicitly or clarifying why this assumption is safe.


394-416: LGTM!

The GetSystemStart query implementation correctly converts the timestamp to the expected protocol format (year, day of year, picoseconds of day).


418-440: LGTM!

The block number conversion uses try_from with unwrap_or(u32::MAX) which safely handles overflow. The comment at line 432 documents the intent.


524-531: Consider if returning null for unknown queries is the desired protocol behavior.

Currently, unhandled queries and decode failures return AnyCbor::from_encode(()) (null/unit). This is a reasonable fallback, but some clients might expect an error response instead. The warning logs provide good visibility for debugging.


596-623: LGTM!

The handle_session implementation follows the established pattern from chainsync::handle_session, with proper cancellation support and error handling.

Comment on lines +166 to +174
if output.era() >= pallas::ledger::traverse::Era::Alonzo {
Ok(q16::TransactionOutput::Current(
q16::PostAlonsoTransactionOutput {
address: address.into(),
amount: value,
inline_datum,
script_ref: None,
},
))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n -C3 'script_ref|reference_script|ScriptRef' --type rust

Repository: txpipe/dolos

Length of output: 4242


🏁 Script executed:

# Also, let's look at the full context of the convert_output_to_q16 function
sed -n '112,184p' src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 2541


🏁 Script executed:

# Check the function signature and input parameters
sed -n '100,115p' src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 515


🏁 Script executed:

# Search for MultiEraOutput definition and methods
rg -n 'impl.*MultiEraOutput|fn script_ref' --type rust -A 3 | head -50

Repository: txpipe/dolos

Length of output: 1568


🏁 Script executed:

# Check the pallas crate for MultiEraOutput and its methods
rg -n 'struct MultiEraOutput|enum MultiEraOutput' --type rust -A 10

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Look at the q16 module to understand PostAlonsoTransactionOutput structure
rg -n 'PostAlonsoTransactionOutput|struct.*TransactionOutput' --type rust -A 5 | head -60

Repository: txpipe/dolos

Length of output: 475


🏁 Script executed:

# Check imports and find MultiEraOutput origin
rg -n 'use.*MultiEraOutput|from pallas' src/serve/o7s_unix/statequery.rs | head -20

Repository: txpipe/dolos

Length of output: 134


🏁 Script executed:

# Check if script_ref is being used in other parts of the codebase with output
rg -n 'output\.script_ref\(\)' --type rust

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Search for the q16 module definition to understand the structure
find . -name "*.rs" -type f | xargs grep -l "mod q16\|pub mod q16" | head -5

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Find where q16 types are defined
find . -name "*.rs" -type f | xargs grep -l "PostAlonsoTransactionOutput" | head -10

Repository: txpipe/dolos

Length of output: 90


🏁 Script executed:

# Check the actual definition of PostAlonsoTransactionOutput
rg -n 'PostAlonsoTransactionOutput' --type rust -B 2 -A 8 | head -80

Repository: txpipe/dolos

Length of output: 834


🏁 Script executed:

# Verify MultiEraOutput has script_ref method from pallas documentation/source
rg -n 'script_ref' crates/minibf/src/mapping.rs -B 2 -A 2 | head -60

Repository: txpipe/dolos

Length of output: 760


🏁 Script executed:

# Check what type PostAlonsoTransactionOutput expects for script_ref
rg -n 'script_ref:' --type rust -B 5 -A 1 | head -40

Repository: txpipe/dolos

Length of output: 1037


🏁 Script executed:

# Look at the full definition in the file where PostAlonsoTransactionOutput is defined
sed -n '1,50p' src/serve/o7s_unix/statequery.rs | grep -n "use\|mod"

Repository: txpipe/dolos

Length of output: 546


🏁 Script executed:

# Check imports at the top of statequery.rs to find q16 definition
head -30 src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 1047


🏁 Script executed:

# Check the exact definition of q16::PostAlonsoTransactionOutput
rg -n 'PostAlonsoTransactionOutput' -A 10 | grep -A 10 "struct\|pub struct" | head -30

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Check if there's documentation or examples of how script_ref should be populated
rg -n 'script_ref.*Some\|Some.*script_ref' --type rust -B 2 -A 2 | head -30

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Let's search for any conversion or handling of script references in the entire statequery.rs file
cat src/serve/o7s_unix/statequery.rs | grep -n -i 'script'

Repository: txpipe/dolos

Length of output: 93


🏁 Script executed:

# Check the pallas crate documentation or source to understand MultiEraOutput better
# by looking at how it's used elsewhere in the project
rg -n 'MultiEraOutput.*script' --type rust

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Check if there's any mapping/conversion logic that might handle script_ref elsewhere
rg -n 'fn.*output.*q16|to_q16' --type rust -A 5

Repository: txpipe/dolos

Length of output: 969


Extract and convert the script reference from the output.

script_ref is hardcoded to None, which means reference scripts attached to UTxOs will not be returned to clients. The MultiEraOutput provides a script_ref() method (as evidenced by its use in other parts of the codebase like crates/minibf/src/mapping.rs), but it is not being called here. This could cause issues for DApps that rely on reference scripts for transaction building.

Extract the script reference using output.script_ref() and convert it appropriately for the q16::PostAlonsoTransactionOutput structure.

🤖 Prompt for AI Agents
In @src/serve/o7s_unix/statequery.rs around lines 166 - 174, The code currently
hardcodes script_ref: None when building q16::PostAlonsoTransactionOutput; call
output.script_ref() to extract the Option and convert it to the q16 type (e.g.,
using .map(|sr| sr.into()) or the same conversion helper used elsewhere) and set
script_ref to that value so reference scripts are returned; update the block
constructing q16::PostAlonsoTransactionOutput (the code that sets address,
amount, inline_datum) to use output.script_ref().map(...) instead of None.

Comment on lines +474 to +484
let era_index = match edge.protocol {
0..=1 => 0, // Byron
2 => 1, // Shelley
3 => 2, // Allegra
4 => 3, // Mary
5..=6 => 4, // Alonzo
7 => 5, // Babbage
_ => 6, // Conway
};

AnyCbor::from_encode(era_index as u16)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Cardano era protocol version mapping Byron Shelley Allegra Mary Alonzo Babbage Conway

💡 Result:

Here’s the protocol-version (major.minor) mapping by Cardano era:

  • Byron — 0.0, 1.0. [1][2]
  • Shelley — 2.0. [1][2]
  • Allegra — 3.0. [1][2]
  • Mary — 4.0. [1][2]
  • Alonzo — 5.0 (initial) and 6.0 (intra-era update). [2][3]
  • Babbage — 7.0 (Babbage/Vasil entry) and 8.0 (subsequent intra-era update). [2][3]
  • Conway — 9.0 (Chang) and 10.0+ (Plomin / later governance updates). [2][3]

Sources: Cardano Developer docs / era table and CIP‑59 feature table; ouroboros‑consensus era-transition notes. [1][2][3]

References
[1] Cardano Developer Portal — Cardano ledger eras / governance (era ↔ protocol version table).
[2] CIP‑59 feature table (era, slot/epoch, protocol version entries).
[3] ouroboros‑consensus — era transition / protocol version governance notes.


🏁 Script executed:

# Search for protocol version references in the codebase
rg "protocol.*version|era.*protocol|Byron|Shelley|Allegra|Mary|Alonzo|Babbage|Conway" --type rust -i -A 2 -B 2

Repository: txpipe/dolos

Length of output: 50369


Fix incomplete era index mapping for protocol versions 8 and beyond.

The mapping omits protocol version 8 (Babbage intra-era update), causing it to incorrectly map to Conway (6) instead of Babbage (5). Additionally, the catch-all _ => 6 should explicitly handle Conway protocol versions 9+ rather than relying on a wildcard, which would incorrectly classify any future protocol versions.

Update the match expression to:

  • 7..=8 => 5 for Babbage
  • 9..=10 => 6 for Conway (or extend as needed for future Conway protocol versions)
Current code
let era_index = match edge.protocol {
    0..=1 => 0, // Byron
    2 => 1,     // Shelley
    3 => 2,     // Allegra
    4 => 3,     // Mary
    5..=6 => 4, // Alonzo
    7 => 5,     // Babbage
    _ => 6,     // Conway
};
🤖 Prompt for AI Agents
In @src/serve/o7s_unix/statequery.rs around lines 474 - 484, The match for
computing era_index (used when constructing AnyCbor::from_encode(era_index as
u16)) incorrectly groups protocol 8 into the Conway fallback; update the match
on edge.protocol in the era_index binding to explicitly map Babbage variants to
5 and Conway variants to 6 (e.g. change the arms to include 7..=8 => 5 for
Babbage and 9..=10 => 6 for Conway or extend ranges as needed) and remove the
wildcard catch-all that currently maps future/unknown versions to Conway so the
mapping is explicit.

Copy link
Copy Markdown

@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: 2

🤖 Fix all issues with AI agents
In `@src/serve/o7s_unix/statequery.rs`:
- Line 66: The multiplication era.slot_length * 1000 can overflow; replace it
with a checked multiply and handle the error before calling encoder.u64. For
example, compute let millis = era.slot_length.checked_mul(1000).ok_or(/*
appropriate overflow error */)? and then call encoder.u64(millis)?; update the
surrounding function (the code that calls encoder.u64) to propagate or map the
overflow error appropriately so overflows don't panic or wrap.
- Around line 433-443: The current snippet silently treats a decode failure as
block number 0; instead, when calling MultiEraBlock::decode on the raw tip from
self.domain.archive().get_tip(), propagate the decode error rather than
returning 0: retrieve the tip, attempt to decode with
MultiEraBlock::decode(&raw) and convert any decode error into the crate Error
(e.g., via map_err or the ? operator) so the surrounding function returns an
error on decode failure; only fall back to 0 when get_tip() returns None, not
when decoding fails.
♻️ Duplicate comments (1)
src/serve/o7s_unix/statequery.rs (1)

179-187: script_ref is still hardcoded to None.

This was previously flagged. Reference scripts attached to UTxOs will not be returned to clients, which could cause issues for DApps that rely on reference scripts for transaction building.

🧹 Nitpick comments (1)
src/serve/o7s_unix/statequery.rs (1)

459-463: The ChainPoint::Slot match arms at lines 462 and 510 are unreachable defensive code.

The tip_cursor() method (lines 323-347) converts all ChainPoint::Slot variants to ChainPoint::Specific before returning, so it should never return Slot. The match arms that handle ChainPoint::Slot(_) => OPoint::Origin at lines 462 and 510 are therefore unreachable.

However, note that GetEpochNo at line 524 explicitly handles ChainPoint::Slot(s), suggesting developers anticipated this case despite the implementation preventing it. Consider either:

  • Removing these unreachable arms (if confident in tip_cursor's guarantee)
  • Adding a comment explaining they're defensive
  • Ensuring consistency across all query handlers
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3ed42c and d1f706f.

📒 Files selected for processing (1)
  • src/serve/o7s_unix/statequery.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/serve/o7s_unix/statequery.rs (4)
crates/core/src/lib.rs (1)
  • era (79-81)
src/prelude.rs (1)
  • server (56-58)
crates/cardano/src/eras.rs (1)
  • edge (96-99)
src/serve/o7s_unix/mod.rs (1)
  • handle_session (16-49)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check Build
🔇 Additional comments (6)
src/serve/o7s_unix/statequery.rs (6)

1-16: LGTM!

Imports and the EraHistoryResponse struct are well-organized and appropriate for the state query functionality.


95-123: LGTM!

Proper validation and error handling for building the era history response.


199-242: LGTM!

Comprehensive Plutus data conversion handling all variants correctly with proper recursive processing.


244-314: LGTM!

Good implementation with proper deduplication, fallback to payment credential lookup, and comprehensive debug logging.


487-496: Era index mapping has been corrected.

The mapping now properly handles protocol version 8 as Babbage (7..=8 => 5) and explicitly maps Conway versions (9..=10 => 6). The wildcard fallback for unknown future versions is a reasonable default.


610-637: LGTM!

Clean async session handling with proper cancellation support via tokio::select! and appropriate error propagation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread src/serve/o7s_unix/statequery.rs Outdated
// EraParams
encoder.array(4)?;
encoder.u64(era.epoch_length)?;
encoder.u64(era.slot_length * 1000)?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential integer overflow on slot_length multiplication.

This line lacks the overflow protection applied elsewhere in this function. If slot_length exceeds u64::MAX / 1000, this will overflow in debug builds (panic) or wrap in release builds.

🔧 Suggested fix
-            encoder.u64(era.slot_length * 1000)?;
+            encoder.u64(era.slot_length.saturating_mul(1000))?;
📝 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
encoder.u64(era.slot_length * 1000)?;
encoder.u64(era.slot_length.saturating_mul(1000))?;
🤖 Prompt for AI Agents
In `@src/serve/o7s_unix/statequery.rs` at line 66, The multiplication
era.slot_length * 1000 can overflow; replace it with a checked multiply and
handle the error before calling encoder.u64. For example, compute let millis =
era.slot_length.checked_mul(1000).ok_or(/* appropriate overflow error */)? and
then call encoder.u64(millis)?; update the surrounding function (the code that
calls encoder.u64) to propagate or map the overflow error appropriately so
overflows don't panic or wrap.

Comment thread src/serve/o7s_unix/statequery.rs
Copy link
Copy Markdown

@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

🤖 Fix all issues with AI agents
In `@src/serve/o7s_unix/statequery.rs`:
- Around line 167-177: The two Option transformations use and_then with closures
that always return Some(...); change them to use map so the closure directly
returns the transformed value: update the inline_datum assignment (which
currently calls output.datum().and_then(|d| match d { DatumOption::Hash(h) =>
Some(q16::DatumOption::Hash(h)), DatumOption::Data(data) =>
Some(q16::DatumOption::Data(pallas::codec::utils::CborWrap(convert_plutus_data(&data.0))))
})) to use output.datum().map(|d| match d { DatumOption::Hash(h) =>
q16::DatumOption::Hash(h), DatumOption::Data(data) =>
q16::DatumOption::Data(pallas::codec::utils::CborWrap(convert_plutus_data(&data.0)))
}); likewise replace the datum_hash assignment (currently using and_then
returning Some(h) / Some(data.original_hash())) with output.datum().map(|d|
match d { DatumOption::Hash(h) => h, DatumOption::Data(data) =>
data.original_hash() }); this preserves types for inline_datum and datum_hash
while eliminating the clippy warning.
♻️ Duplicate comments (1)
src/serve/o7s_unix/statequery.rs (1)

179-186: Return reference scripts for post‑Alonzo outputs.

script_ref is hardcoded to None, so reference scripts on UTxOs are dropped. This is the same gap flagged earlier.

Proposed fix
-    if output.era() >= pallas::ledger::traverse::Era::Alonzo {
+    if output.era() >= pallas::ledger::traverse::Era::Alonzo {
+        let script_ref = output.script_ref().map(|sr| sr.into());
         Ok(q16::TransactionOutput::Current(
             q16::PostAlonsoTransactionOutput {
                 address: address.into(),
                 amount: value,
                 inline_datum,
-                script_ref: None,
+                script_ref,
             },
         ))
     } else {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d980449 and f4f8eaa.

📒 Files selected for processing (1)
  • src/serve/o7s_unix/statequery.rs
🧰 Additional context used
🧬 Code graph analysis (1)
src/serve/o7s_unix/statequery.rs (3)
src/prelude.rs (1)
  • server (56-58)
crates/cardano/src/eras.rs (1)
  • edge (96-99)
src/serve/o7s_unix/mod.rs (1)
  • handle_session (16-49)
🪛 GitHub Actions: CI
src/serve/o7s_unix/statequery.rs

[error] 167-167: Clippy error: using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y) (src/serve/o7s_unix/statequery.rs:167)

🪛 GitHub Check: Check Build
src/serve/o7s_unix/statequery.rs

[failure] 431-431:
using map over inspect


[failure] 174-174:
using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y)


[failure] 167-167:
using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y)

🔇 Additional comments (1)
src/serve/o7s_unix/statequery.rs (1)

430-434: The proposed fix using inspect will not compile—Option lacks an inspect method in std.

While Clippy does suggest inspect for side-effect-only closures, inspect is only available on Iterator in the standard library. For Option/Result, there is no built-in inspect method. The current code is correct; keep the .map() as-is unless the project adds a helper trait or external crate providing inspect for these types.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +167 to +177
let inline_datum = output.datum().and_then(|d| match d {
DatumOption::Hash(h) => Some(q16::DatumOption::Hash(h)),
DatumOption::Data(data) => Some(q16::DatumOption::Data(pallas::codec::utils::CborWrap(
convert_plutus_data(&data.0),
))),
});

let datum_hash = output.datum().and_then(|d| match d {
DatumOption::Hash(h) => Some(h),
DatumOption::Data(data) => Some(data.original_hash()),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and examine the code around lines 167-177
if [ -f "src/serve/o7s_unix/statequery.rs" ]; then
  echo "=== File found, examining lines 160-185 ==="
  sed -n '160,185p' "src/serve/o7s_unix/statequery.rs" | cat -n
else
  echo "File not found at src/serve/o7s_unix/statequery.rs"
  # Try to locate it
  find . -name "statequery.rs" -type f
fi

Repository: txpipe/dolos

Length of output: 1196


🏁 Script executed:

# Also check the structure to understand the file better
wc -l src/serve/o7s_unix/statequery.rs 2>/dev/null || echo "File not found"

Repository: txpipe/dolos

Length of output: 92


🏁 Script executed:

# Check the imports and understand the types involved
head -50 src/serve/o7s_unix/statequery.rs | cat -n

Repository: txpipe/dolos

Length of output: 2415


🏁 Script executed:

# Check if there's any type annotation that might affect the fix
sed -n '150,180p' src/serve/o7s_unix/statequery.rs | cat -n

Repository: txpipe/dolos

Length of output: 1332


🏁 Script executed:

# Verify the function signature of output.datum() to understand the type
rg -A 5 -B 5 "fn datum" src/serve/o7s_unix/statequery.rs | head -30

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Check if there are any clippy configuration or CI checks related to warnings
find . -name "clippy.toml" -o -name "deny.toml" -o -name ".clippy.toml" | xargs cat 2>/dev/null

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Look for any CI configuration that might enforce clippy rules
find . -name "*.yml" -o -name "*.yaml" | xargs grep -l "clippy" 2>/dev/null | head -3 | xargs cat

Repository: txpipe/dolos

Length of output: 1360


🏁 Script executed:

# Check what the output.datum() method returns and understand the type flow
rg -B 10 "let inline_datum = output.datum" src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 359


🏁 Script executed:

# Look for where inline_datum is used to confirm the type
rg "inline_datum" src/serve/o7s_unix/statequery.rs | head -10

Repository: txpipe/dolos

Length of output: 146


🏁 Script executed:

# Check the DatumOption definition and q16::DatumOption to ensure the conversion is correct
rg -A 3 "enum DatumOption\|q16::DatumOption" src/serve/o7s_unix/statequery.rs | head -20

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Find the definition of convert_plutus_data and related types
rg -B 5 "fn convert_plutus_data" src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 181


🏁 Script executed:

# Check what type output is and what datum() returns
rg -B 20 "let inline_datum = output.datum" src/serve/o7s_unix/statequery.rs | grep -E "fn |output|MultiEraOutput"

Repository: txpipe/dolos

Length of output: 179


🏁 Script executed:

# Search for the function signature that contains this code
rg -B 30 "let inline_datum = output.datum" src/serve/o7s_unix/statequery.rs | head -50

Repository: txpipe/dolos

Length of output: 1152


🏁 Script executed:

# Get the full function signature
rg -B 50 "let inline_datum = output.datum" src/serve/o7s_unix/statequery.rs | grep -E "^fn |fn convert_output"

Repository: txpipe/dolos

Length of output: 148


🏁 Script executed:

# Try to find the function definition more carefully
sed -n '100,180p' src/serve/o7s_unix/statequery.rs | cat -n

Repository: txpipe/dolos

Length of output: 3300


🏁 Script executed:

# Confirm the return type of output.datum() by checking the MultiEraOutput trait/impl
rg -A 5 "fn datum" $(find . -name "*.rs" -type f | xargs grep -l "MultiEraOutput" | head -5)

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Check if there are existing clippy issues in CI by looking at workflow runs or issues
rg "and_then.*Some\(" src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Look at the exact clippy lint that would apply here
# The pattern is and_then(|x| Some(...)) which should be map(|x| ...)
# Let's verify the type signature more carefully
sed -n '168,180p' src/serve/o7s_unix/statequery.rs | cat -n

Repository: txpipe/dolos

Length of output: 649


🏁 Script executed:

# Check if datum() returns Option
rg "fn datum" -A 3 $(find . -name "*.rs" | head -20)

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Let's verify this is a real clippy issue by searching for the exact pattern in the codebase
rg "and_then\(\|.*Some\(" src/

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Try to understand the exact clippy rule being violated by checking Rust documentation
# For now, let's verify by looking at the pattern more carefully
# and_then with a closure that always returns Some is indeed the lint pattern

# Check if there are other instances of this pattern that have been fixed
rg "\.map\(\|.*match" src/serve/o7s_unix/statequery.rs | head -5

Repository: txpipe/dolos

Length of output: 38


🏁 Script executed:

# Let's verify the Rust standard library behavior of and_then vs map
# and_then(|x| Some(y)) is equivalent to map(|x| y)
# This is a well-known pattern that clippy warns about
# Let's check if there's a cargo.toml to understand the Rust version

cat Cargo.toml | head -20

Repository: txpipe/dolos

Length of output: 533


🏁 Script executed:

# The key insight: output.datum() returns Option<DatumOption>
# When you use and_then with a closure that always returns Some(...),
# it's the same as using map with a closure that returns the inner value
# This is what clippy warns about

# Let's verify the proposed fix is syntactically correct by checking the exact lines again
sed -n '167,180p' src/serve/o7s_unix/statequery.rs

Repository: txpipe/dolos

Length of output: 619


Replace and_then with map for Option conversion.

The code uses and_then(|d| match d { ... => Some(...) }) pattern, where every match arm returns Some(...). This is equivalent to using map with direct value returns, which clippy will flag as a warning. Since the CI enforces clippy with -D warnings, this must be fixed.

Proposed fix
-    let inline_datum = output.datum().and_then(|d| match d {
-        DatumOption::Hash(h) => Some(q16::DatumOption::Hash(h)),
-        DatumOption::Data(data) => Some(q16::DatumOption::Data(pallas::codec::utils::CborWrap(
-            convert_plutus_data(&data.0),
-        ))),
-    });
+    let inline_datum = output.datum().map(|d| match d {
+        DatumOption::Hash(h) => q16::DatumOption::Hash(h),
+        DatumOption::Data(data) => q16::DatumOption::Data(pallas::codec::utils::CborWrap(
+            convert_plutus_data(&data.0),
+        )),
+    });

-    let datum_hash = output.datum().and_then(|d| match d {
-        DatumOption::Hash(h) => Some(h),
-        DatumOption::Data(data) => Some(data.original_hash()),
-    });
+    let datum_hash = output.datum().map(|d| match d {
+        DatumOption::Hash(h) => h,
+        DatumOption::Data(data) => data.original_hash(),
+    });
📝 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 inline_datum = output.datum().and_then(|d| match d {
DatumOption::Hash(h) => Some(q16::DatumOption::Hash(h)),
DatumOption::Data(data) => Some(q16::DatumOption::Data(pallas::codec::utils::CborWrap(
convert_plutus_data(&data.0),
))),
});
let datum_hash = output.datum().and_then(|d| match d {
DatumOption::Hash(h) => Some(h),
DatumOption::Data(data) => Some(data.original_hash()),
});
let inline_datum = output.datum().map(|d| match d {
DatumOption::Hash(h) => q16::DatumOption::Hash(h),
DatumOption::Data(data) => q16::DatumOption::Data(pallas::codec::utils::CborWrap(
convert_plutus_data(&data.0),
)),
});
let datum_hash = output.datum().map(|d| match d {
DatumOption::Hash(h) => h,
DatumOption::Data(data) => data.original_hash(),
});
🧰 Tools
🪛 GitHub Actions: CI

[error] 167-167: Clippy error: using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y) (src/serve/o7s_unix/statequery.rs:167)

🪛 GitHub Check: Check Build

[failure] 174-174:
using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y)


[failure] 167-167:
using Option.and_then(|x| Some(y)), which is more succinctly expressed as map(|x| y)

🤖 Prompt for AI Agents
In `@src/serve/o7s_unix/statequery.rs` around lines 167 - 177, The two Option
transformations use and_then with closures that always return Some(...); change
them to use map so the closure directly returns the transformed value: update
the inline_datum assignment (which currently calls output.datum().and_then(|d|
match d { DatumOption::Hash(h) => Some(q16::DatumOption::Hash(h)),
DatumOption::Data(data) =>
Some(q16::DatumOption::Data(pallas::codec::utils::CborWrap(convert_plutus_data(&data.0))))
})) to use output.datum().map(|d| match d { DatumOption::Hash(h) =>
q16::DatumOption::Hash(h), DatumOption::Data(data) =>
q16::DatumOption::Data(pallas::codec::utils::CborWrap(convert_plutus_data(&data.0)))
}); likewise replace the datum_hash assignment (currently using and_then
returning Some(h) / Some(data.original_hash())) with output.datum().map(|d|
match d { DatumOption::Hash(h) => h, DatumOption::Data(data) =>
data.original_hash() }); this preserves types for inline_datum and datum_hash
while eliminating the clippy warning.

@scarmuega scarmuega force-pushed the main branch 2 times, most recently from 295b558 to 09d0b81 Compare January 28, 2026 07:14
@sofia-bobbiesi sofia-bobbiesi deleted the query-utxos-on-cardano-cli branch February 3, 2026 18:45
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.

2 participants