-
Notifications
You must be signed in to change notification settings - Fork 174
feat(solana-indexer): add types module scaffolding
#4506
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
Open
tilacog
wants to merge
3
commits into
main
Choose a base branch
from
solana-indexer/PR2-bootstrap
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| //! `solana-indexer` — Solana settlement indexer. | ||
|
|
||
| pub mod types; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| //! Message types passed over the internal channels. | ||
| //! | ||
| //! The ingester pushes [`StreamUpdate`] into the channel to the decoder; the | ||
| //! decoder pushes [`PartialEvent`] / [`PartialHalf`] to the partial-event | ||
| //! watchdog. | ||
|
|
||
| use crate::types::{ | ||
| Signature, | ||
| wire::{SubscribeUpdateAccountInfo, SubscribeUpdateTransactionInfo}, | ||
| }; | ||
|
|
||
| /// From `Ingester` → `Decoder`. | ||
| /// | ||
| /// One multiplexed wire message, tagged with the slot the message was observed | ||
| /// at. The org file names the channel payload "Event"; the spec defines that | ||
| /// type as `StreamUpdate`, and that is what this crate uses. | ||
| #[derive(Debug, Clone)] | ||
| pub enum StreamUpdate { | ||
| /// A transaction-update slot message. | ||
| Tx { | ||
| /// Slot the message was observed at. | ||
| slot: u64, | ||
| /// Transaction signature. | ||
| signature: Signature, | ||
| /// Wire message body. | ||
| inner: Box<SubscribeUpdateTransactionInfo>, | ||
| }, | ||
| /// An account-update slot message. | ||
| Account { | ||
| /// Slot the message was observed at. | ||
| slot: u64, | ||
| /// Optional signature linking the write back to its originating | ||
| /// transaction. | ||
| txn_signature: Option<Signature>, | ||
| /// Wire message body. | ||
| inner: Box<SubscribeUpdateAccountInfo>, | ||
| }, | ||
| } | ||
|
|
||
| /// From `Decoder` → `PartialEventWatchdog`. | ||
| /// | ||
| /// The watchdog holds incomplete `(slot, signature)` pairs until both halves | ||
| /// arrive; each delivery carries the half that just landed. | ||
| #[derive(Debug, Clone, Copy)] | ||
| pub struct PartialEvent { | ||
| /// Slot the partial was observed at. | ||
| pub slot: u64, | ||
| /// Transaction signature the partial corresponds to. | ||
| pub signature: Signature, | ||
| } | ||
|
|
||
| /// One of the two halves a [`StreamUpdate`] can produce. | ||
| /// | ||
| /// The decoder pushes one `PartialEvent` per `StreamUpdate` it processes; the | ||
| /// watchdog uses the `(slot, signature)` key to match pairs. | ||
| #[derive(Debug, Clone)] | ||
| pub enum PartialHalf { | ||
| /// Transaction-update half. | ||
| Tx(Box<SubscribeUpdateTransactionInfo>), | ||
| /// Account-update half. | ||
| Account(Box<SubscribeUpdateAccountInfo>), | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| //! Commitment-tracking types: confirmation state, signature status, and the row | ||
| //! shapes consumed by the finalization worker. | ||
|
|
||
| use {crate::types::Signature, solana_sdk::pubkey::Pubkey}; | ||
|
|
||
| /// On-chain commitment of a transaction or row. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub enum Commitment { | ||
| /// The row is at `confirmed` commitment; the finalization worker still has | ||
| /// work to do. | ||
| Confirmed, | ||
| /// The row is at `finalized` commitment. | ||
| Finalized, | ||
| /// The row's transaction never landed (or was rolled back). | ||
| RolledBack, | ||
| } | ||
|
|
||
| impl Commitment { | ||
| /// String label used in `solana.*` `commitment` columns. | ||
| pub fn as_str(self) -> &'static str { | ||
| match self { | ||
| Self::Confirmed => "confirmed", | ||
| Self::Finalized => "finalized", | ||
| Self::RolledBack => "rolled_back", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Result of an RPC `getSignatureStatuses` poll. | ||
| #[derive(Debug, Clone, Copy)] | ||
| pub struct SignatureStatus { | ||
| /// Slot the transaction landed at, if known. | ||
| pub slot: u64, | ||
| /// Confirmation status reported by the RPC. | ||
| pub confirmation_status: Commitment, | ||
| } | ||
|
|
||
| /// Snapshot of an account at a given slot (from `getAccountInfo`). | ||
| #[derive(Debug, Clone)] | ||
| pub struct AccountInfo { | ||
| /// Slot the snapshot was read at. | ||
| pub slot: u64, | ||
| /// Account data (serialized). | ||
| pub data: Vec<u8>, | ||
| /// Account owner program. | ||
| pub owner: Pubkey, | ||
| } | ||
|
|
||
| /// A `solana.*` row that has not yet reached `finalized` commitment — the kind | ||
| /// picked up by the aged-row sweep, where `commitment = 'confirmed'` and the | ||
| /// row's slot is at least one finalization window behind `LATEST_CHAIN_SLOT`. | ||
| #[derive(Debug, Clone)] | ||
| pub struct UnfinalizedRow { | ||
| /// Table the row lives in. | ||
| pub table: &'static str, | ||
| /// Transaction signature. | ||
| pub signature: Signature, | ||
| /// Slot the row was inserted at. | ||
| pub slot: u64, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| //! Dead-letter types: events that failed to persist and were diverted to | ||
| //! `solana.dead_letter` for operator follow-up. | ||
|
|
||
| use crate::types::Signature; | ||
|
|
||
| /// A decoded event whose write to `solana.*` failed and was diverted to | ||
| /// `solana.dead_letter`. | ||
| #[derive(Debug, Clone)] | ||
| pub struct DeadLetterEntry { | ||
| /// Slot the event was observed at. | ||
| pub slot: u64, | ||
| /// Transaction signature, if the failure was per-transaction. | ||
| pub signature: Option<Signature>, | ||
| /// Why the event landed in the dead-letter table. | ||
| pub reason: DeadLetterReason, | ||
| /// Original raw bytes for replay. | ||
| pub raw_bytes: Vec<u8>, | ||
| } | ||
|
|
||
| /// Why a row landed in the dead-letter table. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub enum DeadLetterReason { | ||
| /// Decoder received both halves but couldn't parse them. | ||
| DecoderError, | ||
| /// Watchdog gave up: account-update half never arrived. | ||
| AccountUpdateMissing, | ||
| /// Watchdog gave up: transaction-update half never arrived. | ||
| TxUpdateMissing, | ||
| /// Settlement landed but no `proposed_solutions` row matched. | ||
| SolutionUidUnmatchable, | ||
| } | ||
|
|
||
| impl DeadLetterReason { | ||
| /// String label used in `solana.dead_letter.reason`. | ||
| pub fn as_str(self) -> &'static str { | ||
| match self { | ||
| Self::DecoderError => "decoder_error", | ||
| Self::AccountUpdateMissing => "account_update_missing", | ||
| Self::TxUpdateMissing => "tx_update_missing", | ||
| Self::SolutionUidUnmatchable => "solution_uid_unmatchable", | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| //! Error types used across the indexer's domain. | ||
|
|
||
| use thiserror::Error; | ||
|
|
||
| /// Failures surfaced from the decoder. | ||
| #[derive(Debug, Error, PartialEq, Eq)] | ||
| pub enum DecodeError { | ||
| /// The discriminator byte(s) at the start of the instruction data did not | ||
| /// match any known instruction on either program. | ||
| #[error("unknown instruction discriminator")] | ||
| UnknownDiscriminator, | ||
| /// The ALT (Address Lookup Table) loaded-address list could not be resolved | ||
| /// against the full account list. | ||
| #[error("alt resolution failed")] | ||
| AltResolutionFailed, | ||
| /// The instruction was recognised but its schema did not match the on-chain | ||
| /// layout. | ||
| #[error("schema mismatch")] | ||
| SchemaMismatch, | ||
| } | ||
|
|
||
| /// Failures surfaced from the persistence boundary. | ||
| #[derive(Debug, Error, PartialEq, Eq)] | ||
| pub enum StoreError { | ||
| /// The SQL `ON CONFLICT` clause rejected the write (e.g. watermark | ||
| /// regression). | ||
| #[error("store conflict")] | ||
| Conflict, | ||
| /// The store is temporarily unavailable (e.g. connection lost, pool | ||
| /// exhausted). The caller is expected to retry. | ||
| #[error("store unavailable")] | ||
| Unavailable, | ||
| } | ||
|
|
||
| /// Failures surfaced from the stream boundary. | ||
| #[derive(Debug, Error)] | ||
| pub enum StreamError { | ||
| /// The stream has been disconnected by the server. | ||
| #[error("stream disconnected")] | ||
| Disconnected, | ||
| /// The internal mpsc send timed out (backpressure on the decoder). | ||
| #[error("stream send timeout")] | ||
| SendTimeout, | ||
| /// The resume slot is outside the provider's replay window. The caller | ||
| /// should reset `from_slot` to `LATEST_CHAIN_SLOT − replay_window`, | ||
| /// record the lost range, and retry the subscription. | ||
| #[error("replay window exceeded")] | ||
| ReplayWindowExceeded { | ||
| /// The slot the subscriber attempted to resume from. | ||
| attempted_slot: u64, | ||
| /// The earliest slot the provider can still serve. | ||
| earliest_replayable_slot: u64, | ||
| }, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| //! Domain event taxonomy. | ||
| //! | ||
| //! The settlement program and SolFlow each have their own enum | ||
| //! (`SettlementEvent`, `SolFlowEvent`); the decoder's handoff to the | ||
| //! persistence step is the sum [`DecodedEvent`]. Per-order accounting | ||
| //! is reconstructed from [`TradeDelta`] snapshots. | ||
|
|
||
| use {crate::types::Signature, solana_sdk::pubkey::Pubkey}; | ||
|
|
||
| /// Change in a single order's `amount_withdrawn` and `amount_received` | ||
| /// between two consecutive account snapshots. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| pub struct TradeDelta { | ||
| /// Order UID this delta applies to. | ||
| pub order_uid: [u8; 32], | ||
| /// Change in `amount_withdrawn` since the previous snapshot. | ||
| pub amount_withdrawn_delta: u64, | ||
| /// Change in `amount_received` since the previous snapshot. | ||
| pub amount_received_delta: u64, | ||
| /// `true` when post-trade `amount_withdrawn` equals the order's | ||
| /// full sell amount, or `amount_received` equals the full buy | ||
| /// amount. | ||
| pub order_fulfilled: bool, | ||
| } | ||
|
|
||
| /// Settlement-program events decoded from on-chain instructions. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum SettlementEvent { | ||
| /// A new order was created on-chain. | ||
| OrderCreated { | ||
| /// Intent hash of the order. | ||
| intent_hash: [u8; 32], | ||
| /// Owner of the order. | ||
| owner: Pubkey, | ||
| /// Address that created the order (relayer / solver). | ||
| created_by: Pubkey, | ||
| }, | ||
| /// An order was closed. | ||
| OrderClosed { | ||
| /// Intent hash of the order. | ||
| intent_hash: [u8; 32], | ||
| }, | ||
| /// An order was cancelled. | ||
| OrderCancelled { | ||
| /// Intent hash of the order. | ||
| intent_hash: [u8; 32], | ||
| }, | ||
| /// A settlement was finalized on-chain. | ||
| SettlementFinalized { | ||
| /// Auction id this settlement belongs to. | ||
| auction_id: i64, | ||
| /// Solver that won the auction. | ||
| solver: Pubkey, | ||
| /// Transaction signature. | ||
| tx_signature: Signature, | ||
| /// Slot the settlement was observed at. | ||
| slot: u64, | ||
| /// Per-order accounting deltas. | ||
| trades: Vec<TradeDelta>, | ||
| }, | ||
| /// A new buffer PDA was created. | ||
| BufferCreated { | ||
| /// Token the buffer is denominated in. | ||
| token: Pubkey, | ||
| }, | ||
| /// A buffer PDA was used by a transaction. | ||
| BufferUsed { | ||
| /// Token the buffer is denominated in. | ||
| token: Pubkey, | ||
| /// Transaction signature that consumed the buffer. | ||
| tx_signature: Signature, | ||
| }, | ||
| /// A manager was updated (e.g. ownership rotation). | ||
| ManagerUpdated { | ||
| /// Previous manager. | ||
| from: Pubkey, | ||
| /// New manager. | ||
| to: Pubkey, | ||
| }, | ||
| /// A solver was added to the allow-list. | ||
| SolverAdded { | ||
| /// Solver that was added. | ||
| solver: Pubkey, | ||
| }, | ||
| /// A solver was removed from the allow-list. | ||
| SolverRemoved { | ||
| /// Solver that was removed. | ||
| solver: Pubkey, | ||
| }, | ||
| /// Generic solver interaction (instruction observed but not decoded into | ||
| /// one of the structured events above). | ||
| SolverInteraction { | ||
| /// Transaction signature. | ||
| tx_signature: Signature, | ||
| /// Index of the instruction within the transaction. | ||
| ix_index: u8, | ||
| }, | ||
| } | ||
|
|
||
| /// SolFlow-side events, populates the `solana.sol_flow` table. | ||
| /// | ||
| /// Note: the paired `solana.orders` row for `OrderEnabled` is written by the | ||
| /// settlement-program decode path, not here. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum SolFlowEvent { | ||
| /// A new order was created on SolFlow. | ||
| OrderCreated { | ||
| /// Custodial PDA that holds the wSOL for this order. | ||
| custodial_pda: Pubkey, | ||
| /// Real owner of the order. | ||
| real_owner: Pubkey, | ||
| /// Order UID. | ||
| order_uid: [u8; 32], | ||
| /// From `meta.post_token_balances` on the custodial wSOL | ||
| /// account. | ||
| sol_amount: u64, | ||
| }, | ||
| /// An order was enabled (custody transferred to settlement program). | ||
| OrderEnabled { | ||
| /// Custodial PDA. | ||
| custodial_pda: Pubkey, | ||
| /// Address that enabled the order. | ||
| enabler: Pubkey, | ||
| /// Order UID. | ||
| order_uid: [u8; 32], | ||
| }, | ||
| /// An order was recovered (e.g. after a stuck-state cleanup). | ||
| OrderRecovered { | ||
| /// Custodial PDA. | ||
| custodial_pda: Pubkey, | ||
| /// Slot the recovery was observed at. | ||
| slot: u64, | ||
| }, | ||
| } | ||
|
|
||
| /// Sum of the two program-side event enums for the persistence step. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum DecodedEvent { | ||
| /// A settlement-program event. | ||
| Settlement(SettlementEvent), | ||
| /// A SolFlow event. | ||
| SolFlow(SolFlowEvent), | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
The doc comment for
PartialEventstates that "each delivery carries the half that just landed," but the struct definition is missing thehalf: PartialHalffield. Without this field, the watchdog cannot receive or hold the actual data halves to match and reconstruct the full event. Additionally, sincePartialHalfcontains heap-allocatedBoxtypes,PartialEventcannot deriveCopyand should only deriveDebug, Clone.