Skip to content

Conversation

@AloeareV
Copy link
Contributor

Fixes: #769

This PR adds a validator_finalized_height field to a snapshot. If this height is higher than the snapshot's tip, most APIs will pass requests through to the backing validator.

@fluidvanadium fluidvanadium self-assigned this Jan 19, 2026
@idky137
Copy link
Contributor

idky137 commented Jan 20, 2026

Hey I've been mulling over the problem with testing this functionality and came up with a possible solution (you may have already fixed this but thought I would add this in case it has not been possible to implement in the "MockChainSource".

I think one way we could implement the test only "Stop finalised state syncing at block X" is to add a "test" feature flagged field ("pause_sync_at_height?) to the "BlockCacheConfig" struct (or we could define a "TestVariables" struct to be added as this new field?):

pub struct BlockCacheConfig {

Then under the same "test" feature flag stop writing to the finalised state at the given block height in "start_sync_loop":

fs.write_block(next_finalized_block.clone())

It may still be cleaner to implement this in the "MockChainSource" if possible but that could be more intrusive than just adding here.

@AloeareV
Copy link
Contributor Author

Hey I've been mulling over the problem with testing this functionality and came up with a possible solution (you may have already fixed this but thought I would add this in case it has not been possible to implement in the "MockChainSource".

I think one way we could implement the test only "Stop finalised state syncing at block X" is to add a "test" feature flagged field ("pause_sync_at_height?) to the "BlockCacheConfig" struct (or we could define a "TestVariables" struct to be added as this new field?):

pub struct BlockCacheConfig {

Then under the same "test" feature flag stop writing to the finalised state at the given block height in "start_sync_loop":

fs.write_block(next_finalized_block.clone())

It may still be cleaner to implement this in the "MockChainSource" if possible but that could be more intrusive than just adding here.

I just thought of something, which I'm gonna try out...if we add a customizable delay to MockchainSource methods, we could do some experimentation with simulated network latency...and if we make the delay long enough, it could slow sync down to a crawl, and still allow passthroughs to go through (as a passthrough requires one or maybe two calls to the source, a one-second delay would mean that the passthrough takes a couple seconds, and syncing a block would also take seconds, so if we make the chain a few hundred blocks we won't catch up to the finalized boundary before the request comes in.)

@AloeareV AloeareV force-pushed the chainindex_passthrough_behind_zebra branch from ea266dd to 62a42d3 Compare January 27, 2026 16:56
@AloeareV
Copy link
Contributor Author

AloeareV commented Jan 29, 2026

An open question: When a passthrough request is made, and the needed information is in the nonfinalized state, is this:

  1. An Ok(None) return, as if we and the validator were synced to the top of the finalized range
  2. An Error return, indicating that the information requested exists but isn't synced yet
  3. Something else?

If 2, do we just return this error whenever it's convenient (i.e., when we don't need to additional work to distinguish between the requested information being nonfinalized or nonexistent), or do we make the guarantee of correctly identifying this error in all cases?
Also, if 2, if the request covers multiple blocks and some of them are finalized, do we consider this a success case and return everything finalized, or an error case as the request covered information in the nonfinalized state that we can't safely serve until we've caught up to it? Possibly even vary by specific case/method?

@AloeareV
Copy link
Contributor Author

Still to be done:

  • Make sure passthrough only triggers when the validator's finalized tip is ahead of ChainIndexes best (nonfinalized) tip.
  • Some error handling
  • Fix passthrough triggering before local lookup in some RPCs

@AloeareV AloeareV force-pushed the chainindex_passthrough_behind_zebra branch from 9f49584 to 02959d4 Compare January 29, 2026 18:34
}

// if the tranasction isn't finalized on the best chain
// check our indexes
Copy link
Contributor

Choose a reason for hiding this comment

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

should we check the index first?

SyncError::ReorgFailure(String::from("could not determine best chain"))
}
UpdateError::ValidatorConnectionError => SyncError::ZebradConnectionError(todo!(
"don't have a request error. \
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To any reviewers: I would appreciate help figuring out what to do with this error case.

Copy link
Contributor

Choose a reason for hiding this comment

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

I will look into it.

.await
.map_err(|e| InitError::InvalidNodeData(Box::new(e)))?
.ok_or_else(|| {
InitError::InvalidNodeData(todo!("we need something that implements error"))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As well as this error case

@fluidvanadium
Copy link
Contributor

fluidvanadium commented Feb 5, 2026

Why do self.get_fullblock_bytes_from_node and self.blockchain_source.get_block look so different? shouldnt they be sister functions?

.source
.get_best_block_height()
.await
.map_err(|_| UpdateError::ValidatorConnectionError)?
Copy link
Contributor

Choose a reason for hiding this comment

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

this sort of mapping i think is a bit troublesome, as it essentially hides any trace of the original error. I think the correct way of mapping it would be some way in the higher error can be traced back to the original through .source().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I think this was meant to be a todo!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a source error to the enum variant!

@AloeareV
Copy link
Contributor Author

AloeareV commented Feb 5, 2026

Why do self.get_fullblock_bytes_from_node and self.blockchain_source.get_block look so different? shouldnt they be sister functions?

How do you mean 'so different'? They're not sisters, one calls the other. get_fullblock_bytes_from_node calls get_block and then serializes it to a vec.

(Originally, I somehow edited your message with my answer. I've turned it into a reply now)

@AloeareV AloeareV marked this pull request as ready for review February 5, 2026 20:56
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.

Passthrough queries when not fully synced

4 participants