Skip to content

Conversation

@golddydev
Copy link
Collaborator

@golddydev golddydev commented Nov 16, 2025

Description

This PR introduces a new dedicated historical-epochs-state module for managing historical epoch data with persistent storage, refactors the existing epochs-state module to remove historical tracking responsibilities, and improves CBOR encoding for EpochActivityMessage with custom u128 encoder/decoder.

Changes

1. New historical-epochs-state Module

  • Purpose: Dedicated module for storing and querying historical epoch activity data for Blockfrost API compatibility
  • Architecture: Volatile + Immutable dual-layer storage
    • Volatile: In-memory state for recent epochs (rollback support)
    • Immutable: Persistent disk storage using fjall LSM-tree database
  • Features:
    • Automatic pruning based on security parameter k
    • Background persistence worker (bounded channel to prevent backpressure)
    • Bounded pending queue (5 max) to prevent memory exhaustion
    • Rollback support for chain reorganizations
    • Query handlers for: GetEpochInfo, GetNextEpochs, GetPreviousEpochs

2. REST API Improvements

  • Fixed: handle_epoch_info_blockfrost now returns latest epoch when numeric param equals latest
  • Enhanced: Better separation between current epoch queries (epochs-state) vs historical queries

Fixes

1. Epoch Activity Message Timing

  • Fixed: Publish epoch activity message before evolving nonce
  • Impact: Ensures consumers receive correct epoch activity message (with corresponding epoch's nonce)
    2. Byron Block Mint Handling
  • Fixed: Handle mint for Byron-era blocks correctly

Related Issue(s)

Related to #364

How was this tested?

  • Added test cases for historical_epochs_state to test volatile's prune, correct persistent and reading from disk.
  • Added test cases for minicbor encode/decode for u128

Checklist

  • My code builds and passes local tests
  • I added/updated tests for my changes, where applicable
  • I added some comments
  • CI is green for this PR

Impact / Side effects

  • This PR off-loads the memory usage which is consumed by epochs_state to serve in-memory epochs history.

Reviewer notes / Areas to focus

  • historical_epochs_state module is the main changes of this PR.

@golddydev golddydev requested a review from Copilot November 17, 2025 14:23
Copilot finished reviewing on behalf of golddydev November 17, 2025 14:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a new historical_epochs_state module to track historical epoch information separately from the main epochs_state module, improving separation of concerns and enabling optional historical data storage. The refactoring moves epoch history storage from an in-memory DashMap to a persistent fjall database with proper volatile/immutable state separation.

Key Changes

  • New historical_epochs_state module with persistent storage using fjall database
  • Refactored epochs_state to remove history tracking and delegate to the new module
  • Updated REST handlers to query historical epochs from the new dedicated state module
  • Added CBOR serialization support for EpochActivityMessage and related types

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
modules/historical_epochs_state/* New module implementing persistent historical epoch storage with volatile/immutable state separation
modules/epochs_state/src/epochs_state.rs Removed epoch history logic and simplified to focus on current epoch tracking
modules/epochs_state/src/state.rs Changed handle_mint to accept Option<&[u8]> for issuer_vkey
modules/epochs_state/src/epochs_history.rs Deleted - functionality moved to new module
modules/epochs_state/src/store_config.rs Deleted - no longer needed
modules/rest_blockfrost/src/handlers/epochs.rs Updated to route historical queries to new module and validate epoch bounds
modules/rest_blockfrost/src/handlers_config.rs Added historical epochs query topic configuration
common/src/queries/epochs.rs Added DEFAULT_HISTORICAL_EPOCHS_QUERY_TOPIC constant
common/src/messages.rs Added minicbor serialization derives to EpochActivityMessage
common/src/protocol_params.rs Added minicbor serialization to Nonce types
common/src/cbor.rs New module with custom u128 CBOR codec
processes/omnibus/* Registered new module and added configuration section
modules/accounts_state/src/accounts_state.rs Fixed log message from "subscriber" to "publisher"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@golddydev golddydev marked this pull request as ready for review November 17, 2025 15:32
Comment on lines 96 to 97
for epoch in range {
let slice = self.epochs_history.get(Self::make_epoch_key(epoch))?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The fjall library has a range method which takes a range of keys and fetches everything in that range at once. Probably faster to use that and let fjall batch stuff.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed
6a951d1

Comment on lines 26 to 30
let db_path = if Path::new(&config.db_path).is_relative() {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(&config.db_path)
} else {
PathBuf::from(&config.db_path)
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

CARGO_MANIFEST_DIR is set at compile time, to a path on the compiling machine. It won't generally work to use it at runtime.

Honestly, we probably should just pass relative paths along unchanged. Filesystem APIs will treat them as relative to the PWD at runtime, which is probably the behavior we want for them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

28d9e0b

Thank you for pointing this out.
Now it uses relative path (both accounts and epochs historical state) and prefix default db path with fjall-* to add them .gitignore easily.

@golddydev golddydev requested a review from SupernaviX November 18, 2025 06:05
Copy link
Collaborator

@whankinsiv whankinsiv left a comment

Choose a reason for hiding this comment

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

Great job! Could you please explain why this modules ready_to_prune logic is offset by one epoch compared to historical_accounts_state.

Comment on lines +18 to +20
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
let cfg = fjall::Config::new(path)
// 4MB write buffer since EpochActivityMessage is not that big
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you please add a clear-on-start config option.

Copy link
Collaborator

@lowhung lowhung left a comment

Choose a reason for hiding this comment

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

Nice to see the historical responsbility split out like this!

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.

5 participants