diff --git a/Cargo.lock b/Cargo.lock index 03861374c0..54df55803f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5246,6 +5246,7 @@ dependencies = [ "proptest", "serde", "tendermint 0.40.1", + "tonic", "tracing", ] diff --git a/crates/core/component/funding/Cargo.toml b/crates/core/component/funding/Cargo.toml index 6f4897d744..b4ffbc35e3 100644 --- a/crates/core/component/funding/Cargo.toml +++ b/crates/core/component/funding/Cargo.toml @@ -20,7 +20,8 @@ component = [ "penumbra-sdk-shielded-pool/component", "penumbra-sdk-stake/component", "metrics", - "futures" + "futures", + "tonic", ] default = ["component"] parallel = [ @@ -62,6 +63,7 @@ penumbra-sdk-txhash = {workspace = true, default-features = false} serde = {workspace = true, features = ["derive"]} tendermint = {workspace = true} tracing = {workspace = true} +tonic = {workspace = true, optional = true} [dev-dependencies] proptest = {workspace = true} diff --git a/crates/core/component/funding/src/component.rs b/crates/core/component/funding/src/component.rs index f4378e11c0..443880abd6 100644 --- a/crates/core/component/funding/src/component.rs +++ b/crates/core/component/funding/src/component.rs @@ -1,4 +1,5 @@ pub mod metrics; +pub mod rpc; mod state_key; pub mod view; use ::metrics::{gauge, histogram}; diff --git a/crates/core/component/funding/src/component/rpc.rs b/crates/core/component/funding/src/component/rpc.rs new file mode 100644 index 0000000000..a722cf5e05 --- /dev/null +++ b/crates/core/component/funding/src/component/rpc.rs @@ -0,0 +1,55 @@ +use cnidarium::Storage; +use penumbra_sdk_proto::core::component::funding::v1::{ + self as pb, funding_service_server::FundingService, +}; +use penumbra_sdk_sct::Nullifier; + +use super::liquidity_tournament::nullifier::NullifierRead; + +pub struct Server { + storage: Storage, +} + +impl Server { + pub fn new(storage: Storage) -> Self { + Self { storage } + } +} + +#[tonic::async_trait] +impl FundingService for Server { + async fn lqt_current_epoch_voted( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + // Retrieve latest state snapshot. + let state = self.storage.latest_snapshot(); + + let req_nullifier = request.into_inner(); + let nullifier_proto = req_nullifier + .nullifier + .ok_or_else(|| tonic::Status::invalid_argument("missing nullifier"))?; + + // Proto to domain type conversion. + let nullifier: Nullifier = nullifier_proto + .try_into() + .map_err(|e| tonic::Status::invalid_argument(format!("invalid nullifier: {e}")))?; + + // Perform a state nullifier lookup. + let maybe_tx_id = state.get_lqt_spent_nullifier(nullifier).await; + + if let Some(tx_id) = maybe_tx_id { + // Nullifier was spent and user has already voted. + Ok(tonic::Response::new(pb::LqtCurrentEpochVotedResponse { + tx_id: Some(tx_id.into()), + already_voted: true, + })) + } else { + // Nullifier was not spent and user has not voted yet. + Ok(tonic::Response::new(pb::LqtCurrentEpochVotedResponse { + tx_id: None, + already_voted: false, + })) + } + } +}