Skip to content

Conversation

@rrruko
Copy link

@rrruko rrruko commented Oct 15, 2025

This keeps the default permissive behavior of the codecs while providing the option to enable pedantically node-conformant validations by using minicbor::decode_with(..., BasicStrictContext::new()).

See also #697, pragma-org/amaru#494

Summary by CodeRabbit

  • New Features
    • Stricter, schema-validated decoding for values and transaction bodies, rejecting zero/empty amounts and invalid fields.
    • Non-empty collections for multi-asset and withdrawals, ensuring meaningful data.
    • Helper to quickly construct a basic transaction body.
  • Refactor
    • Mint handling now guarantees non-empty, non-zero assets.
    • Size limits enforced on multi-asset bundles to prevent excessively large values.
    • Builders and validation updated to align with new representations and checks, improving robustness and error clarity.

@coderabbitai
Copy link

coderabbitai bot commented Oct 15, 2025

Walkthrough

Introduces strict decoding/validation for PositiveCoin. Refactors Conway primitives with new Multiasset, NonEmptyMap, and strict Value/TransactionBody decoding/encoding. Updates txbuilder to construct NonEmptyMultiasset mint directly. Adjusts validation to pass Multiasset representation of mint to preservation checks.

Changes

Cohort / File(s) Summary of Changes
PositiveCoin decoding
pallas-codec/src/utils.rs
Removed derived Decode; added manual minicbor Decode enforcing non-zero value with explicit error on 0.
Conway primitives refactor
pallas-primitives/src/conway/model.rs
Added Multiasset (generic), NonEmptyMultiasset, NonEmptyMap; reworked Value/Mint/Withdrawals; introduced StrictValue, StrictMint, StrictMultiasset, StrictTransactionBody, StrictWitnessSet; new tx body field decoding/encoding helpers; size and non-zero/non-empty validations; utility functions and constructors.
TxBuilder mint handling
pallas-txbuilder/src/conway.rs
Construct mint via NonEmptyMultiasset::from_multiasset(...); updated imports/types and policy iteration to NonEmptyMultiasset.
Validation mint handling
pallas-validate/src/phase1/conway.rs
In preservation check, pass mint as Multiasset (m.clone().to_multiasset()) to conway_add_minted_non_zero.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor CBOR as CBOR Data
  participant Dec as Decoder
  participant SV as StrictValue/StrictMultiasset
  participant PC as PositiveCoin
  participant TB as StrictTransactionBody
  CBOR->>Dec: Feed bytes
  Dec->>PC: Decode PositiveCoin
  alt value == 0
    PC-->>Dec: Error (invalid zero)
  else value > 0
    PC-->>Dec: PositiveCoin
  end
  Dec->>SV: Decode Multiasset/Value
  SV->>SV: Validate non-empty, non-zero, size limits
  Dec->>TB: Decode TxBody fields (by index)
  TB->>TB: Per-field strict validation
  TB-->>Dec: TransactionBody
Loading
sequenceDiagram
  autonumber
  actor App as TxBuilder
  participant NE as NonEmptyMultiasset
  participant MA as Multiasset
  App->>MA: Gather mint entries (NonZeroInt)
  MA-->>App: Multiasset map
  App->>NE: from_multiasset(MA.into_iter().collect())
  NE-->>App: NonEmptyMultiasset mint
  App-->>App: Use mint in tx body
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

I thump my paws at zeros’ fate—
No empty bags shall cross this gate.
Coins hop high, assets align,
Maps packed tight in ordered line.
Mint made stout, the checks now sing—
A tidy warren for every thing. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.93% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change of adding pedantic codec validations via a minicbor context, reflecting the core objective of the pull request and enabling reviewers to immediately understand the main feature being implemented.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

🧹 Nitpick comments (5)
pallas-validate/src/phase1/conway.rs (1)

368-369: Avoid cloning mint just to borrow Multiasset

m.clone().to_multiasset() allocates a full clone. Prefer borrowing the inner Multiasset to avoid copying.

Recommended:

  • Add as_multiasset(&self) -> &Multiasset<T> on NonEmptyMultiasset<T>.
  • Use it here to pass a borrowed &Multiasset<_>.

Apply this diff locally:

-        input = conway_add_minted_non_zero(&input, &m.clone().to_multiasset(), &PostAlonzo(NegativeValue))?;
+        // borrow mint instead of cloning
+        input = conway_add_minted_non_zero(&input, m.as_multiasset(), &PostAlonzo(NegativeValue))?;

And add this helper in pallas-primitives/src/conway/model.rs within impl<A> NonEmptyMultiasset<A>:

 impl<A> NonEmptyMultiasset<A> {
+    pub fn as_multiasset(&self) -> &Multiasset<A> {
+        &self.asset
+    }
     pub fn from_multiasset(ma: Multiasset<A>) -> Option<Self> {
         if ma.is_empty() {
             None
         } else {
             Some(NonEmptyMultiasset {
                 asset: ma,
             })
         }
     }

     pub fn to_multiasset(self) -> Multiasset<A> {
         self.asset
     }
 }
pallas-txbuilder/src/conway.rs (1)

157-161: Minor: simplify iterator type annotation

The explicit type on x isn’t needed; inference is clear from mint.iter(). Consider:

-            .flat_map(|x: &pallas_primitives::conway::NonEmptyMultiasset<NonZeroInt>| x.iter())
+            .flat_map(|x| x.iter())
pallas-primitives/src/conway/model.rs (3)

30-65: StrictContext scaffolding is solid; consider Default

The context/error accumulator is straightforward. Optionally derive Default for BasicStrictContext to ease construction alongside new().


945-957: NonEmptyMultiasset: add a borrow accessor

To avoid cloning in downstream callers (e.g., validation), expose a borrow:

pub fn as_multiasset(&self) -> &Multiasset<T> { &self.asset }

This enables borrowing the underlying Multiasset where a reference is expected.

Also applies to: 967-981


1358-1394: context_bound on Block/Tx: consider Strict wrappers

Adding #[cbor(context_bound = "StrictContext")] is useful but won’t surface errors from ctx.push_error unless a Strict wrapper is used. Consider adding StrictBlock/StrictTx wrappers mirroring StrictTransactionBody/StrictWitnessSet to fail fast in pedantic decodes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d34b143 and 937f2cf.

📒 Files selected for processing (4)
  • pallas-codec/src/utils.rs (3 hunks)
  • pallas-primitives/src/conway/model.rs (12 hunks)
  • pallas-txbuilder/src/conway.rs (3 hunks)
  • pallas-validate/src/phase1/conway.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
pallas-validate/src/phase1/conway.rs (2)
pallas-txbuilder/src/transaction/model.rs (1)
  • input (58-63)
pallas-validate/src/utils.rs (1)
  • conway_add_minted_non_zero (334-355)
pallas-primitives/src/conway/model.rs (1)
pallas-codec/src/utils.rs (19)
  • decode (22-33)
  • decode (148-161)
  • decode (326-345)
  • decode (420-430)
  • decode (495-501)
  • decode (537-543)
  • decode (593-597)
  • decode (631-637)
  • decode (672-685)
  • decode (758-769)
  • decode (849-866)
  • decode (896-917)
  • decode (1020-1026)
  • decode (1076-1084)
  • decode (1173-1183)
  • decode (1284-1296)
  • d (151-151)
  • d (329-329)
  • map (1326-1336)
pallas-txbuilder/src/conway.rs (2)
pallas-validate/src/phase2/script_context.rs (1)
  • mint (405-411)
pallas-primitives/src/conway/model.rs (1)
  • from_multiasset (968-976)
pallas-codec/src/utils.rs (1)
pallas-primitives/src/conway/model.rs (13)
  • decode (94-111)
  • decode (123-135)
  • decode (149-161)
  • decode (194-210)
  • decode (222-234)
  • decode (365-388)
  • decode (690-717)
  • decode (818-854)
  • decode (866-878)
  • decode (909-915)
  • decode (950-956)
  • decode (1123-1137)
  • decode (1264-1276)
🔇 Additional comments (10)
pallas-codec/src/utils.rs (2)

861-865: Good: enforce NonEmptySet invariant at decode

Rejecting empty sets here is correct and aligns with NonEmpty semantics used across Conway types and tests.


1001-1005: PositiveCoin: manual Decode with non‑zero validation

Removing the derived Decode and introducing a custom Decode that errors on zero is the right call. It guarantees Zero is rejected at the codec boundary and integrates cleanly with stricter Multiasset decoders.

Also applies to: 1019-1027

pallas-txbuilder/src/conway.rs (1)

71-72: Mint construction via NonEmptyMultiasset looks correct

Collecting into Multiasset and wrapping with NonEmptyMultiasset::from_multiasset matches the new primitives. No issues.

pallas-primitives/src/conway/model.rs (7)

66-112: Multiasset strict decode + size check

Decoding into a BTreeMap and pushing:

  • empty policy errors, and
  • a size‑cap error via is_multiasset_small_enough
    works well with the Strict wrappers. Relying on the A decoder (e.g., PositiveCoin/NonZeroInt) to enforce non‑zero amounts is appropriate and keeps this generic.

190-211: Value encode/decode aligns with node semantics

Encoding as coin or 2‑tuple and supporting both definite/indef arrays during decode matches Conway’s representation. Tests cover zero‑coin and empty MA cases. LGTM.


505-567: TransactionBody: custom decode validates required fields

Requiring inputs, outputs, and fee; capturing duplicates via ctx.push_error; and wiring strict types (NonEmptyMap/Multiasset) provides the intended pedantic behavior when used with StrictTransactionBody. Good direction.


678-679: Unknown txbody fields: confirm intended strictness

Decoding errors on unknown keys unconditionally (not just via the Strict wrappers). If backward compatibility with future fields is needed in permissive mode, consider pushing to ctx instead of returning Err, leaving the hard error to StrictTransactionBody.


903-916: NonEmptyMap: push error (not Err) is consistent with Strict wrappers

This lets permissive decodes proceed while StrictTransactionBody turns it into a hard error. Good balance.


1100-1117: TransactionOutput: datatype‑based decode is correct

Switching by CBOR datatype (array→legacy, map→post‑alonzo) is robust and mirrors long‑standing traversal logic.

Also applies to: 1119-1138


1398-1411: is_multiasset_small_enough: matches node behavior

The size heuristic and limit are in line with the Haskell node. Tests exercise the failure path. LGTM.

use pallas_codec::minicbor::data::Type;

pub type Mint = Multiasset<NonZeroInt>;
trait StrictContext {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Some suggestions:

  • If we name this something more general, like ValidationContext,
  • and have the push_error should return a Result that we can ?;

Then we can have a NoopContext, which does nothing; an AccumulatingContext which accumulates things as errors, and a TerminatingContext, which returns an error.

Then, our Strict<T> implements minicbor::Decode, constructs a TerminatingContext, and deserializes T;

And we could have a StrictVerbose<T> that implements minicbor::Decode, constructs an AccumulatingContext, and then concatenates all the errors to return it's own at the end.

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