diff --git a/Cargo.lock b/Cargo.lock index 329f698219..d0a0f70c9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4772,6 +4772,7 @@ dependencies = [ "im", "metrics", "once_cell", + "pbjson-types", "penumbra-asset", "penumbra-dex", "penumbra-keys", diff --git a/crates/core/component/auction/Cargo.toml b/crates/core/component/auction/Cargo.toml index 165e1eb3da..8c5bf8eb87 100644 --- a/crates/core/component/auction/Cargo.toml +++ b/crates/core/component/auction/Cargo.toml @@ -77,6 +77,7 @@ tendermint = {workspace = true, default-features = true} tokio = {workspace = true, features = ["full", "tracing"], optional = true} tonic = {workspace = true, optional = true} tracing = {workspace = true} +pbjson-types = "0.6.0" [dev-dependencies] ed25519-consensus = {workspace = true} diff --git a/crates/core/component/auction/src/component/auction_store.rs b/crates/core/component/auction/src/component/auction_store.rs index 3c730d5c42..a0722d3d1d 100644 --- a/crates/core/component/auction/src/component/auction_store.rs +++ b/crates/core/component/auction/src/component/auction_store.rs @@ -1,11 +1,11 @@ use anyhow::Result; use async_trait::async_trait; use cnidarium::StateRead; +use pbjson_types::Any; use penumbra_proto::core::component::auction::v1alpha1 as pb; use penumbra_proto::DomainType; use penumbra_proto::Name; use penumbra_proto::StateReadProto; -use prost_types::Any; use crate::{ auction::{dutch::DutchAuction, id::AuctionId}, diff --git a/crates/core/component/auction/src/component/rpc.rs b/crates/core/component/auction/src/component/rpc.rs old mode 100644 new mode 100755 index 6b83abcca8..57c8a233cc --- a/crates/core/component/auction/src/component/rpc.rs +++ b/crates/core/component/auction/src/component/rpc.rs @@ -1,16 +1,23 @@ #![allow(unused)] // TODO: remove this when filling in the RPCs use penumbra_proto::{ - core::component::auction::v1alpha1::query_service_server::QueryService, DomainType, + core::component::auction::v1alpha1::{ + query_service_server::QueryService, AuctionStateByIdRequest, AuctionStateByIdResponse, + AuctionStateByIdsRequest, AuctionStateByIdsResponse, DutchAuctionState, + }, + DomainType, }; use async_stream::try_stream; -use cnidarium::Storage; use futures::{StreamExt, TryStreamExt}; +use penumbra_proto::Message; use std::pin::Pin; use tonic::Status; use tracing::instrument; +use super::AuctionStoreRead; +use cnidarium::Storage; + pub struct Server { storage: Storage, } @@ -22,4 +29,41 @@ impl Server { } #[tonic::async_trait] -impl QueryService for Server {} +impl QueryService for Server { + #[instrument(skip(self, request))] + async fn auction_state_by_id( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + let request = request.into_inner(); + + let id = request + .id + .ok_or_else(|| Status::invalid_argument("missing auction id"))? + .try_into() + .map_err(|_| Status::invalid_argument("invalid auction id"))?; + + let auction_data = state + .get_raw_auction(id) + .await + .ok_or_else(|| tonic::Status::not_found("auction data not found for specified id"))?; + + Ok(tonic::Response::new(AuctionStateByIdResponse { + auction: Some(auction_data), + positions: Vec::new(), + })) + } + + type AuctionStateByIdsStream = Pin< + Box> + Send>, + >; + + #[instrument(skip(self, request))] + async fn auction_state_by_ids( + &self, + request: tonic::Request, + ) -> Result, Status> { + todo!() + } +} diff --git a/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.rs b/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.rs index 07b4f355ab..9cb11332f5 100644 --- a/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.rs +++ b/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.rs @@ -28,6 +28,82 @@ impl ::prost::Name for GenesisContent { ) } } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuctionStateByIdRequest { + #[prost(message, optional, tag = "1")] + pub id: ::core::option::Option, +} +impl ::prost::Name for AuctionStateByIdRequest { + const NAME: &'static str = "AuctionStateByIdRequest"; + const PACKAGE: &'static str = "penumbra.core.component.auction.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.core.component.auction.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuctionStateByIdResponse { + /// If present, the state of the auction. If not present, no such auction is known. + #[prost(message, optional, tag = "2")] + pub auction: ::core::option::Option<::pbjson_types::Any>, + /// The state of any DEX positions relevant to the returned auction. + /// + /// Could be empty, depending on the auction state. + #[prost(message, repeated, tag = "3")] + pub positions: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for AuctionStateByIdResponse { + const NAME: &'static str = "AuctionStateByIdResponse"; + const PACKAGE: &'static str = "penumbra.core.component.auction.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.core.component.auction.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuctionStateByIdsRequest { + /// The auction IDs to request. Only known IDs will be returned in the response. + #[prost(message, repeated, tag = "1")] + pub id: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for AuctionStateByIdsRequest { + const NAME: &'static str = "AuctionStateByIdsRequest"; + const PACKAGE: &'static str = "penumbra.core.component.auction.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.core.component.auction.v1alpha1.{}", Self::NAME + ) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuctionStateByIdsResponse { + /// The auction ID of the returned auction. + #[prost(message, optional, tag = "1")] + pub id: ::core::option::Option, + /// The state of the returned auction. + #[prost(message, optional, tag = "2")] + pub auction: ::core::option::Option, + /// The state of any DEX positions relevant to the returned auction. + /// + /// Could be empty, depending on the auction state. + #[prost(message, repeated, tag = "3")] + pub positions: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for AuctionStateByIdsResponse { + const NAME: &'static str = "AuctionStateByIdsResponse"; + const PACKAGE: &'static str = "penumbra.core.component.auction.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.core.component.auction.v1alpha1.{}", Self::NAME + ) + } +} /// A unique identifier for an auction, obtained from hashing a domain separator /// along with the immutable part of an auction description. #[allow(clippy::derive_partial_eq_without_eq)] @@ -303,7 +379,7 @@ pub mod query_service_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; use tonic::codegen::http::Uri; - /// Query operations for the Auction component. + /// Query operations for the auction component. #[derive(Debug, Clone)] pub struct QueryServiceClient { inner: tonic::client::Grpc, @@ -384,6 +460,68 @@ pub mod query_service_client { self.inner = self.inner.max_encoding_message_size(limit); self } + /// Get the current state of an auction by ID. + pub async fn auction_state_by_id( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.auction.v1alpha1.QueryService/AuctionStateById", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.auction.v1alpha1.QueryService", + "AuctionStateById", + ), + ); + self.inner.unary(req, path, codec).await + } + /// Get the current state of a group of auctions by ID. + pub async fn auction_state_by_ids( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/penumbra.core.component.auction.v1alpha1.QueryService/AuctionStateByIds", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.core.component.auction.v1alpha1.QueryService", + "AuctionStateByIds", + ), + ); + self.inner.server_streaming(req, path, codec).await + } } } /// Generated server implementations. @@ -393,8 +531,34 @@ pub mod query_service_server { use tonic::codegen::*; /// Generated trait containing gRPC methods that should be implemented for use with QueryServiceServer. #[async_trait] - pub trait QueryService: Send + Sync + 'static {} - /// Query operations for the Auction component. + pub trait QueryService: Send + Sync + 'static { + /// Get the current state of an auction by ID. + async fn auction_state_by_id( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the AuctionStateByIds method. + type AuctionStateByIdsStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::AuctionStateByIdsResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// Get the current state of a group of auctions by ID. + async fn auction_state_by_ids( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Query operations for the auction component. #[derive(Debug)] pub struct QueryServiceServer { inner: _Inner, @@ -474,6 +638,102 @@ pub mod query_service_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { + "/penumbra.core.component.auction.v1alpha1.QueryService/AuctionStateById" => { + #[allow(non_camel_case_types)] + struct AuctionStateByIdSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService + for AuctionStateByIdSvc { + type Response = super::AuctionStateByIdResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::auction_state_by_id(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AuctionStateByIdSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/penumbra.core.component.auction.v1alpha1.QueryService/AuctionStateByIds" => { + #[allow(non_camel_case_types)] + struct AuctionStateByIdsSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::ServerStreamingService< + super::AuctionStateByIdsRequest, + > for AuctionStateByIdsSvc { + type Response = super::AuctionStateByIdsResponse; + type ResponseStream = T::AuctionStateByIdsStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::auction_state_by_ids(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AuctionStateByIdsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { Ok( diff --git a/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.serde.rs b/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.serde.rs index ee693da763..7f8cd6a68f 100644 --- a/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.auction.v1alpha1.serde.rs @@ -1021,6 +1021,437 @@ impl<'de> serde::Deserialize<'de> for AuctionParameters { deserializer.deserialize_struct("penumbra.core.component.auction.v1alpha1.AuctionParameters", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for AuctionStateByIdRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.id.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdRequest", len)?; + if let Some(v) = self.id.as_ref() { + struct_ser.serialize_field("id", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AuctionStateByIdRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "id", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Id, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "id" => Ok(GeneratedField::Id), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AuctionStateByIdRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.auction.v1alpha1.AuctionStateByIdRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(AuctionStateByIdRequest { + id: id__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for AuctionStateByIdResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.auction.is_some() { + len += 1; + } + if !self.positions.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdResponse", len)?; + if let Some(v) = self.auction.as_ref() { + struct_ser.serialize_field("auction", v)?; + } + if !self.positions.is_empty() { + struct_ser.serialize_field("positions", &self.positions)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AuctionStateByIdResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "auction", + "positions", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Auction, + Positions, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "auction" => Ok(GeneratedField::Auction), + "positions" => Ok(GeneratedField::Positions), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AuctionStateByIdResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.auction.v1alpha1.AuctionStateByIdResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut auction__ = None; + let mut positions__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Auction => { + if auction__.is_some() { + return Err(serde::de::Error::duplicate_field("auction")); + } + auction__ = map_.next_value()?; + } + GeneratedField::Positions => { + if positions__.is_some() { + return Err(serde::de::Error::duplicate_field("positions")); + } + positions__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(AuctionStateByIdResponse { + auction: auction__, + positions: positions__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for AuctionStateByIdsRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.id.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdsRequest", len)?; + if !self.id.is_empty() { + struct_ser.serialize_field("id", &self.id)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AuctionStateByIdsRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "id", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Id, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "id" => Ok(GeneratedField::Id), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AuctionStateByIdsRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.auction.v1alpha1.AuctionStateByIdsRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(AuctionStateByIdsRequest { + id: id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdsRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for AuctionStateByIdsResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.id.is_some() { + len += 1; + } + if self.auction.is_some() { + len += 1; + } + if !self.positions.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdsResponse", len)?; + if let Some(v) = self.id.as_ref() { + struct_ser.serialize_field("id", v)?; + } + if let Some(v) = self.auction.as_ref() { + struct_ser.serialize_field("auction", v)?; + } + if !self.positions.is_empty() { + struct_ser.serialize_field("positions", &self.positions)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AuctionStateByIdsResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "id", + "auction", + "positions", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Id, + Auction, + Positions, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "id" => Ok(GeneratedField::Id), + "auction" => Ok(GeneratedField::Auction), + "positions" => Ok(GeneratedField::Positions), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AuctionStateByIdsResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.auction.v1alpha1.AuctionStateByIdsResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut id__ = None; + let mut auction__ = None; + let mut positions__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = map_.next_value()?; + } + GeneratedField::Auction => { + if auction__.is_some() { + return Err(serde::de::Error::duplicate_field("auction")); + } + auction__ = map_.next_value()?; + } + GeneratedField::Positions => { + if positions__.is_some() { + return Err(serde::de::Error::duplicate_field("positions")); + } + positions__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(AuctionStateByIdsResponse { + id: id__, + auction: auction__, + positions: positions__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.auction.v1alpha1.AuctionStateByIdsResponse", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for DutchAuction { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index f537de9326..8abb21f0fe 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 diff --git a/proto/penumbra/penumbra/core/component/auction/v1alpha1/auction.proto b/proto/penumbra/penumbra/core/component/auction/v1alpha1/auction.proto old mode 100644 new mode 100755 index ee6507f3ef..3e900af140 --- a/proto/penumbra/penumbra/core/component/auction/v1alpha1/auction.proto +++ b/proto/penumbra/penumbra/core/component/auction/v1alpha1/auction.proto @@ -4,6 +4,7 @@ package penumbra.core.component.auction.v1alpha1; import "penumbra/core/asset/v1/asset.proto"; import "penumbra/core/component/dex/v1/dex.proto"; import "penumbra/core/num/v1/num.proto"; +import "google/protobuf/any.proto"; // The configuration parameters for the auction component. message AuctionParameters {} @@ -14,8 +15,42 @@ message GenesisContent { AuctionParameters params = 1; } -// Query operations for the Auction component. -service QueryService {} +// Query operations for the auction component. +service QueryService { + // Get the current state of an auction by ID. + rpc AuctionStateById(AuctionStateByIdRequest) returns (AuctionStateByIdResponse); + // Get the current state of a group of auctions by ID. + rpc AuctionStateByIds(AuctionStateByIdsRequest) returns (stream AuctionStateByIdsResponse); +} + +message AuctionStateByIdRequest { + AuctionId id = 1; +} + +message AuctionStateByIdResponse { + // If present, the state of the auction. If not present, no such auction is known. + google.protobuf.Any auction = 2; + // The state of any DEX positions relevant to the returned auction. + // + // Could be empty, depending on the auction state. + repeated core.component.dex.v1.Position positions = 3; +} + +message AuctionStateByIdsRequest { + // The auction IDs to request. Only known IDs will be returned in the response. + repeated AuctionId id = 1; +} + +message AuctionStateByIdsResponse { + // The auction ID of the returned auction. + AuctionId id = 1; + // The state of the returned auction. + DutchAuctionState auction = 2; + // The state of any DEX positions relevant to the returned auction. + // + // Could be empty, depending on the auction state. + repeated core.component.dex.v1.Position positions = 3; +} // A unique identifier for an auction, obtained from hashing a domain separator // along with the immutable part of an auction description.