Skip to content

Conversation

@whankinsiv
Copy link
Collaborator

Description

This PR introduces the initial custom indexer module. It enables downstream code to implement the ChainIndex trait and receive transaction events. The module currently supports a single index and a follow up PR will extend this to handle multiple indices within one module.

It also includes 2 CursorStore implementations (Fjall and in memory). Example usage is provided in processes/indexer/main.rs, including a simple pool cost index to demonstrate how to construct and run an indexer.

Related Issue(s)

Completes #375

How was this tested?

  • Manually running the indexer process and verifying that state updates propagate through the watch channel.
  • Restarting the process to confirm that the Fjall backed CursorStore restores the last persisted point and resumes sync from it.

Checklist

  • My code builds and passes local tests
  • I added/updated tests for my changes, where applicable
  • I updated documentation (if applicable)
  • CI is green for this PR

Impact / Side effects

No side effects for the omnibus process.

Reviewer notes / Areas to focus

custom_indexer.rs, especially the init flow and context.run behavior.

Signed-off-by: William Hankins <william@sundae.fi>
Signed-off-by: William Hankins <william@sundae.fi>
Signed-off-by: William Hankins <william@sundae.fi>
Signed-off-by: William Hankins <william@sundae.fi>
Signed-off-by: William Hankins <william@sundae.fi>
Signed-off-by: William Hankins <william@sundae.fi>
@whankinsiv whankinsiv requested a review from SupernaviX December 2, 2025 20:59
Comment on lines +101 to +107
if let Err(e) = idx.handle_onchain_tx(block, &tx).await
{
warn!(
"Failed to index tx {} in block {}: {e:#}",
tx_index, block.number
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now that we're interfacing with code outside of acropolis itself, I think we need a new error-handling strategy. Specifically, if a custom index returns an error when processing a TX, we should not keep passing data to that index.

The reason for that is, the implementation of an index is coming from user code. If that user code throws an error, something is wrong which its code can't account for; maybe a datum is malformed, maybe their DB is down, maybe it's some bug. But the application's operator should be able to fix their code or infrastructure, and after they do, the application should resume indexing from wherever it left off.

Comment on lines +110 to +113
warn!(
"Failed to decode tx {} in block {}",
tx_index, block.number
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

For similar reasons to the ones described above, if we can't decode a transaction, we should not apply future transactions. If someone submits a "malformed" transaction on-chain, we shouldn't just skip it; we should stop processing transactions until it gets rolled back or our decoder gets fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

this'll be handled in a followup

@whankinsiv whankinsiv requested a review from lowhung December 3, 2025 04:39
}

async fn save(&self, point: &Point) -> Result<()> {
let raw = bincode::serialize(point)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not likely to be a problem here as I would expect the structure of Point to be stable (it's a simple two-variant enum), however for less stable types that you're encoding/decoding, I like to include a version prefix constant to ensure compatibility with previous versions of a struct's encoding/decoding. This is particularly relevant with bincode since it's a non-self-describing format—it doesn't store field names or type information, so changes to field order or structure can cause silent deserialization failures.

Something like the following:

const CURSOR_VERSION: u8 = 1;

async fn save(&self, point: &Point) -> Result<()> {
    let mut raw = vec![CURSOR_VERSION];
    raw.extend(bincode::serialize(point)?);
    self.cursor.insert("cursor", raw)?;
    Ok(())
}

async fn load(&self) -> Result<Option<Point>> {
    let Some(bytes) = self.cursor.get("cursor")? else {
        return Ok(None);
    };
    
    match bytes.first() {
        Some(1) => Ok(Some(bincode::deserialize(&bytes[1..])?)),
        Some(v) => anyhow::bail!("unsupported cursor version: {v}"),
        None => anyhow::bail!("empty cursor data"),
    }
}

Again, this is more pertinent to data that could be subject to change in the future.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This could totally change in the future. A Point is just two fields, but as we index more forms of data we may need more info in these cursors.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Makes sense 👍🏼

Copy link
Collaborator

Choose a reason for hiding this comment

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

this'll be handed in a followup

Signed-off-by: William Hankins <william@sundae.fi>
@whankinsiv
Copy link
Collaborator Author

Merging. Simon's feedback regarding how the indexer handles decode, rollback, and tx handling errors will be included in the follow up PR.

@whankinsiv whankinsiv merged commit 9c24955 into main Dec 4, 2025
2 checks passed
@whankinsiv whankinsiv deleted the whankinsiv/indexer-module branch December 4, 2025 18:20
@whankinsiv whankinsiv restored the whankinsiv/indexer-module branch December 7, 2025 22:38
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.

4 participants