Skip to content

feat: add a transaction API#727

Merged
etorreborre merged 2 commits intomainfrom
etorreborre/feat/transaction-api
Mar 19, 2026
Merged

feat: add a transaction API#727
etorreborre merged 2 commits intomainfrom
etorreborre/feat/transaction-api

Conversation

@etorreborre
Copy link
Copy Markdown
Contributor

@etorreborre etorreborre commented Mar 18, 2026

This PR allows to start an amaru node with a local HTTP server offering an endpoint to post a CBOR-serialized transaction.
That transaction is then directly inserted in the mempool (if it is not a duplicate of an existing transaction).

Fixes: #706

Summary by CodeRabbit

  • New Features
    • Optional HTTP Submit API for CBOR transaction submission (configurable via flag/env); returns 202 on success and appropriate error codes on failure.
  • Documentation
    • Added comprehensive Submit API guide with examples and error mappings.
  • Library
    • Exposed raw block/transaction accessors and new CBOR utilities to support extraction/inspection of serialized data.
  • Behavior Changes
    • Validation and rejection plumbing revamped to surface richer transaction validation errors.
  • Tests
    • Added extensive tests for submissions, invalid payloads, duplicates, and content-type variations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an optional Submit HTTP API: workspace axum dependency, CLI/config wiring for a submit address, new submit_api module implementing a CBOR POST /api/submit/tx endpoint that validates and inserts transactions into the mempool, tests, docs, kernel/cbor helpers and raw block transaction iteration, and runtime lifecycle integration with graceful shutdown.

Changes

Cohort / File(s) Summary
Workspace deps
Cargo.toml, crates/amaru/Cargo.toml
Added axum = "0.8" to workspace dependencies and enabled workspace usage for the amaru crate.
Runtime CLI & wiring
crates/amaru/src/bin/amaru/cmd/run.rs
Added --submit-api-address Arg and Config.submit_api_address; added start_submit_api helper; integrates Submit API start/shutdown with node lifecycle and cancellation handling.
Config & exports
crates/amaru/src/stages/config.rs, crates/amaru/src/lib.rs
Added Config.submit_api_address: Option<String> with parsing accessor; exported submit_api module and AMARU_SUBMIT_API_ADDRESS env var constant.
Submit API implementation
crates/amaru/src/submit_api.rs
New axum-based server: POST /api/submit/tx (application/cbor) decodes CBOR→Transaction, validates via mempool, inserts with TxOrigin::Local; provides start -> (JoinHandle, bound addr); includes extensive tests.
Tests & docs
crates/amaru/src/tests/mod.rs, docs/SUBMIT_API.md
Made test_data pub(crate); added docs/SUBMIT_API.md documenting endpoint, request/response, examples and error mappings.
Kernel: block & raw block iteration
crates/amaru-kernel/src/cardano/block.rs, .../raw_block.rs, .../network_block.rs, crates/amaru-kernel/src/lib.rs
Added Block::CBOR_FIELD_COUNT; exposed RawBlock and RawBlock::transactions() iterator (RawBlockTransactions) to reconstruct standalone CBOR transactions; added NetworkBlock::encoded_block(); re-exported new cbor helpers.
CBOR helpers
crates/amaru-minicbor-extra/src/decode.rs
Added collect_array_item_bytes and collect_map_value_bytes helpers to capture raw CBOR item bytes (handles definite/indefinite collections).
Mempool / validation refactor
crates/amaru-mempool/..., crates/amaru-ouroboros-traits/..., crates/amaru-protocols/..., crates/amaru-protocols/src/tx_submission/...
Replaced CanValidateTransactions with new TxValidator trait and TransactionValidationError plumbing; removed old trait and mocks; updated InMemoryMempool, mempool traits, TxSubmission flows, and tests to use new validator/error types; adjusted error handling for invalid txs.
Misc tests & imports
crates/amaru/src/tests/*, crates/amaru-protocols/src/tx_submission/tests/sized_mempool.rs
Updated imports and test helpers to reflect new validator trait and public visibility changes.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI Args
    participant Config as Config
    participant Runtime as Node Runtime
    participant API as Submit API Server
    participant Mempool as ResourceMempool
    participant Client as HTTP Client

    CLI->>Config: provide submit_api_address
    Config->>Runtime: include address in startup config
    Runtime->>API: start_submit_api(address, running, exit)
    API->>Mempool: obtain shared ResourceMempool<Transaction>
    API->>API: bind socket & spawn server task
    API-->>Runtime: return JoinHandle

    Client->>API: POST /api/submit/tx (application/cbor)
    API->>API: verify Content-Type and decode CBOR -> Transaction
    API->>Mempool: validate & insert (TxOrigin::Local)
    Mempool-->>API: ack + tx_id
    API-->>Client: 202 Accepted + {tx_id}

    Runtime->>API: shutdown signal (CancellationToken)
    API-->>Runtime: graceful shutdown / task termination
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • rkuhn
  • jeluard

Poem

Ahoy — a tiny axum pub sets sail,
CBOR parcels riding the mempool gale,
202s cheer with tx_id in tow,
Node hums softly, then graceful go,
Tests wink like easter eggs in a tale.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Changes include refactoring of transaction validation (removing CanValidateTransactions, adding TxValidator) beyond the stated issue scope, though supporting the main objective. Confirm whether validation trait refactoring aligns with intended scope or if it should be split into a separate PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: adding a transaction submission API endpoint to the node.
Linked Issues check ✅ Passed All objectives from issue #706 are met: HTTP endpoint implemented, embedded server starts with node, transactions added to mempool, and API documented with examples.
Docstring Coverage ✅ Passed Docstring coverage is 84.51% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch etorreborre/feat/transaction-api
📝 Coding Plan
  • Generate coding plan for human review comments

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

🧹 Nitpick comments (1)
crates/amaru/src/submit_api.rs (1)

49-50: Don't let the API die silently in the background task.

.await.ok() bins the axum::serve error, so a late listener failure just makes the submit API vanish with no breadcrumb in the logs. At least log the error inside the task, or return a JoinHandle<Result<_, _>> so shutdown can surface it.

🪵 Proposed fix
-use tracing::info;
+use tracing::{error, info};
@@
     let handle = tokio::spawn(async move {
-        axum::serve(listener, app).with_graceful_shutdown(shutdown.cancelled_owned()).await.ok();
+        if let Err(err) = axum::serve(listener, app)
+            .with_graceful_shutdown(shutdown.cancelled_owned())
+            .await
+        {
+            error!(?err, "Submit API server stopped unexpectedly");
+        }
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/amaru/src/submit_api.rs` around lines 49 - 50, The background task
currently swallows any axum::serve error by calling .await.ok(), so replace the
silent discard with explicit error handling: inside the tokio::spawn that
creates handle (the async block invoking axum::serve(listener,
app).with_graceful_shutdown(shutdown.cancelled_owned())), await the serve call
into a Result and either log the Err via the project's logger (so late listener
failures are visible) or change the spawn to return a JoinHandle<Result<(), E>>
so callers can propagate/surface the error; ensure you reference the same
listener, app and shutdown.cancelled_owned() values when capturing the error
handling change.
🤖 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/amaru/src/bin/amaru/cmd/run.rs`:
- Around line 159-167: The current shutdown sequence cancels the parent token
then immediately calls handle.abort() on submit_api_handle (created by
start_submit_api and using amaru::exit::hook_exit_token), which can interrupt
Axum's graceful shutdown initiated by exit.cancelled().await; change the logic
so after exit.cancelled().await and running.abort() you await the
submit_api_handle (e.g., let _ = handle.await) to allow the server to finish
draining, or if a hard cutoff is required wrap that await in a
tokio::time::timeout to force-abort only after a fixed deadline instead of
calling handle.abort() immediately.

In `@crates/amaru/src/submit_api.rs`:
- Around line 72-74: The 202 branch is returning a plain String with manual
quotes which mislabels the response as text/plain; change the success branch
that handles mempool.insert(tx, TxOrigin::Local) to return an axum::Json-wrapped
value (so the tx_id is serialized properly as JSON) and remove manual quoting,
while the Err branch should return a plain unquoted error message (no wrapping
in Json) so it stays text/plain; also update any tests that assert quoted
response bodies to expect JSON for the success case and unquoted plain text for
errors; locate the match on mempool.insert and adjust the success tuple to use
Json(...) and the error tuple to remove format!("\"{...}\"").

---

Nitpick comments:
In `@crates/amaru/src/submit_api.rs`:
- Around line 49-50: The background task currently swallows any axum::serve
error by calling .await.ok(), so replace the silent discard with explicit error
handling: inside the tokio::spawn that creates handle (the async block invoking
axum::serve(listener, app).with_graceful_shutdown(shutdown.cancelled_owned())),
await the serve call into a Result and either log the Err via the project's
logger (so late listener failures are visible) or change the spawn to return a
JoinHandle<Result<(), E>> so callers can propagate/surface the error; ensure you
reference the same listener, app and shutdown.cancelled_owned() values when
capturing the error handling change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dcfdd4df-faf8-4e6e-9e6f-c7bc628d54b8

📥 Commits

Reviewing files that changed from the base of the PR and between 1de248b and 5f630c2.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • crates/amaru/Cargo.toml
  • crates/amaru/src/bin/amaru/cmd/run.rs
  • crates/amaru/src/lib.rs
  • crates/amaru/src/stages/config.rs
  • crates/amaru/src/submit_api.rs
  • crates/amaru/src/tests/mod.rs
  • docs/SUBMIT_API.md

@etorreborre etorreborre force-pushed the etorreborre/feat/transaction-api branch 2 times, most recently from 94adca6 to 84f2d96 Compare March 18, 2026 15:28
Copy link
Copy Markdown
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: 2

♻️ Duplicate comments (1)
crates/amaru/src/bin/amaru/cmd/run.rs (1)

165-167: ⚠️ Potential issue | 🟠 Major

Let graceful shutdown finish instead of hard-aborting immediately.

At Line 165, aborting the submit API handle right after cancellation can chop Axum’s graceful drain mid-scene. Since crates/amaru/src/submit_api.rs already uses with_graceful_shutdown(...), await the handle (optionally with timeout) before forcing abort.

🔧 Proposed fix
         exit.cancelled().await;
         running.abort();
 
         if let Some(handle) = submit_api_handle {
-            handle.abort();
+            // Let Axum finish graceful shutdown triggered by the child cancellation token.
+            let _ = handle.await;
         }
#!/bin/bash
# Verify shutdown ordering between run.rs and submit_api.rs.
rg -n -C3 'exit\.cancelled\(\)\.await|handle\.abort\(\)' crates/amaru/src/bin/amaru/cmd/run.rs
rg -n -C3 'with_graceful_shutdown' crates/amaru/src/submit_api.rs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/amaru/src/bin/amaru/cmd/run.rs` around lines 165 - 167, The submit API
JoinHandle (submit_api_handle) is being immediately aborted which can interrupt
Axum’s with_graceful_shutdown; instead await the handle to allow graceful
shutdown (with an overall timeout), and only call handle.abort() if the timeout
elapses or the await errors. Replace the immediate handle.abort() in run.rs with
awaiting the JoinHandle (e.g. tokio::time::timeout(Duration::from_secs(N),
submit_api_handle).await) and on timeout or join error call
submit_api_handle.abort(); keep references to submit_api_handle and the
submit_api.rs with_graceful_shutdown usage so the graceful drain can complete.
🧹 Nitpick comments (1)
crates/amaru/src/submit_api.rs (1)

80-86: Consider an atomic validate+insert path in mempool.

Lines 80–86 split validation and insertion into two calls. Under concurrent state changes, that opens a TOCTOU window between “valid” and “inserted.” A combined mempool API (e.g., validate-and-insert) would tighten correctness.

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

In `@crates/amaru/src/submit_api.rs` around lines 80 - 86, The current code calls
mempool.validate_transaction(tx.clone()) and then mempool.insert(tx,
TxOrigin::Local), which creates a TOCTOU race; implement and call an atomic
validate-and-insert API (e.g., mempool.validate_and_insert(tx, TxOrigin::Local)
or insert_with_validation) on the mempool trait/impl so validation and insertion
happen under the same lock/transaction, and replace the two-step sequence with a
single call that returns Ok((tx_id, seq_no)) or Err(e) to preserve the existing
response handling.
🤖 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/amaru/src/submit_api.rs`:
- Around line 69-70: The Content-Type check using content_type !=
"application/cbor" is case-sensitive and should be made case-insensitive; update
the conditional in submit_api.rs to compare the header using a case-insensitive
comparison (e.g., content_type.eq_ignore_ascii_case("application/cbor")) or
parse the header with a MIME parser and compare type/subtype case-insensitively
so requests like "Application/CBOR" are accepted and still return
StatusCode::UNSUPPORTED_MEDIA_TYPE via text_response when it doesn't match.
- Around line 50-52: The axum server result is being swallowed by `.await.ok()`,
so replace that silent ignore with explicit handling: await the future returned
by `axum::serve(listener,
app).with_graceful_shutdown(shutdown.cancelled_owned())`, capture the `Result`
inside the `tokio::spawn` task (the block where `handle` is assigned), and log
or propagate any `Err` (e.g., via `tracing::error!` or `process_logger`)
including the error details so server errors from `axum::serve` are visible;
ensure you reference the same `listener`, `app`, and
`shutdown.cancelled_owned()` used in the current `tokio::spawn` closure.

---

Duplicate comments:
In `@crates/amaru/src/bin/amaru/cmd/run.rs`:
- Around line 165-167: The submit API JoinHandle (submit_api_handle) is being
immediately aborted which can interrupt Axum’s with_graceful_shutdown; instead
await the handle to allow graceful shutdown (with an overall timeout), and only
call handle.abort() if the timeout elapses or the await errors. Replace the
immediate handle.abort() in run.rs with awaiting the JoinHandle (e.g.
tokio::time::timeout(Duration::from_secs(N), submit_api_handle).await) and on
timeout or join error call submit_api_handle.abort(); keep references to
submit_api_handle and the submit_api.rs with_graceful_shutdown usage so the
graceful drain can complete.

---

Nitpick comments:
In `@crates/amaru/src/submit_api.rs`:
- Around line 80-86: The current code calls
mempool.validate_transaction(tx.clone()) and then mempool.insert(tx,
TxOrigin::Local), which creates a TOCTOU race; implement and call an atomic
validate-and-insert API (e.g., mempool.validate_and_insert(tx, TxOrigin::Local)
or insert_with_validation) on the mempool trait/impl so validation and insertion
happen under the same lock/transaction, and replace the two-step sequence with a
single call that returns Ok((tx_id, seq_no)) or Err(e) to preserve the existing
response handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5237299-7f91-478c-9816-a978038fb71b

📥 Commits

Reviewing files that changed from the base of the PR and between 5f630c2 and 94adca6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • crates/amaru/Cargo.toml
  • crates/amaru/src/bin/amaru/cmd/run.rs
  • crates/amaru/src/lib.rs
  • crates/amaru/src/stages/config.rs
  • crates/amaru/src/submit_api.rs
  • crates/amaru/src/tests/mod.rs
  • docs/SUBMIT_API.md
✅ Files skipped from review due to trivial changes (1)
  • docs/SUBMIT_API.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/amaru/src/stages/config.rs
  • Cargo.toml

Copy link
Copy Markdown
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: 2

♻️ Duplicate comments (2)
crates/amaru/src/submit_api.rs (2)

50-52: ⚠️ Potential issue | 🟠 Major

Don’t let the server fail off-screen.

That .await.ok() bins the accept-loop error, so the submit API can keel over with zero log breadcrumbs. Please log the Err before the task exits.

🔧 Suggested tweak
-use tracing::info;
+use tracing::{error, info};
@@
     let handle = tokio::spawn(async move {
-        axum::serve(listener, app).with_graceful_shutdown(shutdown.cancelled_owned()).await.ok();
+        if let Err(err) = axum::serve(listener, app)
+            .with_graceful_shutdown(shutdown.cancelled_owned())
+            .await
+        {
+            error!(error = %err, "Submit API server stopped with error");
+        }
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/amaru/src/submit_api.rs` around lines 50 - 52, The accept-loop error
is currently being discarded by calling .await.ok() inside the spawned task;
change the spawned task (the closure passed to tokio::spawn that calls
axum::serve(listener,
app).with_graceful_shutdown(shutdown.cancelled_owned()).await) to capture the
Result from .await and log any Err before the task exits (e.g., replace the
.ok() with a match or if let Err(e) branch and emit a
tracing::error!/log::error! with context mentioning the submit API server and
the error).

69-70: ⚠️ Potential issue | 🟡 Minor

Make the media-type check case-insensitive.

HTTP media types are case-insensitive, so Application/CBOR gets bounced here for no good reason. eq_ignore_ascii_case sorts it.

🔧 Suggested tweak
-    if content_type != "application/cbor" {
+    if !content_type.eq_ignore_ascii_case("application/cbor") {
         return text_response(StatusCode::UNSUPPORTED_MEDIA_TYPE, "Content-Type must be application/cbor");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/amaru/src/submit_api.rs` around lines 69 - 70, The media-type check in
submit_api.rs currently uses a case-sensitive comparison on content_type; change
the check to use a case-insensitive comparison (e.g., call
content_type.eq_ignore_ascii_case("application/cbor")) where the current if uses
!= "application/cbor", so requests like "Application/CBOR" are accepted while
keeping the existing UNSUPPORTED_MEDIA_TYPE response when it fails.
🤖 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/amaru/src/bin/amaru/cmd/run.rs`:
- Around line 156-160: If start_submit_api can fail after
build_and_run_node/metrics have started, ensure you roll them back on error:
replace the direct `?` on `start_submit_api(submit_api_address, &running,
&exit).await?` with an explicit match/if-let that, on Err(e), first stops the
running node (call the node handle's graceful shutdown method such as
`running.shutdown().await` or `running.abort()`/`running.stop()` if present) and
also stop/abort the metrics task created by
`meter_provider.clone().map(track_system_metrics).transpose()` (use whatever
handle `track_system_metrics` returns), then return the error; on Ok, continue
as before. Reference symbols:
`meter_provider.clone().map(track_system_metrics).transpose()`,
`build_and_run_node`, `running`, and `start_submit_api`.

In `@crates/amaru/src/submit_api.rs`:
- Around line 84-86: The match on mempool.insert currently maps all errors to
BAD_REQUEST; update the error handling in the submit path that calls
mempool.insert(tx, TxOrigin::Local) so that MempoolFull is mapped to a 503 (or
ServiceUnavailable) response to signal backpressure,
duplicate/AlreadyExists-style rejects map to 409 Conflict, and only genuine
client-side validation rejects continue to return 400; locate the match around
mempool.insert (the Ok((tx_id, _seq_no)) arm and the Err(e) arm) and branch on
the typed reject (e) — e.g., match on TxReject::MempoolFull =>
text_response(StatusCode::SERVICE_UNAVAILABLE, ...),
TxReject::Duplicate/AlreadyExists => text_response(StatusCode::CONFLICT, ...), _
=> text_response(StatusCode::BAD_REQUEST, e.to_string()) — preserving the
accepted path using json_response(StatusCode::ACCEPTED, tx_id.to_string()).

---

Duplicate comments:
In `@crates/amaru/src/submit_api.rs`:
- Around line 50-52: The accept-loop error is currently being discarded by
calling .await.ok() inside the spawned task; change the spawned task (the
closure passed to tokio::spawn that calls axum::serve(listener,
app).with_graceful_shutdown(shutdown.cancelled_owned()).await) to capture the
Result from .await and log any Err before the task exits (e.g., replace the
.ok() with a match or if let Err(e) branch and emit a
tracing::error!/log::error! with context mentioning the submit API server and
the error).
- Around line 69-70: The media-type check in submit_api.rs currently uses a
case-sensitive comparison on content_type; change the check to use a
case-insensitive comparison (e.g., call
content_type.eq_ignore_ascii_case("application/cbor")) where the current if uses
!= "application/cbor", so requests like "Application/CBOR" are accepted while
keeping the existing UNSUPPORTED_MEDIA_TYPE response when it fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3ec42770-24a6-482c-a06c-8f827d6655c6

📥 Commits

Reviewing files that changed from the base of the PR and between 94adca6 and 84f2d96.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • crates/amaru/Cargo.toml
  • crates/amaru/src/bin/amaru/cmd/run.rs
  • crates/amaru/src/lib.rs
  • crates/amaru/src/stages/config.rs
  • crates/amaru/src/submit_api.rs
  • crates/amaru/src/tests/mod.rs
  • docs/SUBMIT_API.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/amaru/src/stages/config.rs
  • docs/SUBMIT_API.md

@etorreborre etorreborre force-pushed the etorreborre/feat/transaction-api branch 3 times, most recently from cfd710d to fb45ef4 Compare March 18, 2026 15:51
Signed-off-by: Eric Torreborre <etorreborre@yahoo.com>
@etorreborre etorreborre force-pushed the etorreborre/feat/transaction-api branch from fb45ef4 to 8811af1 Compare March 18, 2026 16:10
@etorreborre etorreborre self-assigned this Mar 18, 2026
Copy link
Copy Markdown
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

🤖 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/amaru/src/submit_api.rs`:
- Around line 82-96: The current validate-then-insert sequence
(validate_transaction(tx.clone()) followed by insert(tx, TxOrigin::Local)) has a
race if validate_transaction depends on mutable mempool state; fix by either
moving validation into the atomic insert path or performing the validation while
holding the same lock used by insert so both checks happen under one critical
section (i.e., call the validation logic from inside mempool.insert or acquire
the mempool write lock, run validate_transaction, then call insert), and if you
choose not to change behavior, add a clear comment in the submit handler
referencing validate_transaction, insert, and TxRejectReason to state that
validation is pure transaction-only (no mempool-dependent checks) so the race is
acceptable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0bdce1c-49fb-4bd4-b48f-b92e34354f2e

📥 Commits

Reviewing files that changed from the base of the PR and between 84f2d96 and 8811af1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • Cargo.toml
  • crates/amaru-kernel/src/cardano/block.rs
  • crates/amaru-kernel/src/cardano/network_block.rs
  • crates/amaru-kernel/src/cardano/raw_block.rs
  • crates/amaru-kernel/src/lib.rs
  • crates/amaru-minicbor-extra/src/decode.rs
  • crates/amaru/Cargo.toml
  • crates/amaru/src/bin/amaru/cmd/run.rs
  • crates/amaru/src/lib.rs
  • crates/amaru/src/stages/config.rs
  • crates/amaru/src/submit_api.rs
  • crates/amaru/src/tests/mod.rs
  • docs/SUBMIT_API.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/amaru/Cargo.toml
  • docs/SUBMIT_API.md
  • crates/amaru/src/stages/config.rs
  • crates/amaru/src/tests/mod.rs
  • crates/amaru/src/lib.rs

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 18, 2026

Codecov Report

❌ Patch coverage is 76.27494% with 107 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/amaru/src/bin/amaru/cmd/run.rs 0.00% 27 Missing ⚠️
crates/amaru-ouroboros-traits/src/mempool.rs 37.83% 23 Missing ⚠️
...tes/amaru-protocols/src/tx_submission/responder.rs 64.44% 16 Missing ⚠️
crates/amaru-minicbor-extra/src/decode.rs 66.66% 15 Missing ⚠️
crates/amaru-kernel/src/cardano/raw_block.rs 82.19% 13 Missing ⚠️
crates/amaru/src/submit_api.rs 97.76% 4 Missing ⚠️
crates/amaru-protocols/src/mempool_effects.rs 0.00% 3 Missing ⚠️
crates/amaru/src/stages/config.rs 25.00% 3 Missing ⚠️
...rates/amaru-protocols/src/tx_submission/outcome.rs 0.00% 2 Missing ⚠️
.../amaru-mempool/src/strategies/in_memory_mempool.rs 96.66% 1 Missing ⚠️
Files with missing lines Coverage Δ
crates/amaru-kernel/src/cardano/block.rs 96.96% <100.00%> (ø)
crates/amaru-kernel/src/cardano/network_block.rs 81.81% <100.00%> (+0.86%) ⬆️
crates/amaru-mempool/src/strategies/dummy.rs 78.87% <100.00%> (+3.19%) ⬆️
crates/amaru/src/lib.rs 0.00% <ø> (ø)
.../amaru-mempool/src/strategies/in_memory_mempool.rs 80.12% <96.66%> (+3.24%) ⬆️
...rates/amaru-protocols/src/tx_submission/outcome.rs 0.00% <0.00%> (ø)
crates/amaru-protocols/src/mempool_effects.rs 55.88% <0.00%> (+5.88%) ⬆️
crates/amaru/src/stages/config.rs 64.28% <25.00%> (-3.03%) ⬇️
crates/amaru/src/submit_api.rs 97.76% <97.76%> (ø)
crates/amaru-kernel/src/cardano/raw_block.rs 87.37% <82.19%> (-12.63%) ⬇️
... and 4 more

... and 15 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.

Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
crates/amaru/src/submit_api.rs (1)

61-93: Handler logic is clean and well-structured!

The Content-Type stripping (split by ;, trim) handles charset parameters properly—nice attention to detail there. The match on TxRejectReason variants giving distinct status codes (503/409/400) is exactly what you want for API consumers to react appropriately. It's like a good RPG dialogue tree—each branch leads somewhere meaningful.

One small nit: the doc comment on line 60 says "CBOR-encoded." but feels like half a sentence. Maybe "The request body is expected to be CBOR-encoded." would read better?

📝 Suggested doc fix
 /// Handle incoming transaction submission requests.
-/// The request body is expected to be a CBOR-encoded.
+/// The request body is expected to be CBOR-encoded.
 async fn submit_tx(State(mempool): State<SubmitApiState>, headers: HeaderMap, body: Bytes) -> Response {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/amaru/src/submit_api.rs` around lines 61 - 93, Update the doc comment
above the submit_tx handler: replace the half-sentence "CBOR-encoded." with a
full sentence such as "The request body is expected to be CBOR-encoded." so the
documentation for the submit_tx function is clear and complete; edit the comment
immediately preceding the async fn submit_tx(...) declaration to reflect this
change.
🤖 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/amaru-protocols/src/tx_submission/responder.rs`:
- Around line 326-334: The match on mempool.insert currently returns Err for
non-Invalid TxRejectReason variants; change it so that when mempool.insert
returns Err(TxRejectReason::MempoolFull) or Err(TxRejectReason::Duplicate) you
log a warning/info (including requested_id and the reject reason) and continue
processing instead of returning Err, while preserving the existing behavior for
Err(TxRejectReason::Invalid(error)) (log invalid and continue) and for any other
unexpected error return Err as before; update the match arm(s) around
mempool.insert(...) in responder.rs to handle TxRejectReason::MempoolFull and
TxRejectReason::Duplicate explicitly and avoid terminating the protocol.

---

Nitpick comments:
In `@crates/amaru/src/submit_api.rs`:
- Around line 61-93: Update the doc comment above the submit_tx handler: replace
the half-sentence "CBOR-encoded." with a full sentence such as "The request body
is expected to be CBOR-encoded." so the documentation for the submit_tx function
is clear and complete; edit the comment immediately preceding the async fn
submit_tx(...) declaration to reflect this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e61cf5cf-e18f-47d6-8882-9b743cf70077

📥 Commits

Reviewing files that changed from the base of the PR and between 8811af1 and 88e8069.

📒 Files selected for processing (10)
  • crates/amaru-mempool/src/strategies/dummy.rs
  • crates/amaru-mempool/src/strategies/in_memory_mempool.rs
  • crates/amaru-ouroboros-traits/src/can_validate_transactions/mock.rs
  • crates/amaru-ouroboros-traits/src/can_validate_transactions/mod.rs
  • crates/amaru-ouroboros-traits/src/lib.rs
  • crates/amaru-ouroboros-traits/src/mempool.rs
  • crates/amaru-protocols/src/mempool_effects.rs
  • crates/amaru-protocols/src/tx_submission/responder.rs
  • crates/amaru-protocols/src/tx_submission/tests/sized_mempool.rs
  • crates/amaru/src/submit_api.rs
💤 Files with no reviewable changes (3)
  • crates/amaru-ouroboros-traits/src/lib.rs
  • crates/amaru-ouroboros-traits/src/can_validate_transactions/mock.rs
  • crates/amaru-ouroboros-traits/src/can_validate_transactions/mod.rs

…pool at the same time

Signed-off-by: Eric Torreborre <etorreborre@yahoo.com>
@etorreborre etorreborre force-pushed the etorreborre/feat/transaction-api branch from 88e8069 to a51a146 Compare March 18, 2026 18:52
@etorreborre etorreborre merged commit 61abbf8 into main Mar 19, 2026
27 checks passed
@etorreborre etorreborre deleted the etorreborre/feat/transaction-api branch March 19, 2026 08:36
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.

Add an HTTP endpoint to create transactions

3 participants