diff --git a/Cargo.lock b/Cargo.lock index 91dd894a17..461d47bae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6110,6 +6110,7 @@ dependencies = [ "penumbra-sdk-sct", "penumbra-sdk-shielded-pool", "penumbra-sdk-stake", + "penumbra-sdk-transaction", "predicates 2.1.5", "prost 0.13.4", "prost-reflect", diff --git a/crates/bin/pindexer/Cargo.toml b/crates/bin/pindexer/Cargo.toml index c8d5af4f00..65f417a369 100644 --- a/crates/bin/pindexer/Cargo.toml +++ b/crates/bin/pindexer/Cargo.toml @@ -38,6 +38,7 @@ penumbra-sdk-num = {workspace = true, default-features = false} penumbra-sdk-asset = {workspace = true, default-features = false} penumbra-sdk-proto = {workspace = true, default-features = false} penumbra-sdk-sct = {workspace = true, default-features = false} +penumbra-sdk-transaction = {workspace = true, default-features = false} prost = {workspace = true} tracing = {workspace = true} tokio = {workspace = true, features = ["full"]} diff --git a/crates/bin/pindexer/src/dex_ex/mod.rs b/crates/bin/pindexer/src/dex_ex/mod.rs index 2f91d1f662..de0215eb23 100644 --- a/crates/bin/pindexer/src/dex_ex/mod.rs +++ b/crates/bin/pindexer/src/dex_ex/mod.rs @@ -21,6 +21,7 @@ use penumbra_sdk_num::Amount; use penumbra_sdk_proto::event::EventDomainType; use penumbra_sdk_proto::DomainType; use penumbra_sdk_sct::event::EventBlockRoot; +use penumbra_sdk_transaction::Transaction; use sqlx::types::BigDecimal; use sqlx::{prelude::Type, Row}; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -1397,6 +1398,47 @@ impl Component { Ok(()) } + + async fn record_transaction( + &self, + dbtx: &mut PgTransaction<'_>, + time: DateTime, + height: u64, + transaction_id: [u8; 32], + transaction: Transaction, + ) -> anyhow::Result<()> { + sqlx::query( + "INSERT INTO dex_ex_transactions ( + transaction_id, + transaction, + height, + time + ) VALUES ($1, $2, $3, $4)", + ) + .bind(transaction_id) + .bind(transaction.encode_to_vec()) + .bind(i32::try_from(height)?) + .bind(time) + .execute(dbtx.as_mut()) + .await?; + + Ok(()) + } + + async fn record_all_transactions( + &self, + dbtx: &mut PgTransaction<'_>, + time: DateTime, + block: &BlockEvents, + ) -> anyhow::Result<()> { + for (tx_id, tx_bytes) in block.transactions() { + let tx = Transaction::try_from(tx_bytes)?; + let height = block.height(); + self.record_transaction(dbtx, time, height, tx_id, tx) + .await?; + } + Ok(()) + } } #[async_trait] @@ -1432,6 +1474,8 @@ impl AppView for Component { .expect(&format!("no block root event at height {}", block.height)); last_time = Some(time); + self.record_all_transactions(dbtx, time, block).await?; + // Load any missing positions before processing events events.load_positions(dbtx).await?; diff --git a/crates/bin/pindexer/src/dex_ex/schema.sql b/crates/bin/pindexer/src/dex_ex/schema.sql index 8a5615a912..48d00957eb 100644 --- a/crates/bin/pindexer/src/dex_ex/schema.sql +++ b/crates/bin/pindexer/src/dex_ex/schema.sql @@ -251,6 +251,19 @@ CREATE TABLE IF NOT EXISTS dex_ex_block_summary ( CREATE INDEX ON dex_ex_block_summary (time, height); +CREATE TABLE IF NOT EXISTS dex_ex_transactions ( + -- The unique identifier of the transaction + transaction_id BYTEA NOT NULL PRIMARY KEY, + -- The raw transaction bytes + transaction BYTEA NOT NULL, + -- The block height at which this transaction was included + height INTEGER NOT NULL, + -- The timestamp when this transaction was included in a block + time TIMESTAMPTZ NOT NULL +); + +CREATE INDEX ON dex_ex_transactions (time, height); + ALTER TABLE dex_ex_position_executions ADD CONSTRAINT fk_position_executions FOREIGN KEY (position_id) REFERENCES dex_ex_position_state(position_id); diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index f613403ab9..6dc78d95fc 100644 Binary files a/crates/proto/src/gen/proto_descriptor.bin.no_lfs and b/crates/proto/src/gen/proto_descriptor.bin.no_lfs differ