-
Notifications
You must be signed in to change notification settings - Fork 84
feat(utxorpc): implement mapping of genesis configuration to UTxO RPC Genesis type #689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@hrmtm23 looks good! |
Signed-off-by: Hermi <[email protected]>
WalkthroughThe changes introduce multi-era genesis support to Pallas by adding era transition slot tracking to Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Mapper
participant GenesisValues
participant u5c Protocol
Client->>Mapper: map_genesis(byron, shelley, alonzo, conway, params)
Mapper->>GenesisValues: available_eras()
GenesisValues-->>Mapper: [Byron, Shelley, Allegra, Mary, Alonzo, Babbage, Conway]
loop For each era
Mapper->>GenesisValues: era_start_slot(era)
GenesisValues-->>Mapper: Option<u64>
Mapper->>Mapper: aggregate era data
end
Mapper->>Mapper: map block version data<br/>map Conway governance<br/>map Alonzo parameters
Mapper->>u5c Protocol: Genesis (aggregated)
u5c Protocol-->>Client: Complete genesis object
Client->>Mapper: map_era_summaries(current_params)
loop For each available era
Mapper->>GenesisValues: slot_to_era(slot)
GenesisValues-->>Mapper: Era
Mapper->>Mapper: compute start/end slots & epochs
end
Mapper->>u5c Protocol: EraSummaries[]
u5c Protocol-->>Client: Era summaries with boundaries
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The changes introduce significant structural modifications across multiple modules: five new fields and six new methods in Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
I have resolved the conflict @scarmuega! TY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (2)
pallas-utxorpc/src/lib.rs (1)
46-64: Document the default network assumption or require explicit genesis.Mapper::default/new now assume mainnet GenesisValues. This is easy to misuse on preview/preprod/testnet. Either:
- Document prominently, or
- Prefer an explicit constructor and consider deprecating new in favor of new_with_genesis.
I can add rustdoc and a builder-style ctor if desired.
pallas-utxorpc/src/genesis.rs (1)
55-60: Active slots coeff scaling may lose precision.Scaling by 1000 truncates. Consider higher precision (e.g., denominator 1_000_000) or a simple fraction approximation to preserve accuracy.
Apply:
- active_slots_coeff: shelley.active_slots_coeff.map(|coeff| u5c::RationalNumber { - numerator: (coeff * 1000.0) as i32, - denominator: 1000, - }), + active_slots_coeff: shelley.active_slots_coeff.map(|coeff| { + let denom: u32 = 1_000_000; + let numer: i32 = (coeff * denom as f64).round() as i32; + u5c::RationalNumber { numerator: numer, denominator: denom } + }),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
pallas-configs/src/byron.rs(1 hunks)pallas-traverse/src/wellknown.rs(6 hunks)pallas-utxorpc/Cargo.toml(1 hunks)pallas-utxorpc/src/genesis.rs(1 hunks)pallas-utxorpc/src/lib.rs(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pallas-traverse/src/wellknown.rs (1)
pallas-traverse/src/header.rs (1)
slot(57-70)
pallas-utxorpc/src/lib.rs (1)
pallas-traverse/src/wellknown.rs (1)
default(274-276)
🔇 Additional comments (3)
pallas-utxorpc/Cargo.toml (1)
21-22: Review comment is incorrect; no workspace version mismatch exists.Only pallas-utxorpc depends on prost-types 0.14.1. No other workspace member uses prost or prost-types, so there is no version mixing to align. The concern about "mixing 0.13 and 0.14" within the workspace is unfounded and the recommendation is not actionable.
Likely an incorrect or invalid review comment.
pallas-traverse/src/wellknown.rs (1)
119-125: Confirm the preview Conway start slot value from official Cardano sources.The code structure is actually correct—setting
babbage_start_slot: Nonewithconway_start_slot: Some(31424418)properly represents that preview started in Babbage era and transitioned to Conway at that slot. The era detection logic (lines 193-196) correctly treatsNoneto mean "all earlier slots belong to this era." This matches the semantic intent of your comment "Preview likely started in Babbage era."However, you should verify that
31424418is the official preview testnet Conway era start slot from Cardano's official sources (e.g., cardano-node repository, IOG documentation, or the official genesis files atbook.world.dev.cardano.org/environments/preview/). I couldn't locate this specific value in publicly available documentation to confirm its accuracy.pallas-configs/src/byron.rs (1)
80-87: Verify omega field origin and backward-compatibility requirements.The review comment raises a valid concern, but verification reveals conflicting signals:
- Official Cardano Byron genesis specification lists heavyDelegation fields as: delegate hash, VRF key hash, and certificate—no omega mentioned
- Test genesis files in the repo (mainnet-byron-genesis.json, preview-byron-genesis.json) both contain
omega: 0- The struct requires
omega: u32without#[serde(default)]orOptionWithout access to git history, it's unclear whether:
omegais a pre-existing field already present in real genesis data, making the struct change safeomegais newly added, creating a backward-compatibility risk for existing genesis files lacking it- The test data represents the authoritative format or a Pallas-specific variant
Required verification: Confirm whether omega has been present in Cardano's official Byron genesis format from chain inception, or if it's a recent addition/Pallas extension. If real-world genesis files lack omega, the
#[serde(default)]suggestion is necessary.
| // Era transitions - source: official Cardano documentation | ||
| allegra_start_slot: Some(16588800), | ||
| mary_start_slot: Some(23068800), | ||
| alonzo_start_slot: Some(39916975), | ||
| babbage_start_slot: Some(72316896), | ||
| conway_start_slot: Some(133660855), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify mainnet transition slots.
Hardcoded slots for Allegra/Mary/Alonzo/Babbage/Conway must match canonical sources.
🌐 Web query:
Confirm Cardano mainnet era transition absolute slot numbers for: Allegra, Mary, Alonzo, Babbage, Conway.
Return authoritative sources.
💡 Result:
Here are the mainnet era-transition absolute slot boundaries (last absolute slot of the previous era) with authoritative sources:
- Allegra → Mary: last Allegra absolute slot = 23,068,793. [1]
- Mary → Alonzo: last Mary absolute slot = 39,916,796. [1]
- Alonzo → Babbage: last Alonzo absolute slot = 72,316,796. [1]
- Babbage → Conway: last Babbage absolute slot = 133,660,799. [1]
(Therefore the next-era first absolute slot = last-slot + 1 for each transition.)
Sources:
[1] Ogmios — Chain synchronization / “Points of interest” (mainnet era-boundaries with absolute slot numbers). [2] Cardano Wallet — Eras (explanation of era-switching by on‑chain proposal). [1]: https://ogmios.dev/mini-protocols/local-chain-sync/ [2]: https://cardano-foundation.github.io/cardano-wallet/design/concepts/eras.html
If you want I can also return the exact first-slot-of-era values (last+1) and block hashes for those boundary blocks.
Fix era transition slots to match Cardano mainnet canonical values.
The hardcoded slot values do not match authoritative Ogmios and Cardano Wallet documentation. Correct values are:
- mary_start_slot: 23068794 (currently 23068800)
- alonzo_start_slot: 39916797 (currently 39916975)
- babbage_start_slot: 72316797 (currently 72316896)
- conway_start_slot: 133660800 (currently 133660855)
🤖 Prompt for AI Agents
In pallas-traverse/src/wellknown.rs around lines 67 to 73, the hardcoded era
transition slot constants are incorrect; update the values to match Cardano
mainnet canonical sources by changing mary_start_slot to Some(23068794),
alonzo_start_slot to Some(39916797), babbage_start_slot to Some(72316797), and
conway_start_slot to Some(133660800) so the file uses the authoritative
Ogmios/Cardano Wallet slot numbers.
| /// Get era from slot number using HFC data | ||
| pub fn slot_to_era(&self, slot: u64) -> crate::Era { | ||
| use crate::Era; | ||
|
|
||
| if slot < self.shelley_known_slot { | ||
| Era::Byron | ||
| } else if self.allegra_start_slot.map_or(true, |s| slot < s) { | ||
| Era::Shelley | ||
| } else if self.mary_start_slot.map_or(true, |s| slot < s) { | ||
| Era::Allegra | ||
| } else if self.alonzo_start_slot.map_or(true, |s| slot < s) { | ||
| Era::Mary | ||
| } else if self.babbage_start_slot.map_or(true, |s| slot < s) { | ||
| Era::Alonzo | ||
| } else if self.conway_start_slot.map_or(true, |s| slot < s) { | ||
| Era::Babbage | ||
| } else { | ||
| Era::Conway | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix slot_to_era when intermediate era start slots are None.
Current map_or(true, …) short‑circuits to earlier eras; e.g., preview classifies everything as Shelley. Implement skip‑if‑None with rolling last‑known era.
Apply:
- pub fn slot_to_era(&self, slot: u64) -> crate::Era {
- use crate::Era;
-
- if slot < self.shelley_known_slot {
- Era::Byron
- } else if self.allegra_start_slot.map_or(true, |s| slot < s) {
- Era::Shelley
- } else if self.mary_start_slot.map_or(true, |s| slot < s) {
- Era::Allegra
- } else if self.alonzo_start_slot.map_or(true, |s| slot < s) {
- Era::Mary
- } else if self.babbage_start_slot.map_or(true, |s| slot < s) {
- Era::Alonzo
- } else if self.conway_start_slot.map_or(true, |s| slot < s) {
- Era::Babbage
- } else {
- Era::Conway
- }
- }
+ pub fn slot_to_era(&self, slot: u64) -> crate::Era {
+ use crate::Era;
+ if slot < self.shelley_known_slot {
+ return Era::Byron;
+ }
+ // List known transitions in order, skipping unknowns.
+ let transitions: &[(Era, Option<u64>)] = &[
+ (Era::Allegra, self.allegra_start_slot),
+ (Era::Mary, self.mary_start_slot),
+ (Era::Alonzo, self.alonzo_start_slot),
+ (Era::Babbage, self.babbage_start_slot),
+ (Era::Conway, self.conway_start_slot),
+ ];
+ let mut current = Era::Shelley;
+ for (era, maybe_start) in transitions {
+ if let Some(start) = maybe_start {
+ if slot < *start {
+ return current;
+ }
+ current = *era;
+ }
+ }
+ current
+ }📝 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.
| /// Get era from slot number using HFC data | |
| pub fn slot_to_era(&self, slot: u64) -> crate::Era { | |
| use crate::Era; | |
| if slot < self.shelley_known_slot { | |
| Era::Byron | |
| } else if self.allegra_start_slot.map_or(true, |s| slot < s) { | |
| Era::Shelley | |
| } else if self.mary_start_slot.map_or(true, |s| slot < s) { | |
| Era::Allegra | |
| } else if self.alonzo_start_slot.map_or(true, |s| slot < s) { | |
| Era::Mary | |
| } else if self.babbage_start_slot.map_or(true, |s| slot < s) { | |
| Era::Alonzo | |
| } else if self.conway_start_slot.map_or(true, |s| slot < s) { | |
| Era::Babbage | |
| } else { | |
| Era::Conway | |
| } | |
| } | |
| pub fn slot_to_era(&self, slot: u64) -> crate::Era { | |
| use crate::Era; | |
| if slot < self.shelley_known_slot { | |
| return Era::Byron; | |
| } | |
| // List known transitions in order, skipping unknowns. | |
| let transitions: &[(Era, Option<u64>)] = &[ | |
| (Era::Allegra, self.allegra_start_slot), | |
| (Era::Mary, self.mary_start_slot), | |
| (Era::Alonzo, self.alonzo_start_slot), | |
| (Era::Babbage, self.babbage_start_slot), | |
| (Era::Conway, self.conway_start_slot), | |
| ]; | |
| let mut current = Era::Shelley; | |
| for (era, maybe_start) in transitions { | |
| if let Some(start) = maybe_start { | |
| if slot < *start { | |
| return current; | |
| } | |
| current = *era; | |
| } | |
| } | |
| current | |
| } |
🤖 Prompt for AI Agents
pallas-traverse/src/wellknown.rs around lines 181 to 200: slot_to_era currently
uses map_or(true, |s| slot < s) which treats missing (None) start slots as if
the era boundary is in the future and thus short‑circuits to earlier eras;
change the logic to skip None boundaries and only compare when a start slot is
Some. Implement a sequential check or a rolling approach: start with
Byron/Shelley decision using shelley_known_slot, then for each subsequent era
boundary (allegra, mary, alonzo, babbage, conway) only compare slot < start when
the Option is Some, otherwise skip that check and continue to the next boundary;
alternatively maintain a current_era and advance it only when a boundary is Some
and slot >= that boundary, then return the final current_era.
| constitution: Some(u5c::Constitution { | ||
| anchor: Some(u5c::Anchor { | ||
| url: conway.constitution.anchor.url.clone(), | ||
| content_hash: conway.constitution.anchor.data_hash.clone().into(), | ||
| }), | ||
| hash: conway | ||
| .constitution | ||
| .script | ||
| .clone() | ||
| .unwrap_or_default() | ||
| .into(), | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Confirm constitution.hash meaning.
Spec typically expects a guardrail script hash, not the script bytes. You’re passing the optional script content (or empty). Validate against UTxO RPC schema.
🌐 Web query:
In utxorpc v1alpha cardano Genesis.Constitution, does `hash` hold the guardrail script hash or script bytes?
💡 Result:
It holds the guardrails script hash (the 28-byte script hash), not the full script bytes. [1][2][3]
Sources:
- Ogmios changelog (notes guardrails field contains a script hash, blake2b 28 bytes). [1]
- Cardano Constitution / docs (describes a script hash being associated with the Constitution/Guardrails Script). [2]
- UTxORPC Cardano module reference (module uses script/script_hash types; scripts are referenced by hash). [3]
🏁 Script executed:
#!/bin/bash
# Examine the genesis.rs file around lines 110-121 to understand context and types
sed -n '100,130p' pallas-utxorpc/src/genesis.rsLength of output: 1354
🏁 Script executed:
#!/bin/bash
# Search for conway structure/type definition and constitution fields
rg -n -A5 "conway\." pallas-utxorpc/src/genesis.rs | head -40Length of output: 1905
🏁 Script executed:
#!/bin/bash
# Look for imports and see what u5c::Constitution and related types are
rg -n "use.*u5c|struct Constitution|conway:" pallas-utxorpc/src/genesis.rs | head -20Length of output: 94
🏁 Script executed:
#!/bin/bash
# Find conway module and GenesisFile structure
rg -n "pub.*struct GenesisFile|pub.*constitution" --type rust | grep -i conway | head -20Length of output: 277
🏁 Script executed:
#!/bin/bash
# Search for conway module definition in the codebase
fd -name "*.rs" -exec rg -l "mod conway|pub mod conway" {} \;Length of output: 230
🏁 Script executed:
#!/bin/bash
# Look for constitution-related types in conway module
rg -n "pub.*constitution|pub struct.*Constitution" --type rust | head -20Length of output: 870
🏁 Script executed:
#!/bin/bash
# Check if there's a script_hash field separate from script in any constitution struct
rg -n "script_hash|guardrail" --type rust | head -30Length of output: 3450
🏁 Script executed:
#!/bin/bash
# Inspect the Constitution struct definition in pallas-configs/src/conway.rs
sed -n '47,80p' pallas-configs/src/conway.rsLength of output: 777
🏁 Script executed:
#!/bin/bash
# Get more context around the Constitution struct definition
rg -n -A15 "pub struct Constitution" pallas-configs/src/conway.rsLength of output: 403
🏁 Script executed:
#!/bin/bash
# Check the UTxORPC spec type definition for u5c::Constitution
rg -n "message Constitution|pub struct Constitution" --type rust | grep -i "utxorpc\|u5c"Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for script vs script_hash in conway module
rg -n "\.script|\.script_hash" pallas-configs/src/conway.rsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look for script hashing or blake2b utilities in the codebase
rg -n "blake2b|script.*hash|compute.*hash" pallas-utxorpc/src/ | head -20Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check nearby code in genesis.rs to see how other script hashes are handled
sed -n '1,50p' pallas-utxorpc/src/genesis.rsLength of output: 2282
🏁 Script executed:
#!/bin/bash
# Look at the full function context where this Constitution code is
rg -n -B20 "constitution: Some" pallas-utxorpc/src/genesis.rs | head -50Length of output: 2113
🏁 Script executed:
#!/bin/bash
# Check if there are other similar issues with script handling in the file
rg -n "\.script\." pallas-utxorpc/src/genesis.rsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Find the UTxORPC spec types (likely in proto or generated rust)
find . -name "*.rs" -type f | xargs rg -l "u5c::|utxorpc.*cardano" | head -10Length of output: 120
🏁 Script executed:
#!/bin/bash
# Look for blake2b or script hashing in pallas-validate or pallas-crypto
rg -n "blake2b|compute.*script.*hash" --type rust | grep -E "validate|crypto" | head -20Length of output: 2104
🏁 Script executed:
#!/bin/bash
# Check what the .into() conversion produces for Option<String>
rg -n "impl.*From.*Option.*String|impl.*Into.*for.*Option" pallas-utxorpc/src/ --type rustLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look for hash function usage elsewhere in the file
sed -n '1,300p' pallas-utxorpc/src/genesis.rs | rg -n "hash|Hash"Length of output: 123
Hash must be computed from script, not passed as script bytes.
The code passes conway.constitution.script (the script content) directly to the hash field. Per UTxORPC spec, this field should contain the 28-byte guardrails script hash, not the script bytes. Use script hashing utilities from pallas-validate::utils (e.g., compute_native_script_hash, compute_plutus_v1_script_hash) to compute the hash before assignment. Currently, the hash field will contain incorrect data.
🤖 Prompt for AI Agents
In pallas-utxorpc/src/genesis.rs around lines 110 to 121, the constitution.hash
is being set from the raw script bytes (conway.constitution.script) but must
contain the 28-byte guardrails script hash; replace the direct use of script
bytes with a computed hash using the appropriate pallas-validate::utils helper
(e.g., compute_native_script_hash for native scripts or
compute_plutus_v1_script_hash for Plutus V1), handle the Option/script variant
safely (return default or None if no script), convert the resulting 28-byte hash
into the expected type for the u5c::Constitution.hash field, and add the
necessary use/import for the chosen compute_* function.
| // Conway voting thresholds | ||
| drep_voting_thresholds: Some(u5c::DRepVotingThresholds { | ||
| motion_no_confidence: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.motion_no_confidence * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| committee_normal: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.committee_normal * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| committee_no_confidence: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.committee_no_confidence * 100.0) | ||
| as i32, | ||
| denominator: 100, | ||
| }), | ||
| update_to_constitution: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.update_to_constitution * 100.0) | ||
| as i32, | ||
| denominator: 100, | ||
| }), | ||
| hard_fork_initiation: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.hard_fork_initiation * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| pp_network_group: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.pp_network_group * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| pp_economic_group: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.pp_economic_group * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| pp_technical_group: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.pp_technical_group * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| pp_gov_group: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.pp_gov_group * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| treasury_withdrawal: Some(u5c::RationalNumber { | ||
| numerator: (conway.d_rep_voting_thresholds.treasury_withdrawal * 100.0) as i32, | ||
| denominator: 100, | ||
| }), | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Rational thresholds: confirm required precision.
Multiplying by 100 and casting truncates. If spec demands precise thresholds, increase denominator and round.
Also applies to: 172-195
🌐 Web query:
What numeric precision is required for DRep/Pool voting thresholds in utxorpc v1alpha? Are percentages encoded as exact rationals or is 2‑decimal fixed acceptable?
💡 Result:
Short answer: thresholds are encoded as exact rationals (RationalNumber — numerator/denominator), not a 2‑decimal fixed string. You can of course express common percentages as rationals (e.g. 0.50 → 1/2 or 50/100), but send them using the spec's RationalNumber/UnitInterval types rather than assuming 2‑decimal fixed-point. [1][2][3]
Sources:
- UTxO RPC spec / docs (shows Cardano voting threshold types live in the v1alpha Cardano schema). [1]
- docs.rs listing for the generated utxorpc-spec showing DRepVotingThresholds / PoolVotingThresholds and RationalNumber types. [2]
- Example code (aiken) converting Pool/DRep thresholds to Plutus using rational/array-of-rationals representation. [3]
I'll verify the source of the threshold values and their precision to determine if truncation is a legitimate concern.
🏁 Script executed:
#!/bin/bash
# Find where conway struct is defined and how thresholds are sourced
rg -n "d_rep_voting_thresholds" --type=rs -A 2 -B 2Length of output: 26
🏁 Script executed:
#!/bin/bash
# Search for conway struct definition
rg -n "struct.*[Cc]onway" --type=rs -A 5 | head -100Length of output: 26
🏁 Script executed:
#!/bin/bash
# Look for genesis data loading/parsing to understand source precision
rg -n "genesis|from_json|parse.*conway" --type=rs -A 3 -B 3 | head -150Length of output: 26
🏁 Script executed:
#!/bin/bash
# Check the full context around the flagged lines in genesis.rs
fd "genesis.rs" --type=rs --exec cat {} \; | head -250Length of output: 169
🏁 Script executed:
#!/bin/bash
# Search for similar rational conversions elsewhere
rg -n "RationalNumber|as i32.*denominator" --type=rs -B 3 -A 1 | head -100Length of output: 26
---
🏁 Script executed:
```shell
#!/bin/bash
# Find where conway struct is defined and threshold values sourced
rg -n "d_rep_voting_thresholds" --type rust -A 2 -B 2
Length of output: 4150
🏁 Script executed:
#!/bin/bash
# Search for conway struct definition
rg -n "struct.*[Cc]onway" --type rust -A 5 | head -100Length of output: 584
🏁 Script executed:
#!/bin/bash
# Look for genesis data loading
rg -n "genesis|from_json|parse" --type rust | grep -i conway | head -50Length of output: 917
🏁 Script executed:
#!/bin/bash
# Get the full genesis.rs file to understand context
find . -name "genesis.rs" -exec cat {} \;Length of output: 15654
🏁 Script executed:
#!/bin/bash
# Search for RationalNumber usage patterns
rg -n "RationalNumber" --type rust -B 3 -A 1 | head -80Length of output: 5979
---
🏁 Script executed:
```shell
#!/bin/bash
# Find DRepVotingThresholds struct definition
rg -n "struct DRepVotingThresholds|pub struct DRepVotingThresholds" --type rust -A 12
Length of output: 2939
🏁 Script executed:
#!/bin/bash
# Check conway config types (look at pallas-configs/src/conway.rs)
cat pallas-configs/src/conway.rs | head -150Length of output: 2871
🏁 Script executed:
#!/bin/bash
# Search for any test genesis data or examples with voting thresholds
rg -n "motion_no_confidence|committee_normal" --type rust -B 2 -A 2 | grep -A 2 -B 2 "0\."Length of output: 1630
🏁 Script executed:
#!/bin/bash
# Check if similar rational conversions elsewhere suggest a pattern
rg -n "\* (100|1000)\.0.*as i32" --type rustLength of output: 1671
Add rounding to preserve voting threshold precision.
Voting thresholds are loaded from f32 genesis values and converted to rationals. Current code truncates (e.g., 50.1 → 50) instead of rounding. Per spec, exact rationals are required. Add .round() before the as i32 cast on all voting threshold conversions, or use a higher denominator to preserve full f32 precision.
Lines affected: 130, 134, 138, 143, 148, 152, 156, 160, 164, 168 (DRep); 174, 178, 182, 187, 191 (Pool).
Suggested fix:
numerator: (conway.d_rep_voting_thresholds.motion_no_confidence * 100.0).round() as i32,🤖 Prompt for AI Agents
In pallas-utxorpc/src/genesis.rs around lines 127 to 171 (DRep voting
thresholds) and similarly for the Pool lines noted in the review, the
conversions from f32 to integer numerator are truncating (e.g., 50.1 → 50);
change each numerator expression to round the multiplied value before casting
(e.g., apply .round() on the result of * 100.0) so the RationalNumber numerators
preserve correct precision, or alternatively increase the denominator to capture
full f32 precision consistently across all listed threshold fields.
| pub fn map_era_summaries( | ||
| &self, | ||
| current_params: Option<MultiEraProtocolParameters>, | ||
| ) -> u5c::EraSummaries { | ||
| // Include all eras that exist, not just ones with available slot data | ||
| let all_eras = self.genesis.available_eras(); | ||
|
|
||
| let summaries = all_eras | ||
| .iter() | ||
| .enumerate() | ||
| .map(|(i, era)| { | ||
| let start_slot = self.genesis.era_start_slot(*era); | ||
| let end_slot = if i < all_eras.len() - 1 { | ||
| self.genesis.era_start_slot(all_eras[i + 1]) | ||
| } else { | ||
| None // Current era has no end | ||
| }; | ||
|
|
||
| let is_current_era = i == all_eras.len() - 1; | ||
|
|
||
| u5c::EraSummary { | ||
| name: era.to_string().to_lowercase(), | ||
| start: start_slot.map(|slot| { | ||
| let (epoch, _) = self.genesis.absolute_slot_to_relative(slot); | ||
| u5c::EraBoundary { | ||
| time: self.genesis.slot_to_wallclock(slot), | ||
| slot, | ||
| epoch, | ||
| } | ||
| }), | ||
| end: end_slot.map(|slot| { | ||
| let (epoch, _) = self.genesis.absolute_slot_to_relative(slot); | ||
| u5c::EraBoundary { | ||
| time: self.genesis.slot_to_wallclock(slot), | ||
| slot, | ||
| epoch, | ||
| } | ||
| }), | ||
| protocol_params: if is_current_era { | ||
| current_params | ||
| .as_ref() | ||
| .map(|params| self.map_pparams(params.clone())) | ||
| } else { | ||
| None | ||
| }, | ||
| } | ||
| }) | ||
| .collect(); | ||
|
|
||
| u5c::EraSummaries { summaries } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pick the actual current era for protocol_params.
Using the last enum entry as “current” sets protocol_params on Conway even when earlier eras are current or when slots are unknown. Use the last era with a known start slot instead.
Apply:
- let summaries = all_eras
+ // Determine the last era with a known start slot (at least Shelley is known).
+ let last_known_index = all_eras
+ .iter()
+ .enumerate()
+ .rfind(|(_, era)| self.genesis.era_start_slot(**era).is_some())
+ .map(|(i, _)| i)
+ .unwrap_or(1); // Byron(0) and Shelley(1) always known
+
+ let summaries = all_eras
.iter()
.enumerate()
.map(|(i, era)| {
let start_slot = self.genesis.era_start_slot(*era);
let end_slot = if i < all_eras.len() - 1 {
self.genesis.era_start_slot(all_eras[i + 1])
} else {
None // Current era has no end
};
- let is_current_era = i == all_eras.len() - 1;
+ let is_current_era = i == last_known_index;| timestamp: block.wallclock(&self.genesis), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve ledger-provided timestamps; fall back to genesis wallclock.
Previously, when available, LedgerContext::get_slot_timestamp(slot) provided authoritative timestamps. Now it’s ignored. Prefer ledger first, then compute.
Apply:
u5c::Block {
header: u5c::BlockHeader {
slot: block.slot(),
hash: block.hash().to_vec().into(),
height: block.number(),
}
.into(),
body: u5c::BlockBody {
tx: block.txs().iter().map(|x| self.map_tx(x)).collect(),
}
.into(),
- timestamp: block.wallclock(&self.genesis),
+ timestamp: self.ledger
+ .as_ref()
+ .and_then(|ctx| ctx.get_slot_timestamp(block.slot()))
+ .unwrap_or_else(|| block.wallclock(&self.genesis)),
}📝 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.
| timestamp: block.wallclock(&self.genesis), | |
| } | |
| timestamp: self.ledger | |
| .as_ref() | |
| .and_then(|ctx| ctx.get_slot_timestamp(block.slot())) | |
| .unwrap_or_else(|| block.wallclock(&self.genesis)), | |
| } |
🤖 Prompt for AI Agents
In pallas-utxorpc/src/lib.rs around lines 747 to 748, the code unconditionally
uses block.wallclock(&self.genesis) for the timestamp; update it to prefer the
ledger-provided timestamp first (call the ledger/ledger context method that
returns an Option<Result/Option-like> timestamp for the given slot) and only
fall back to computing the wallclock from genesis when the ledger has no
timestamp; ensure you handle the Option/Result correctly (map/and_then or
unwrap_or) so the final field remains the same type as before.
Summary by CodeRabbit
New Features
Chores