Skip to content

fix(minibf): return affected addresses from /blocks/{hash_or_number}/addresses#1124

Open
slowbackspace wants to merge 4 commits into
mainfrom
fix/minibf-block-addresses
Open

fix(minibf): return affected addresses from /blocks/{hash_or_number}/addresses#1124
slowbackspace wants to merge 4 commits into
mainfrom
fix/minibf-block-addresses

Conversation

@slowbackspace

@slowbackspace slowbackspace commented Jul 22, 2026

Copy link
Copy Markdown

Closes #1068

Problem

/blocks/{hash_or_number}/addresses returned a plain list of tx hashes — the same shape as /blocks/{hash_or_number}/txs — instead of Blockfrost's contract: one entry per affected address with the transactions that affected it. The existing IntoModel<Vec<BlockContentAddressesInner>> impl was dead code, and it only looked at outputs, emitted one entry per output (no grouping), and unwrap()ed address decoding.

Implementation

An address is affected by a tx if it received a regular output or had a UTxO spent by a regular input. Since inputs are only (tx_hash, index) pointers, input-side addresses require resolving the referenced transactions — done with the same dep-resolution pattern TxModelBuilder uses for /txs/{hash}/utxos: BlockModelBuilder::required_input_deps() collects the deduped source-tx hashes across the block, the handler fetches them via get_tx_batch, and load_dep feeds them back for resolution during mapping.

Response semantics (matching Blockfrost):

  • one entry per address, sorted bytewise-ascending by encoded address string; the order param is ignored, as in Blockfrost
  • per address, tx hashes in block order, each at most once regardless of how many outputs/inputs touched it
  • count/page paginate whole address entries; past-the-end pages return 200 []

Deliberate divergences from Blockfrost

  • Phase-2-failed txs are included: their collateral inputs and collateral-return outputs move funds, so those addresses are affected in ledger terms. Blockfrost omits them only because its SQL reads db-sync's tx_in/tx_out (valid txs only) — treated as a Blockfrost bug rather than behavior to reproduce. Documented in a code comment.
  • Genesis virtual block: Blockfrost returns 200 [] for /blocks/0/addresses / the genesis hash; dolos returns 404 (the genesis block isn't in the archive). Kept simple deliberately — no practical traffic on that path.

Testing

Verified locally against the official blockfrost-tests suite targeting a dolos preview node bootstrapped from a full snapshot and synced to tip:

SERVICE_NAME=blockfrost-tests yarn test:preview -t "blocks/:hash_or_number/addresses"

Result: 4/4 passedblocks/d452532f…/addresses, blocks/1846333/addresses, and both ?count=3&page=1 variants, matching the pinned fixture responses (which include input-side addresses on a deep-history block).

  • 7 new unit tests: happy path (grouping/dedup + alphabetical ordering), order param ignored, pagination, bad request, not found, fault-injected 500.
  • Live verification against Blockfrost preview: 28/28 blocks byte-identical (including empty blocks and a block with intra-block tx chaining), plus pagination windows, past-the-end pages, by-hash lookups, and 400/404 error bodies.
  • cargo +nightly fmt --check, cargo clippy --workspace --all-targets --all-features (zero warnings), cargo test -p dolos-minibf (227 passing).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Block address endpoints now return, per address, the set of transaction hashes that “touch” it (including both produced outputs and resolved consumed-input effects).
  • Bug Fixes

    • Address ordering is now deterministic (alphabetical) and consistently ignores the order parameter.
    • Missing archive dependencies degrade gracefully while still producing correct address mappings (including collateral-return cases).
  • Tests

    • Expanded address endpoint coverage for alphabetical sorting, count/page pagination (including empty past-the-end pages), and 400/404/500 error handling.

The /blocks/{hash_or_number}/addresses endpoint returned a plain list of
tx hashes instead of Blockfrost's address entries. Rebuild it to return
addresses affected by the block's transactions, resolving spent inputs
through the archive to include input-side addresses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 51aad6b4-15b7-40a9-8bbd-3b59236fc002

📥 Commits

Reviewing files that changed from the base of the PR and between 09c22cc and 0a5320b.

📒 Files selected for processing (2)
  • crates/minibf/src/mapping.rs
  • crates/minibf/src/routes/blocks.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/minibf/src/routes/blocks.rs
  • crates/minibf/src/mapping.rs

📝 Walkthrough

Walkthrough

The block addresses endpoint now returns deterministic address-to-transaction mappings, including addresses from consumed inputs resolved through dependent transactions. It loads dependencies before modeling results and adds coverage for ordering, pagination, and error responses.

Changes

Block address resolution

Layer / File(s) Summary
Address dependency resolution
crates/minibf/src/mapping.rs
BlockModelBuilder caches required transaction dependencies and derives deterministic address mappings from produced outputs and resolved consumed inputs.
Address endpoint integration
crates/minibf/src/routes/blocks.rs
The address route loads dependencies, returns BlockContentAddressesInner objects, and applies pagination.
Endpoint behavior validation
crates/minibf/src/routes/blocks.rs
Tests cover sorting, ignored ordering, pagination, collateral-return resolution, invalid identifiers, missing blocks, and archive failures.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant by_hash_or_number_addresses
  participant BlockModelBuilder
  participant domain.get_tx_batch
  Client->>by_hash_or_number_addresses: request block addresses
  by_hash_or_number_addresses->>BlockModelBuilder: create from BlockBody
  BlockModelBuilder->>by_hash_or_number_addresses: required_input_deps
  by_hash_or_number_addresses->>domain.get_tx_batch: fetch dependency CBOR
  by_hash_or_number_addresses->>BlockModelBuilder: load_dep for each dependency
  by_hash_or_number_addresses->>Client: return paginated JSON
Loading

Suggested labels: bug, area:minibf

Suggested reviewers: scarmuega

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: fixing minibf block-address responses to return affected addresses.
Linked Issues check ✅ Passed The PR implements the linked fix by decoding blocks and resolving input/output addresses into Blockfrost-style affected-address entries.
Out of Scope Changes check ✅ Passed The changes stay focused on the /blocks//addresses endpoint plus supporting tests and dependency loading.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/minibf-block-addresses

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.

@slowbackspace
slowbackspace marked this pull request as ready for review July 23, 2026 09:31
@slowbackspace
slowbackspace requested a review from scarmuega as a code owner July 23, 2026 09:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/minibf/src/routes/blocks.rs (1)

406-440: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Missing dependency transactions are silently dropped, producing an incomplete address list with no signal.

When domain.get_tx_batch(deps) returns None for a hash (e.g. the spent tx is outside the archive's retention window), the loop at Lines 423-427 simply skips load_dep for that key. Downstream, BlockModelBuilder::into_model (crates/minibf/src/mapping.rs Lines 2324-2337) then can't resolve that input's address and just omits it from touched — no error, no log, nothing to tell the caller the response is incomplete. Since real Blockfrost typically has full history and would resolve every input address, this is a real compatibility gap for older/pruned blocks, and it fails silently rather than surfacing the limitation.

At minimum, log when a required dependency is missing so this degradation is observable; consider whether returning an error is more appropriate than silently serving a partial result for an endpoint whose entire purpose is byte-for-byte parity with Blockfrost.

🩹 Minimal observability fix
     for (key, cbor) in deps.iter() {
         if let Some(cbor) = cbor {
             builder.load_dep(*key, cbor)?;
+        } else {
+            tracing::warn!(tx_hash = %key, "missing dependency tx while resolving block addresses; result may be incomplete");
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minibf/src/routes/blocks.rs` around lines 406 - 440, Add observable
handling for missing required dependencies in by_hash_or_number_addresses: when
get_tx_batch returns None for a dependency, log the missing transaction
identifier before continuing. Preserve the existing successful load_dep behavior
and response flow; do not silently skip the missing dependency without a
diagnostic.
🧹 Nitpick comments (2)
crates/minibf/src/mapping.rs (1)

2001-2010: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider using EraCbor accessors instead of tuple indices.

cbor.0/cbor.1 bypass the .era()/.cbor() accessor methods already defined on EraCbor (crates/core/src/lib.rs). Purely cosmetic, but using the accessors keeps this call site consistent with the rest of the codebase and insulates it from future field reordering.

♻️ Proposed tweak
-        let era = try_into_or_500!(cbor.0);
+        let era = try_into_or_500!(cbor.era());

-        let tx = MultiEraTx::decode_for_era(era, &cbor.1)
+        let tx = MultiEraTx::decode_for_era(era, cbor.cbor())
             .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minibf/src/mapping.rs` around lines 2001 - 2010, Update load_dep to
use EraCbor’s era() and cbor() accessors instead of direct cbor.0 and cbor.1
tuple indexing, while preserving the existing decoding and error-handling
behavior.
crates/minibf/src/routes/blocks.rs (1)

608-715: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for input-side address resolution and past-the-end pagination.

The new tests cover output-side addresses, alphabetical ordering, ignoring order, one pagination case, and error paths, but none of them exercise the actual new capability this PR introduces — resolving addresses of consumed inputs via required_input_deps/load_dep. There's also no test for the stated "returns 200 [] for past-the-end pages" behavior mentioned in the PR objectives.

Consider adding a test with a fixture block whose first tx spends an input from a known predecessor address, plus a pagination test using a page/count combination beyond the last entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minibf/src/routes/blocks.rs` around lines 608 - 715, Extend the
address endpoint tests around blocks_by_hash_or_number_addresses_happy_path to
use a fixture block whose transaction consumes an input from a known predecessor
address, then assert that address and its transaction are returned through
required_input_deps/load_dep resolution. Add a pagination test for
blocks_by_hash_or_number_addresses using page/count beyond the final entry and
assert the endpoint returns HTTP 200 with an empty list.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/minibf/src/routes/blocks.rs`:
- Around line 406-440: Add observable handling for missing required dependencies
in by_hash_or_number_addresses: when get_tx_batch returns None for a dependency,
log the missing transaction identifier before continuing. Preserve the existing
successful load_dep behavior and response flow; do not silently skip the missing
dependency without a diagnostic.

---

Nitpick comments:
In `@crates/minibf/src/mapping.rs`:
- Around line 2001-2010: Update load_dep to use EraCbor’s era() and cbor()
accessors instead of direct cbor.0 and cbor.1 tuple indexing, while preserving
the existing decoding and error-handling behavior.

In `@crates/minibf/src/routes/blocks.rs`:
- Around line 608-715: Extend the address endpoint tests around
blocks_by_hash_or_number_addresses_happy_path to use a fixture block whose
transaction consumes an input from a known predecessor address, then assert that
address and its transaction are returned through required_input_deps/load_dep
resolution. Add a pagination test for blocks_by_hash_or_number_addresses using
page/count beyond the final entry and assert the endpoint returns HTTP 200 with
an empty list.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d4f82c1-dcd8-4c40-9219-a208d6c92cb5

📥 Commits

Reviewing files that changed from the base of the PR and between de69203 and 09c22cc.

📒 Files selected for processing (2)
  • crates/minibf/src/mapping.rs
  • crates/minibf/src/routes/blocks.rs

@slowbackspace

Copy link
Copy Markdown
Author

@coderabbitai your summary is wrong. Bug fixes section includes
"Improved handling of invalid block identifiers, missing blocks, and archive retrieval errors.", but this PR doesn't address any of that. Check the original PR description and improve your summary.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

@slowbackspace You’re right—those error-path tests verify existing behavior and should not have been described as bug fixes. I’ll regenerate the summary to focus on the endpoint’s response-shape correction: resolving regular input/output addresses, grouping and deduplicating transaction hashes per address, bytewise address ordering, whole-entry pagination (including past-the-end 200 []), ignored order, and inclusion of Phase-2-failed transactions.

🐇

✅ Action performed

Summary regeneration triggered.

@slowbackspace
slowbackspace marked this pull request as draft July 23, 2026 09:45
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@slowbackspace

Copy link
Copy Markdown
Author

@coderabbitai On the silently-skipped missing deps: this is deliberate, and consistent with the crate's existing behavior — /txs/{hash}/utxos degrades exactly the same way when a spent tx is outside the archive (unresolved inputs are served with missing fields, no error, no log). On a full-archive node — the deployment profile that actually matches Blockfrost's semantics — every dep resolves and the gap doesn't exist; the fixture runs and live byte-parity sweeps confirm that.

Making one endpoint strict (or noisy) while its siblings degrade silently would trade a consistency problem for a visibility one. The right fix is crate-wide — e.g. a single policy at the get_tx_batch/facade level (log, or a strict mode that 4xx/5xxs when history is insufficient) applied to all history-dependent endpoints. That deserves its own issue rather than a one-endpoint deviation in this PR. Added a code comment documenting the degradation in e1b8cc3 so the behavior reads as a decision rather than an oversight.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@slowbackspace Agreed. Given the established /txs/{hash}/utxos behavior, the full-archive deployment contract, and the added documentation, endpoint-specific strictness or logging would be an inconsistent regression. I’m withdrawing the concern.

A crate-wide policy at the get_tx_batch/facade boundary is the appropriate place to address observability or strict-history modes later. Would you like me to open that follow-up issue?

🐇💭


✏️ Learnings added
Learnt from: slowbackspace
URL: https://github.com/txpipe/dolos/pull/1124

Timestamp: 2026-07-23T09:50:42.431Z
Learning: In the Rust `crates/minibf` crate, history-dependent endpoints deliberately degrade silently when `get_tx_batch` cannot resolve archived spent-transaction dependencies. This behavior is consistent with `/txs/{hash}/utxos`: unresolved inputs are returned with missing fields and no error or log. Full-archive deployments resolve all dependencies and provide Blockfrost-compatible semantics. Any strict-mode or observability change should be implemented as a crate-wide policy at the `get_tx_batch`/facade layer rather than as endpoint-specific behavior.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@slowbackspace
slowbackspace marked this pull request as ready for review July 23, 2026 09:56
output_at indexes regular outputs only, so an input spending a failed
tx's collateral return (produced at index |outputs|) would not resolve
and its address would be omitted. produces_at is validity-aware and
matches the existing lookup paths in the addresses/assets routes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes the dolos-minibf implementation of Blockfrost’s /blocks/{hash_or_number}/addresses endpoint so it returns one entry per affected address (with the transactions that touched it) rather than a flat list of transaction hashes, aligning the API shape with Blockfrost’s contract.

Changes:

  • Updated /blocks/{hash_or_number}/addresses to return Vec<BlockContentAddressesInner> and paginate address entries (ignoring order, matching Blockfrost).
  • Added dependency collection/loading to resolve input-side addresses by fetching referenced transactions’ CBOR (with graceful skipping when deps are missing).
  • Implemented address grouping + per-address tx deduplication and added a focused test suite for ordering, pagination, and error cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
crates/minibf/src/routes/blocks.rs Switches the addresses endpoint response type/semantics, loads input dependencies via get_tx_batch, and adds endpoint-level tests.
crates/minibf/src/mapping.rs Extends BlockModelBuilder with dependency storage + input-dep discovery, and maps blocks to grouped affected-address entries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/minibf/src/routes/blocks.rs
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.

minibf: fix /blocks/<block>/addresses

2 participants