Skip to content

chore(minibf): Implement /addresses/address#925

Merged
scarmuega merged 3 commits intomainfrom
chore/implement-addresses-address-endpoint
Mar 4, 2026
Merged

chore(minibf): Implement /addresses/address#925
scarmuega merged 3 commits intomainfrom
chore/implement-addresses-address-endpoint

Conversation

@gonzalezzfelipe
Copy link
Contributor

@gonzalezzfelipe gonzalezzfelipe commented Feb 28, 2026

Summary by CodeRabbit

  • New Features

    • Added address lookup endpoint returning enriched address details (including stake and script info).
  • Improvements

    • Improved address parsing to distinguish payment/Shelley/Byron forms and unify key-based queries.
    • Standardized JSON error responses for consistent status, error, and message fields.
    • Exposed richer address model types for clearer API output.
  • Tests

    • Expanded tests covering new address variants, not-found, bad-request, and error cases.

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

Warning

Rate limit exceeded

@gonzalezzfelipe has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 11 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4800dc3 and 1195cc6.

📒 Files selected for processing (1)
  • docs/content/apis/minibf.mdx
📝 Walkthrough

Walkthrough

Adds a new /addresses/{address} endpoint with richer address parsing (Payment, Shelley, Byron), key-based UTXO lookups, address-to-model mapping, and consolidated error response construction via a private ErrorBody type. Includes tests and small refactors in tests/pagination.

Changes

Cohort / File(s) Summary
Address routing
crates/minibf/src/lib.rs
Register new route /addresses/{address}routes::addresses::by_address::<D>.
Address parsing, resolution, handler & tests
crates/minibf/src/routes/addresses.rs
Replace raw-byte parsing with ParsedAddress variants (Payment/Shelley/Byron); add by_address handler, refs_for_parsed_address, is_address_in_chain, amount aggregation, and extended tests for parsing/404/errors.
Model mapping & conversions
crates/minibf/src/mapping.rs
Add AddressKind, AddressModelBuilder, and IntoModel implementations (AddressContent, amounts, script/address string conversions); import AddressContent/Type for mapping.
Error response consolidation
crates/minibf/src/error.rs
Introduce private ErrorBody (serializable) and replace inline serde_json::json! payloads with ErrorBody::new(...) in IntoResponse implementations.
Pagination / tests minor edits
crates/minibf/src/pagination.rs, crates/minibf/src/routes/pools.rs, crates/minibf/src/test_support.rs
Formatting/signature reformat and switch test SyntheticBlockConfig construction from mutation to struct-literal (..Default::default()), no behavior changes.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Router as "Router\n(minibf)"
    participant Parser as "AddressParser"
    participant UTXO as "UTXOResolver"
    participant DB as "Database"
    participant Validator as "ChainValidator"
    participant Builder as "ResponseBuilder"

    Client->>Router: GET /addresses/{address}
    Router->>Parser: parse_address(address_str)
    Parser-->>Router: ParsedAddress (Payment/Shelley/Byron)

    Router->>UTXO: refs_for_parsed_address(parsed)
    UTXO->>DB: Query UTXO refs by key
    DB-->>UTXO: UTXO refs
    UTXO-->>Router: refs

    Router->>Validator: is_address_in_chain(address)
    Validator->>DB: Stream blocks / scan for address
    DB-->>Validator: found / not_found
    Validator-->>Router: bool

    alt refs found or in chain
        Router->>Builder: amount_for_refs(refs)
        Builder->>DB: Fetch UTXO assets
        DB-->>Builder: assets
        Builder-->>Router: AddressContent
        Router-->>Client: 200 JSON(AddressContent)
    else not found
        Router-->>Client: 404 Not Found (ErrorBody)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • scarmuega

Poem

🐰 I hopped through code with nimble paws,
New routes and kinds without a pause.
Payments, Shelley, Byron in one race,
Errors tidy in a snug JSON case.
Hooray — the address blooms in place! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main change: implementing a new /addresses/{address} endpoint. The title is specific, concise, and clearly conveys the primary objective.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/implement-addresses-address-endpoint

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

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/minibf/src/routes/addresses.rs`:
- Around line 247-249: The current code calls is_address_in_chain(&domain,
&address).await when refs.is_empty(), which may stream the full chain per
request; replace that full-chain scan with an indexed or limited check: either
call a fast index lookup (e.g., implement and use
address_exists_in_index(domain, address)) or modify is_address_in_chain to
accept a max_scan_limit and use that (e.g.,
is_address_in_chain_with_limit(domain, address, max_slots)) and return NOT_FOUND
when the limit is exceeded; update the call site to use the index or the bounded
scan and ensure refs, domain, and address flow into the new check instead of
unbounded full-chain scanning.
- Around line 73-78: The current check uses
address.starts_with("addr_vkh")/("script") which is too permissive; instead call
bech32::decode(address).map_err(|_| Error::InvalidAddress)? to get (hrp, addr)
and then validate hrp exactly equals "addr_vkh" or "script" (e.g., if hrp !=
"addr_vkh" && hrp != "script" return Err(Error::InvalidAddress)), then return
Ok(ParsedAddress::Payment { key: addr, script: hrp.as_str() == "script" });
remove the starts_with branch and rely on exact HRP comparison around
bech32::decode/ParsedAddress::Payment/Error::InvalidAddress.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b00f1bb and 2513c1f.

📒 Files selected for processing (6)
  • crates/minibf/src/error.rs
  • crates/minibf/src/lib.rs
  • crates/minibf/src/pagination.rs
  • crates/minibf/src/routes/addresses.rs
  • crates/minibf/src/routes/pools.rs
  • crates/minibf/src/test_support.rs

Comment on lines 73 to +78
if address.starts_with("addr_vkh") || address.starts_with("script") {
let (_, addr) = bech32::decode(address).map_err(|_| Error::InvalidAddress)?;
return Ok(ParsedAddress::Payment(addr));
let (hrp, addr) = bech32::decode(address).map_err(|_| Error::InvalidAddress)?;
return Ok(ParsedAddress::Payment {
key: addr,
script: hrp.as_str() == "script",
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Validate payment-credential HRP exactly (not by prefix).

Line 73 currently accepts any bech32 string starting with addr_vkh/script, which can treat unsupported HRPs as valid and return 404 instead of 400.

🔧 Suggested fix
-    if address.starts_with("addr_vkh") || address.starts_with("script") {
-        let (hrp, addr) = bech32::decode(address).map_err(|_| Error::InvalidAddress)?;
-        return Ok(ParsedAddress::Payment {
-            key: addr,
-            script: hrp.as_str() == "script",
-        });
-    }
+    if let Ok((hrp, key)) = bech32::decode(address) {
+        match hrp.as_str() {
+            "addr_vkh" | "script" if key.len() == 28 => {
+                return Ok(ParsedAddress::Payment {
+                    script: hrp.as_str() == "script",
+                    key,
+                });
+            }
+            "addr_vkh" | "script" => return Err(Error::InvalidAddress),
+            _ => {}
+        }
+    }
📝 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
if address.starts_with("addr_vkh") || address.starts_with("script") {
let (_, addr) = bech32::decode(address).map_err(|_| Error::InvalidAddress)?;
return Ok(ParsedAddress::Payment(addr));
let (hrp, addr) = bech32::decode(address).map_err(|_| Error::InvalidAddress)?;
return Ok(ParsedAddress::Payment {
key: addr,
script: hrp.as_str() == "script",
});
if let Ok((hrp, key)) = bech32::decode(address) {
match hrp.as_str() {
"addr_vkh" | "script" if key.len() == 28 => {
return Ok(ParsedAddress::Payment {
script: hrp.as_str() == "script",
key,
});
}
"addr_vkh" | "script" => return Err(Error::InvalidAddress),
_ => {}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/minibf/src/routes/addresses.rs` around lines 73 - 78, The current
check uses address.starts_with("addr_vkh")/("script") which is too permissive;
instead call bech32::decode(address).map_err(|_| Error::InvalidAddress)? to get
(hrp, addr) and then validate hrp exactly equals "addr_vkh" or "script" (e.g.,
if hrp != "addr_vkh" && hrp != "script" return Err(Error::InvalidAddress)), then
return Ok(ParsedAddress::Payment { key: addr, script: hrp.as_str() == "script"
}); remove the starts_with branch and rely on exact HRP comparison around
bech32::decode/ParsedAddress::Payment/Error::InvalidAddress.

Comment on lines +247 to +249
if refs.is_empty() && !is_address_in_chain(&domain, &address).await? {
return Err(StatusCode::NOT_FOUND.into());
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

/addresses/{address} miss-path can trigger full-chain scans per request.

Line 247 calls is_address_in_chain, which streams from slot 0 to tip on empty refs. For unknown addresses, this is O(chain length) work per request and can become a reliability hotspot under abuse.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/minibf/src/routes/addresses.rs` around lines 247 - 249, The current
code calls is_address_in_chain(&domain, &address).await when refs.is_empty(),
which may stream the full chain per request; replace that full-chain scan with
an indexed or limited check: either call a fast index lookup (e.g., implement
and use address_exists_in_index(domain, address)) or modify is_address_in_chain
to accept a max_scan_limit and use that (e.g.,
is_address_in_chain_with_limit(domain, address, max_slots)) and return NOT_FOUND
when the limit is exceeded; update the call site to use the index or the bounded
scan and ensure refs, domain, and address flow into the new check instead of
unbounded full-chain scanning.

@scarmuega scarmuega merged commit 50b5e0e into main Mar 4, 2026
12 checks passed
@scarmuega scarmuega deleted the chore/implement-addresses-address-endpoint branch March 4, 2026 20:55
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