Skip to content

feat(utxorpc): add v1beta spec support alongside v1alpha#745

Merged
scarmuega merged 7 commits into
mainfrom
feat/utxorpc-v1beta
May 2, 2026
Merged

feat(utxorpc): add v1beta spec support alongside v1alpha#745
scarmuega merged 7 commits into
mainfrom
feat/utxorpc-v1beta

Conversation

@scarmuega
Copy link
Copy Markdown
Member

@scarmuega scarmuega commented May 2, 2026

Summary

  • Bumps utxorpc-spec to 0.19 and exposes both pallas_utxorpc::v1alpha and pallas_utxorpc::v1beta mappers.
  • Existing pallas_utxorpc::Mapper / pallas::interop::utxorpc::Mapper keeps working unchanged: under the default v1alpha feature the v1alpha items (Mapper, spec) are re-exported at the crate root.
  • Adds a v1beta Cargo feature (additive, opt-in) for users who want the new spec — including v1beta-only types (BootstrapWitness, Vote, VotingProcedure, VoterVotes).

Implementation

Most of the mapping logic is wire-identical between the two specs (all certificates, pparams, plutus data, witnesses, scripts, blocks). It lives in a single impl_cardano_mapper_shared! macro in src/shared.rs, instantiated once per version.

Methods that genuinely diverge live per-version in v1alpha/mod.rs and v1beta/mod.rs:

  • map_native_script — variant rename ScriptPubkeyScriptPubkeyHash
  • map_tx_datumoriginal_cbor became optional
  • map_tx_output — new optional original_cbor field in v1beta
  • map_assetQuantity oneof in v1alpha, scalar Option<BigInt> in v1beta
  • map_policy_assets — per-multiasset redeemer removed in v1beta (lifted to WitnessSet.redeemers)
  • map_conway_gov_actionInfoAction shape change
  • map_txWitnessSet and Tx gained fields in v1beta (redeemers, bootstrap_witnesses, votes)

v1beta-only mappers (map_bootstrap_witness, map_vote, map_voting_procedure, map_voter, map_votes) live alongside the rest of the v1beta module rather than in a separate file.

Test plan

  • `cargo test -p pallas-utxorpc` (default features) — existing v1alpha snapshot passes byte-identically
  • `cargo test -p pallas-utxorpc --features v1beta` — adds the v1beta snapshot test (`test_data/u5c_v1beta.json`) and 5 cross-version wire-compat tests (`PParams`, `Certificate`, `PoolRegistrationCert`, `Metadata`, `PlutusData`) that act as a tripwire if any shared message diverges in a future upstream release
  • `cargo check -p pallas-utxorpc --no-default-features --features v1beta` — v1beta-only build
  • `cargo check -p pallas-utxorpc --no-default-features` — minimal build (only `LedgerContext` + aliases)
  • `cargo check -p pallas` — umbrella crate still builds

Notes

  • The existing `test_data/u5c1.json` snapshot is renamed to `test_data/u5c_v1alpha.json` for naming symmetry with the new `u5c_v1beta.json`.
  • v1beta snapshot can be regenerated with `REGEN_V1BETA_SNAPSHOT=1 cargo test -p pallas-utxorpc --features v1beta v1beta::tests::snapshot`.
  • `utxorpc-spec 0.19` adds a `plutus_v4` field to `CostModels` even on v1alpha; the macro handles it via `..Default::default()` in the two affected struct literals.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Exposes both v1alpha and v1beta UTxORPC mappers so callers can choose a version; crate no longer re-exports a single spec.
  • Documentation

    • README updated with dual-version usage, migration guidance, and notes on per-version behavior.
  • Tests

    • Added snapshot tests and cross-version compatibility checks (with optional snapshot regeneration).

Bumps utxorpc-spec to 0.19 and exposes both `pallas_utxorpc::v1alpha`
and `pallas_utxorpc::v1beta` mappers. The crate root keeps re-exporting
v1alpha items (Mapper, spec) under the default `v1alpha` feature, so
existing call sites compile unchanged. v1beta is gated behind an
opt-in `v1beta` feature; both features are additive.

Shared mapping logic (~80% of the surface — all certificates, pparams,
plutus data, witnesses, scripts, blocks) lives in a single
`impl_cardano_mapper_shared!` macro instantiated once per version. Methods
that diverge between specs (map_native_script, map_tx_datum, map_tx_output,
map_asset, map_policy_assets, map_conway_gov_action, map_tx) are defined
per-version. v1beta-only mappers (BootstrapWitness, Vote, VotingProcedure,
VoterVotes) live next to the rest of the v1beta module.

Adds cross-version wire-compatibility tests (PParams, Certificate,
PoolRegistrationCert, Metadata, PlutusData) that act as a tripwire if
upstream diverges any shared message in a future release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 2, 2026

Warning

Rate limit exceeded

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

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f144390-3657-4c64-be05-34fca53527a8

📥 Commits

Reviewing files that changed from the base of the PR and between 4ab8cff and 3ebc4e4.

📒 Files selected for processing (3)
  • pallas-utxorpc/Cargo.toml
  • pallas-utxorpc/README.md
  • pallas-utxorpc/src/lib.rs
📝 Walkthrough

Walkthrough

Consolidates Cardano-to-UTxORPC mapping into a shared macro and introduces explicit, versioned mappers v1alpha and v1beta. Removes standalone certs/params modules, updates utxorpc-spec to 0.19 with feature gating, and adds snapshot and cross-version compatibility tests and documentation updates.

Changes

Mapper Architecture Refactoring

Layer / File(s) Summary
Dependency & Features
pallas-utxorpc/Cargo.toml
Bumps utxorpc-spec to 0.19, sets default-features = false, enables utxorpc-v1alpha-cardano and utxorpc-v1beta-cardano; adds prost = "0.13" to dev-dependencies.
Top-level Wiring
pallas-utxorpc/src/lib.rs
Declares and exposes pub mod v1alpha; and pub mod v1beta;, imports shared via #[macro_use] mod shared;, defines pub trait LedgerContext: Clone with get_utxos and get_slot_timestamp, and adds cross-version wire-compat tests.
Shared Implementation Generation
pallas-utxorpc/src/shared.rs
Adds macro_rules! impl_cardano_mapper_shared!($u5c:path) generating version-agnostic Mapper<C> impls: helpers (rationals, BigInt, exunits/prices), redeemers, resolved-UTXO decoding, inputs/outputs/witnesses/scripts/plutus datums, metadata, governance mapping, certificate mapping (Alonzo/Conway with redeemers), and protocol parameters mapping helpers.
v1alpha Mapper
pallas-utxorpc/src/v1alpha/mod.rs
Adds pub use utxorpc_spec::utxorpc::v1alpha as spec;, implements pub struct Mapper<C: LedgerContext> with new/masked, and provides v1alpha-specific mapping functions (map_native_script, map_tx_datum, map_tx_output, map_asset, map_policy_assets, map_conway_gov_action, map_tx) plus a snapshot test.
v1beta Mapper
pallas-utxorpc/src/v1beta/mod.rs
Adds pub use utxorpc_spec::utxorpc::v1beta as spec;, implements pub struct Mapper<C: LedgerContext> with new/masked, invokes impl_cardano_mapper_shared!(utxorpc_spec::utxorpc::v1beta::cardano), and adds v1beta-specific mappers including voting/bootstraps (map_bootstrap_witness, map_vote, map_voting_procedure, map_votes) and a snapshot test.
Removals / Consolidation
pallas-utxorpc/src/certs.rs, pallas-utxorpc/src/params.rs
Deletes standalone certs.rs and params.rs that previously implemented certificate and protocol-parameter mapping, moving that logic into the shared macro (removes methods like map_cert, map_alonzo_compatible_cert, map_pparams, and map_conway_pparams_update).
Documentation
pallas-utxorpc/README.md
Documents crate now exposes v1alpha and v1beta side-by-side, requires callers to pick a version (crate root no longer re-exports Mapper/spec), lists v1beta-only types, and documents snapshot tests and regeneration process.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A rabbit's mapper note:
Two versions now sit side by side,
A macro threads their guts with pride.
Tests keep bytes aligned and bright,
Snapshots snap — the mappings right. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 clearly summarizes the main change: adding v1beta UTxORPC specification support alongside the existing v1alpha, which aligns with the substantial refactoring across multiple modules and the introduction of version-specific mappers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/utxorpc-v1beta

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
Review rate limit: 0/1 reviews remaining, refill in 54 minutes and 43 seconds.

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

🧹 Nitpick comments (2)
pallas-utxorpc/src/v1alpha/mod.rs (1)

231-234: 💤 Low value

Clarify the placeholder value for InfoAction.

The comment explains this is a placeholder, but the magic number 6 is unclear. Consider adding a brief note about why this specific value was chosen or if it aligns with the v1alpha protobuf definition.

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

In `@pallas-utxorpc/src/v1alpha/mod.rs` around lines 231 - 234, The mapping for
conway::GovAction::Information currently uses a magic number (6) for
u5c::governance_action::GovernanceAction::InfoAction; replace the literal with a
named constant (e.g., INFO_ACTION_V1ALPHA or INFO_ACTION_PLACEHOLDER) and add a
brief comment stating that this constant corresponds to the v1alpha protobuf
enum value (or is a temporary placeholder until the protobuf mapping is
updated), or, if available, map it to the generated protobuf enum variant
instead of a raw integer to make the intent explicit.
pallas-utxorpc/src/shared.rs (1)

10-17: 💤 Low value

Potential numeric truncation in rational_number_to_u5c.

The cast from i64 numerator to i32 and u64 denominator to u32 may silently truncate values that exceed the target type's range. While Cardano protocol parameters typically use small rationals, edge cases could produce incorrect results.

Consider defensive bounds checking
 fn rational_number_to_u5c(
     value: pallas_primitives::RationalNumber,
 ) -> u5c::RationalNumber {
+    debug_assert!(
+        value.numerator >= i32::MIN as i64 && value.numerator <= i32::MAX as i64,
+        "RationalNumber numerator overflow"
+    );
+    debug_assert!(
+        value.denominator <= u32::MAX as u64,
+        "RationalNumber denominator overflow"
+    );
     u5c::RationalNumber {
         numerator: value.numerator as i32,
         denominator: value.denominator as u32,
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallas-utxorpc/src/shared.rs` around lines 10 - 17, The cast in
rational_number_to_u5c (converting pallas_primitives::RationalNumber.numerator:
i64 -> i32 and denominator: u64 -> u32) can silently truncate; add defensive
bounds checking and fail loudly instead of truncating: validate numerator fits
i32::MIN..=i32::MAX and denominator fits 1..=u32::MAX (and non-zero if needed)
before constructing u5c::RationalNumber, and change rational_number_to_u5c to
return Result<u5c::RationalNumber, YourErrorType> (or propagate an existing
error type) so callers can handle out-of-range values; use
i32::try_from(value.numerator) and u32::try_from(value.denominator) or explicit
range checks to implement this.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pallas-utxorpc/src/v1alpha/mod.rs`:
- Around line 53-56: The match arm handling babbage::NativeScript::ScriptAny is
incorrectly constructing u5c::native_script::NativeScript::ScriptAll; update
that arm to construct u5c::native_script::NativeScript::ScriptAny instead,
preserving the same NativeScriptList/items mapping via Self::map_native_script
so semantics remain "any script must pass" (locate the code in the match for
babbage::NativeScript::ScriptAny within the mapping function
Self::map_native_script).

In `@pallas-utxorpc/src/v1beta/mod.rs`:
- Around line 53-56: The mapping for the babbage::NativeScript::ScriptAny branch
is incorrect: it currently constructs
u5c::native_script::NativeScript::ScriptAll. Change that branch to construct
u5c::native_script::NativeScript::ScriptAny instead, using the same
u5c::native_script::NativeScriptList with items: x.iter().map(|x|
Self::map_native_script(x)).collect() so the variant names align (update the
match arm that handles babbage::NativeScript::ScriptAny and ensure
Self::map_native_script is used for each item).

---

Nitpick comments:
In `@pallas-utxorpc/src/shared.rs`:
- Around line 10-17: The cast in rational_number_to_u5c (converting
pallas_primitives::RationalNumber.numerator: i64 -> i32 and denominator: u64 ->
u32) can silently truncate; add defensive bounds checking and fail loudly
instead of truncating: validate numerator fits i32::MIN..=i32::MAX and
denominator fits 1..=u32::MAX (and non-zero if needed) before constructing
u5c::RationalNumber, and change rational_number_to_u5c to return
Result<u5c::RationalNumber, YourErrorType> (or propagate an existing error type)
so callers can handle out-of-range values; use i32::try_from(value.numerator)
and u32::try_from(value.denominator) or explicit range checks to implement this.

In `@pallas-utxorpc/src/v1alpha/mod.rs`:
- Around line 231-234: The mapping for conway::GovAction::Information currently
uses a magic number (6) for
u5c::governance_action::GovernanceAction::InfoAction; replace the literal with a
named constant (e.g., INFO_ACTION_V1ALPHA or INFO_ACTION_PLACEHOLDER) and add a
brief comment stating that this constant corresponds to the v1alpha protobuf
enum value (or is a temporary placeholder until the protobuf mapping is
updated), or, if available, map it to the generated protobuf enum variant
instead of a raw integer to make the intent explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 96a8bba1-795c-4735-8dec-1d314bff3dde

📥 Commits

Reviewing files that changed from the base of the PR and between 3951639 and 2f06497.

📒 Files selected for processing (10)
  • pallas-utxorpc/Cargo.toml
  • pallas-utxorpc/README.md
  • pallas-utxorpc/src/certs.rs
  • pallas-utxorpc/src/lib.rs
  • pallas-utxorpc/src/params.rs
  • pallas-utxorpc/src/shared.rs
  • pallas-utxorpc/src/v1alpha/mod.rs
  • pallas-utxorpc/src/v1beta/mod.rs
  • test_data/u5c_v1alpha.json
  • test_data/u5c_v1beta.json
💤 Files with no reviewable changes (2)
  • pallas-utxorpc/src/params.rs
  • pallas-utxorpc/src/certs.rs

Comment thread pallas-utxorpc/src/v1alpha/mod.rs
Comment thread pallas-utxorpc/src/v1beta/mod.rs
scarmuega and others added 5 commits May 2, 2026 09:05
Replaces v1alpha's "uncomment this fs::write block" pattern and v1beta's
ad-hoc REGEN_V1BETA_SNAPSHOT var with a single REGENERATE_SNAPSHOTS=1
toggle that overwrites both JSON snapshots in place. README documents it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
map_native_script was constructing u5c NativeScript::ScriptAll for the
babbage::NativeScript::ScriptAny arm, inverting the semantics ("any"
becomes "all"). The bug was pre-existing in the original lib.rs and got
copied into both v1alpha and v1beta during the dual-spec split.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rate

Mirrors pallas-utxorpc's spec-version features at the wrapper level so
downstream users depending on `pallas` can opt in to v1beta without
direct-deping `pallas-utxorpc`. Default keeps v1alpha enabled, matching
prior behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Always compile both v1alpha and v1beta into pallas-utxorpc, and require
callers to pick a version explicitly. The crate root no longer re-exports
either Mapper or spec — `pallas_utxorpc::Mapper` becomes
`pallas_utxorpc::v1alpha::Mapper` (or v1beta::Mapper).

This is a breaking change, taken deliberately: having v1alpha implicitly
be "the" Mapper at the crate root was tech-debt that would have grown
worse as v1beta matures and v1alpha eventually deprecates. Surfacing the
choice at the use-site makes the version explicit and removes the
default-feature ambiguity.

Side effects:
  * pallas-utxorpc has no Cargo features anymore.
  * pallas/Cargo.toml drops the hoisted u5c-v1alpha / u5c-v1beta
    features for the same reason.
  * Cross-version wire-compat tests are unconditional now.
  * Shared infrastructure (LedgerContext, type aliases) stays at the
    crate root — they are version-agnostic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

🧹 Nitpick comments (1)
pallas-utxorpc/src/v1alpha/mod.rs (1)

229-231: 💤 Low value

Placeholder value for InfoAction.

The magic number 6 is used as a placeholder for the v1alpha InfoAction encoding. While this works, a named constant would improve clarity.

♻️ Optional: Add a named constant
+// v1alpha encodes Information as a uint32; 6 is an arbitrary placeholder.
+const INFO_ACTION_TAG: u32 = 6;
+
 impl<C: LedgerContext> Mapper<C> {
     // ...
     conway::GovAction::Information => {
-        // The 6 is just a placeholder; v1alpha encodes Information as a uint32.
-        u5c::governance_action::GovernanceAction::InfoAction(6)
+        u5c::governance_action::GovernanceAction::InfoAction(INFO_ACTION_TAG)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pallas-utxorpc/src/v1alpha/mod.rs` around lines 229 - 231, Replace the magic
literal 6 used when mapping conway::GovAction::Information to
u5c::governance_action::GovernanceAction::InfoAction with a named constant
(e.g., INFO_ACTION_CODE) to make the intent clear; declare the constant with the
same numeric type expected by InfoAction (uint32/u32) near the top of the module
or alongside other constants, then use INFO_ACTION_CODE in the match arm instead
of the literal 6 to improve readability and maintainability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pallas-utxorpc/src/v1alpha/mod.rs`:
- Around line 229-231: Replace the magic literal 6 used when mapping
conway::GovAction::Information to
u5c::governance_action::GovernanceAction::InfoAction with a named constant
(e.g., INFO_ACTION_CODE) to make the intent clear; declare the constant with the
same numeric type expected by InfoAction (uint32/u32) near the top of the module
or alongside other constants, then use INFO_ACTION_CODE in the match arm instead
of the literal 6 to improve readability and maintainability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2588fc0-05f1-47b4-9955-5288b7c954bc

📥 Commits

Reviewing files that changed from the base of the PR and between 2f06497 and 4ab8cff.

📒 Files selected for processing (6)
  • pallas-utxorpc/Cargo.toml
  • pallas-utxorpc/README.md
  • pallas-utxorpc/src/lib.rs
  • pallas-utxorpc/src/shared.rs
  • pallas-utxorpc/src/v1alpha/mod.rs
  • pallas-utxorpc/src/v1beta/mod.rs
✅ Files skipped from review due to trivial changes (2)
  • pallas-utxorpc/Cargo.toml
  • pallas-utxorpc/src/shared.rs

Walks back the breaking change from the previous commit. The crate root
re-export of `Mapper` and `spec` returns, but now lives behind a new
`u5c-v1alpha-compat` Cargo feature that is on by default.

Default consumers see no change — `pallas_utxorpc::Mapper` keeps
resolving to the v1alpha mapper. Consumers who want the explicit-version
future can opt out with `default-features = false` and use
`pallas_utxorpc::v1alpha::Mapper` / `v1beta::Mapper` directly.

Both `pub mod v1alpha;` and `pub mod v1beta;` remain unconditional; the
feature only gates the root re-export.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scarmuega scarmuega merged commit a29700a into main May 2, 2026
13 of 15 checks passed
@scarmuega scarmuega deleted the feat/utxorpc-v1beta branch May 2, 2026 13:15
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.

1 participant