feat: implement getEpochInfo RPC method - #30
Conversation
Adds the standard Solana getEpochInfo method, which wallets and explorers use to show epoch progress. The handler resolves a single ClickHouse context slot and derives every field from it so the result is a consistent snapshot: - absoluteSlot, blockHeight and transactionCount reuse the existing slot / block-height / transaction-count resolution paths - epoch, slotIndex and slotsInEpoch are computed from DEFAULT_SLOTS_PER_EPOCH, assuming the default schedule with no warmup (matching getInflationReward) Accepts the same optional config as getSlot (commitment + minContextSlot), rejects processed unless the head cache is enabled, and forwards ClickHouse query timings via the downstream header like the sibling handlers. Closes solana-rpc#28
getEpochInfo resolved the context slot from latest_slot_cache (always the finalized ClickHouse slot) and ignored the requested commitment. Under grpc-head-cache a confirmed request therefore returned finalized data and was inconsistent with getSlot/getTransactionCount, which serve the newer confirmed slot from the head cache. Resolve the slot honoring the commitment by reusing the TransactionCountPlan overlay used by getTransactionCount: when the head cache holds a newer slot that meets the commitment, derive absoluteSlot/epoch/slotIndex from it, read blockHeight from the head cache (the confirmed slot isn't in ClickHouse yet, following getBlockHeight), and compute transactionCount as the ClickHouse count before the overlay start plus the head count. With the head cache off, everything resolves to the finalized ClickHouse slot as before. Add a grpc-head-cache test asserting a confirmed getEpochInfo resolves the context slot to the head slot rather than the ClickHouse slot.
|
Okay, so I've been reviewing this one a bit deeper because it's continuing a bad pattern that I introduced a while ago. Basically, I think the best approach would be to merge this PR in as-is, and then follow up with another PR to fix these to calculate properly for non-mainnet chains. |
|
Yeah, this goes wider than getEpochInfo. I hit the same assumption in getEpochSchedule (#36), which hardcodes the mainnet no-warmup schedule. And getInflationReward on main does it too, in two spots (the default-epoch math and the epoch-to-slot bounds). So it's three methods all assuming mainnet, because the RPC layer has no source for the cluster's real schedule. The design question before any fix: where should that schedule come from? The ingestor already calls I'm close to this through #36, so once there's a direction I can take the follow-up across all three. |
Agreed! I think the ingest-and-store pattern for leader schedule is probably the right approach for Superbank given its goals. Since not all sources expose the schedules (or even have them), we'll need to ensure that any methods that rely on them are under a feature flag. |
Thanks, ingest-and-store works for me. I'll write this up as a tracking issue so the design doesn't clutter this PR, and tag you there. Scoping it to the epoch schedule specifically, since that's what the three methods need and there's no leader-schedule method in the repo yet. |
Closes #28.
Implements the standard Solana
getEpochInfomethod. It's commonly used by wallets/explorers to show epoch progress, and most of the data is already available in the codebase.Approach
The handler resolves a single ClickHouse context slot and derives every field from that one slot, so the response is a consistent snapshot:
absoluteSlot,blockHeight,transactionCountreuse the existing resolution paths (latest_slot_cache,latest_block_height_cache.get_or_refresh,clickhouse.get_transaction_count_by_slot).epoch,slotIndex,slotsInEpochare computed fromDEFAULT_SLOTS_PER_EPOCH, assuming the default schedule with no warmup — the same assumption the existinggetInflationRewardcode makes.It accepts the same optional config object as
getSlot(commitment+minContextSlot), rejectsprocessedunless the head cache is enabled, applies theminContextSlotcheck, and forwards ClickHouse query timings via the downstream header like the sibling handlers.Response shape (no
contextwrapper):{ "absoluteSlot": 0, "blockHeight": 0, "epoch": 0, "slotIndex": 0, "slotsInEpoch": 432000, "transactionCount": 0 }Changes
handlers/blocks.rs: newhandle_get_epoch_info.handlers/types.rs:EpochInforesponse struct (camelCase, nullabletransactionCount).handlers/mod.rs: dispatch arm + metrics label.tests/mod.rs: param-validation / routing / processed-commitment rejection tests + a serialization test for the wire format.crates/superbank-rpc/README.md: added to the supported-methods and config-object lists.Testing
cargo fmt --all -- --check— clean.cargo clippy -p superbank-rpc --all-targets --locked -- -D warnings— clean (default features).cargo test -p superbank-rpc --locked— passes (292 tests; 5 new).cargo check/tests under--features grpc-head-cache— pass.Note: the crate's test harness only has an in-memory hook for the latest-slot query, so the added tests cover param validation, routing, commitment handling, and the serialized wire shape rather than an end-to-end value (which needs a live ClickHouse). Happy to extend if there's a preferred pattern for mocking the block-height/transaction-count queries.
Per the issue, this assumes the default epoch schedule (no warmup), consistent with the existing code.