From 3257d11c99457f839c3548b4298ce8e4a3cb62c2 Mon Sep 17 00:00:00 2001 From: Chris Czub Date: Tue, 14 May 2024 10:25:46 -0700 Subject: [PATCH] Split NV query RPC apart from JMT query RPC --- crates/bin/pcli/src/command/query.rs | 64 ++- .../src/gen/penumbra.cnidarium.v1.rs | 204 ++++++-- .../src/gen/penumbra.cnidarium.v1.serde.rs | 474 ++++++++++++++---- crates/cnidarium/src/rpc.rs | 81 ++- crates/proto/src/gen/penumbra.cnidarium.v1.rs | 204 ++++++-- .../src/gen/penumbra.cnidarium.v1.serde.rs | 474 ++++++++++++++---- .../penumbra/cnidarium/v1/cnidarium.proto | 33 +- 7 files changed, 1184 insertions(+), 350 deletions(-) diff --git a/crates/bin/pcli/src/command/query.rs b/crates/bin/pcli/src/command/query.rs index fec9d1b864..8fe1cfe66f 100644 --- a/crates/bin/pcli/src/command/query.rs +++ b/crates/bin/pcli/src/command/query.rs @@ -11,12 +11,14 @@ mod tx; mod validator; use auction::AuctionCmd; +use base64::prelude::*; use chain::ChainCmd; use colored_json::ToColoredJson; use community_pool::CommunityPoolCmd; use dex::DexCmd; use governance::GovernanceCmd; use ibc_query::IbcCmd; +use penumbra_proto::cnidarium::v1::non_verifiable_key_value_request::Key as NVKey; use shielded_pool::ShieldedPool; use tx::Tx; pub(super) use validator::ValidatorCmd; @@ -179,34 +181,50 @@ impl QueryCmd { let mut client = QueryServiceClient::new(app.pd_channel().await?); // Using an enum in the clap arguments was annoying; this is workable: - let storage_backend = match storage_backend.as_str() { - "jmt" => penumbra_proto::cnidarium::v1::key_value_request::StorageBackend::Jmt as i32, + match storage_backend.as_str() { "nonverifiable" => { - penumbra_proto::cnidarium::v1::key_value_request::StorageBackend::Nonverifiable - as i32 - } - // Default to JMT - _ => penumbra_proto::cnidarium::v1::key_value_request::StorageBackend::Jmt as i32, - }; + let key_bytes = BASE64_STANDARD + .decode(&key) + .map_err(|e| anyhow::anyhow!(format!("invalid base64: {}", e)))?; - let req = penumbra_proto::cnidarium::v1::KeyValueRequest { - key: key.clone(), - storage_backend, - // Command-line queries don't have a reason to include proofs as of now. - proof: false, - ..Default::default() - }; + let req = penumbra_proto::cnidarium::v1::NonVerifiableKeyValueRequest { + key: Some(NVKey { inner: key_bytes }), + ..Default::default() + }; + + tracing::debug!(?req); - tracing::debug!(?req); + let value = client + .non_verifiable_key_value(req) + .await? + .into_inner() + .value + .context(format!("key not found! key={}", key))?; - let value = client - .key_value(req) - .await? - .into_inner() - .value - .context(format!("key not found! key={}", key))?; + self.display_value(&value.value)?; + } + // Default to JMT + "jmt" | _ => { + let req = penumbra_proto::cnidarium::v1::KeyValueRequest { + key: key.clone(), + // Command-line queries don't have a reason to include proofs as of now. + proof: false, + ..Default::default() + }; + + tracing::debug!(?req); + + let value = client + .key_value(req) + .await? + .into_inner() + .value + .context(format!("key not found! key={}", key))?; + + self.display_value(&value.value)?; + } + }; - self.display_value(&value.value)?; Ok(()) } diff --git a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs index afed7a2d59..7a04181967 100644 --- a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs +++ b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs @@ -1,4 +1,69 @@ -/// Performs a key-value query, either by key or by key hash. +/// Performs a key-value query against the nonverifiable storage, +/// using a byte-encoded key. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonVerifiableKeyValueRequest { + #[prost(message, optional, tag = "1")] + pub key: ::core::option::Option, +} +/// Nested message and enum types in `NonVerifiableKeyValueRequest`. +pub mod non_verifiable_key_value_request { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Key { + #[prost(bytes = "vec", tag = "1")] + pub inner: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Key { + const NAME: &'static str = "Key"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.{}", Self::NAME + ) + } + } +} +impl ::prost::Name for NonVerifiableKeyValueRequest { + const NAME: &'static str = "NonVerifiableKeyValueRequest"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("penumbra.cnidarium.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonVerifiableKeyValueResponse { + /// The value corresponding to the specified key, if it was found. + #[prost(message, optional, tag = "1")] + pub value: ::core::option::Option, +} +/// Nested message and enum types in `NonVerifiableKeyValueResponse`. +pub mod non_verifiable_key_value_response { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Value { + #[prost(bytes = "vec", tag = "1")] + pub value: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Value { + const NAME: &'static str = "Value"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.{}", Self::NAME + ) + } + } +} +impl ::prost::Name for NonVerifiableKeyValueResponse { + const NAME: &'static str = "NonVerifiableKeyValueResponse"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("penumbra.cnidarium.v1.{}", Self::NAME) + } +} +/// Performs a key-value query against the JMT, either by key or by key hash. /// /// Proofs are only supported by key. #[allow(clippy::derive_partial_eq_without_eq)] @@ -10,52 +75,6 @@ pub struct KeyValueRequest { /// whether to return a proof #[prost(bool, tag = "3")] pub proof: bool, - /// Which storage to query. - #[prost(enumeration = "key_value_request::StorageBackend", tag = "4")] - pub storage_backend: i32, -} -/// Nested message and enum types in `KeyValueRequest`. -pub mod key_value_request { - /// The storage type to query. - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum StorageBackend { - Unspecified = 0, - Jmt = 1, - Nonverifiable = 2, - } - impl StorageBackend { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - StorageBackend::Unspecified => "STORAGE_BACKEND_UNSPECIFIED", - StorageBackend::Jmt => "STORAGE_BACKEND_JMT", - StorageBackend::Nonverifiable => "STORAGE_BACKEND_NONVERIFIABLE", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "STORAGE_BACKEND_UNSPECIFIED" => Some(Self::Unspecified), - "STORAGE_BACKEND_JMT" => Some(Self::Jmt), - "STORAGE_BACKEND_NONVERIFIABLE" => Some(Self::Nonverifiable), - _ => None, - } - } - } } impl ::prost::Name for KeyValueRequest { const NAME: &'static str = "KeyValueRequest"; @@ -342,6 +361,38 @@ pub mod query_service_client { ); self.inner.unary(req, path, codec).await } + /// General-purpose key-value state query API, that can be used to query + /// arbitrary keys in the non-verifiable storage. + pub async fn non_verifiable_key_value( + &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.cnidarium.v1.QueryService/NonVerifiableKeyValue", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.cnidarium.v1.QueryService", + "NonVerifiableKeyValue", + ), + ); + self.inner.unary(req, path, codec).await + } /// General-purpose prefixed key-value state query API, that can be used to query /// arbitrary prefixes in the JMT storage. pub async fn prefix_value( @@ -416,6 +467,15 @@ pub mod query_service_server { tonic::Response, tonic::Status, >; + /// General-purpose key-value state query API, that can be used to query + /// arbitrary keys in the non-verifiable storage. + async fn non_verifiable_key_value( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// Server streaming response type for the PrefixValue method. type PrefixValueStream: tonic::codegen::tokio_stream::Stream< Item = std::result::Result, @@ -568,6 +628,56 @@ pub mod query_service_server { }; Box::pin(fut) } + "/penumbra.cnidarium.v1.QueryService/NonVerifiableKeyValue" => { + #[allow(non_camel_case_types)] + struct NonVerifiableKeyValueSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService + for NonVerifiableKeyValueSvc { + type Response = super::NonVerifiableKeyValueResponse; + 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 { + ::non_verifiable_key_value( + &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 = NonVerifiableKeyValueSvc(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.cnidarium.v1.QueryService/PrefixValue" => { #[allow(non_camel_case_types)] struct PrefixValueSvc(pub Arc); diff --git a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs index 9d620a2ceb..8751e80e6e 100644 --- a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs +++ b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs @@ -12,9 +12,6 @@ impl serde::Serialize for KeyValueRequest { if self.proof { len += 1; } - if self.storage_backend != 0 { - len += 1; - } let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueRequest", len)?; if !self.key.is_empty() { struct_ser.serialize_field("key", &self.key)?; @@ -22,11 +19,6 @@ impl serde::Serialize for KeyValueRequest { if self.proof { struct_ser.serialize_field("proof", &self.proof)?; } - if self.storage_backend != 0 { - let v = key_value_request::StorageBackend::try_from(self.storage_backend) - .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.storage_backend)))?; - struct_ser.serialize_field("storageBackend", &v)?; - } struct_ser.end() } } @@ -39,15 +31,12 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { const FIELDS: &[&str] = &[ "key", "proof", - "storage_backend", - "storageBackend", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Key, Proof, - StorageBackend, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -72,7 +61,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { match value { "key" => Ok(GeneratedField::Key), "proof" => Ok(GeneratedField::Proof), - "storageBackend" | "storage_backend" => Ok(GeneratedField::StorageBackend), _ => Ok(GeneratedField::__SkipField__), } } @@ -94,7 +82,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { { let mut key__ = None; let mut proof__ = None; - let mut storage_backend__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Key => { @@ -109,12 +96,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { } proof__ = Some(map_.next_value()?); } - GeneratedField::StorageBackend => { - if storage_backend__.is_some() { - return Err(serde::de::Error::duplicate_field("storageBackend")); - } - storage_backend__ = Some(map_.next_value::()? as i32); - } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -123,88 +104,318 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { Ok(KeyValueRequest { key: key__.unwrap_or_default(), proof: proof__.unwrap_or_default(), - storage_backend: storage_backend__.unwrap_or_default(), }) } } deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for key_value_request::StorageBackend { +impl serde::Serialize for KeyValueResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { - let variant = match self { - Self::Unspecified => "STORAGE_BACKEND_UNSPECIFIED", - Self::Jmt => "STORAGE_BACKEND_JMT", - Self::Nonverifiable => "STORAGE_BACKEND_NONVERIFIABLE", - }; - serializer.serialize_str(variant) + use serde::ser::SerializeStruct; + let mut len = 0; + if self.value.is_some() { + len += 1; + } + if self.proof.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse", len)?; + if let Some(v) = self.value.as_ref() { + struct_ser.serialize_field("value", v)?; + } + if let Some(v) = self.proof.as_ref() { + struct_ser.serialize_field("proof", v)?; + } + struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for key_value_request::StorageBackend { +impl<'de> serde::Deserialize<'de> for KeyValueResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "STORAGE_BACKEND_UNSPECIFIED", - "STORAGE_BACKEND_JMT", - "STORAGE_BACKEND_NONVERIFIABLE", + "value", + "proof", ]; - struct GeneratedVisitor; + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + Proof, + __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 { + "value" => Ok(GeneratedField::Value), + "proof" => Ok(GeneratedField::Proof), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = key_value_request::StorageBackend; + type Value = KeyValueResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) + formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse") } - fn visit_i64(self, v: i64) -> std::result::Result - where - E: serde::de::Error, + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, { - i32::try_from(v) - .ok() - .and_then(|x| x.try_into().ok()) - .ok_or_else(|| { - serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self) - }) + let mut value__ = None; + let mut proof__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Value => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("value")); + } + value__ = map_.next_value()?; + } + GeneratedField::Proof => { + if proof__.is_some() { + return Err(serde::de::Error::duplicate_field("proof")); + } + proof__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(KeyValueResponse { + value: value__, + proof: proof__, + }) } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for key_value_response::Value { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.value.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", len)?; + if !self.value.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("value", pbjson::private::base64::encode(&self.value).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for key_value_response::Value { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; - fn visit_u64(self, v: u64) -> std::result::Result + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result where - E: serde::de::Error, + D: serde::Deserializer<'de>, { - i32::try_from(v) - .ok() - .and_then(|x| x.try_into().ok()) - .ok_or_else(|| { - serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self) - }) + 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 { + "value" => Ok(GeneratedField::Value), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = key_value_response::Value; - fn visit_str(self, value: &str) -> std::result::Result + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse.Value") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut value__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Value => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("value")); + } + value__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(key_value_response::Value { + value: value__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for NonVerifiableKeyValueRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.key.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", len)?; + if let Some(v) = self.key.as_ref() { + struct_ser.serialize_field("key", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "key", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Key, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result where - E: serde::de::Error, + D: serde::Deserializer<'de>, { - match value { - "STORAGE_BACKEND_UNSPECIFIED" => Ok(key_value_request::StorageBackend::Unspecified), - "STORAGE_BACKEND_JMT" => Ok(key_value_request::StorageBackend::Jmt), - "STORAGE_BACKEND_NONVERIFIABLE" => Ok(key_value_request::StorageBackend::Nonverifiable), - _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + 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 { + "key" => Ok(GeneratedField::Key), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NonVerifiableKeyValueRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut key__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Key => { + if key__.is_some() { + return Err(serde::de::Error::duplicate_field("key")); + } + key__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } } + Ok(NonVerifiableKeyValueRequest { + key: key__, + }) } } - deserializer.deserialize_any(GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for KeyValueResponse { +impl serde::Serialize for non_verifiable_key_value_request::Key { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -212,23 +423,115 @@ impl serde::Serialize for KeyValueResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if self.value.is_some() { + if !self.inner.is_empty() { len += 1; } - if self.proof.is_some() { + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key", len)?; + if !self.inner.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("inner", pbjson::private::base64::encode(&self.inner).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_request::Key { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "inner", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Inner, + __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 { + "inner" => Ok(GeneratedField::Inner), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = non_verifiable_key_value_request::Key; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut inner__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Inner => { + if inner__.is_some() { + return Err(serde::de::Error::duplicate_field("inner")); + } + inner__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(non_verifiable_key_value_request::Key { + inner: inner__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for NonVerifiableKeyValueResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.value.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse", len)?; + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", len)?; if let Some(v) = self.value.as_ref() { struct_ser.serialize_field("value", v)?; } - if let Some(v) = self.proof.as_ref() { - struct_ser.serialize_field("proof", v)?; - } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for KeyValueResponse { +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -236,13 +539,11 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { { const FIELDS: &[&str] = &[ "value", - "proof", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Value, - Proof, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -266,7 +567,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { { match value { "value" => Ok(GeneratedField::Value), - "proof" => Ok(GeneratedField::Proof), _ => Ok(GeneratedField::__SkipField__), } } @@ -276,18 +576,17 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = KeyValueResponse; + type Value = NonVerifiableKeyValueResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse") + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { let mut value__ = None; - let mut proof__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Value => { @@ -296,27 +595,20 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { } value__ = map_.next_value()?; } - GeneratedField::Proof => { - if proof__.is_some() { - return Err(serde::de::Error::duplicate_field("proof")); - } - proof__ = map_.next_value()?; - } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(KeyValueResponse { + Ok(NonVerifiableKeyValueResponse { value: value__, - proof: proof__, }) } } - deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for key_value_response::Value { +impl serde::Serialize for non_verifiable_key_value_response::Value { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -327,7 +619,7 @@ impl serde::Serialize for key_value_response::Value { if !self.value.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", len)?; + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", len)?; if !self.value.is_empty() { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("value", pbjson::private::base64::encode(&self.value).as_str())?; @@ -335,7 +627,7 @@ impl serde::Serialize for key_value_response::Value { struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for key_value_response::Value { +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_response::Value { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -380,13 +672,13 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = key_value_response::Value; + type Value = non_verifiable_key_value_response::Value; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse.Value") + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { @@ -406,12 +698,12 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { } } } - Ok(key_value_response::Value { + Ok(non_verifiable_key_value_response::Value { value: value__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", FIELDS, GeneratedVisitor) } } impl serde::Serialize for PrefixValueRequest { diff --git a/crates/cnidarium/src/rpc.rs b/crates/cnidarium/src/rpc.rs index fcc8c624ef..cfb3ee3bee 100644 --- a/crates/cnidarium/src/rpc.rs +++ b/crates/cnidarium/src/rpc.rs @@ -23,11 +23,11 @@ use std::pin::Pin; use crate::read::StateRead; use crate::rpc::proto::v1::{ - key_value_response::Value, query_service_server::QueryService, watch_response as wr, - KeyValueRequest, KeyValueResponse, PrefixValueRequest, PrefixValueResponse, WatchRequest, - WatchResponse, + key_value_response::Value as JMTValue, non_verifiable_key_value_response::Value as NVValue, + query_service_server::QueryService, watch_response as wr, KeyValueRequest, KeyValueResponse, + NonVerifiableKeyValueRequest, NonVerifiableKeyValueResponse, PrefixValueRequest, + PrefixValueResponse, WatchRequest, WatchResponse, }; -use base64::prelude::*; use futures::{StreamExt, TryStreamExt}; use regex::Regex; use tokio_stream::wrappers::ReceiverStream; @@ -38,6 +38,29 @@ use crate::Storage; #[tonic::async_trait] impl QueryService for Server { + #[instrument(skip(self, request))] + async fn non_verifiable_key_value( + &self, + request: tonic::Request, + ) -> Result, Status> { + let state = self.storage.latest_snapshot(); + let request = request.into_inner(); + + if request.key.is_none() || request.key.as_ref().expect("key is Some").inner.is_empty() { + return Err(Status::invalid_argument("key is empty")); + } + + let key = request.key.expect("key is Some").inner; + let some_value = state + .nonverifiable_get_raw(&key) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))?; + + Ok(tonic::Response::new(NonVerifiableKeyValueResponse { + value: some_value.map(|value| NVValue { value }), + })) + } + #[instrument(skip(self, request))] async fn key_value( &self, @@ -53,52 +76,28 @@ impl QueryService for Server { return Err(Status::invalid_argument("key is empty")); } - // Determine which storage backend to query. - let storage_backend: proto::v1::key_value_request::StorageBackend = request - .storage_backend - .try_into() - .map_err(|_| tonic::Status::invalid_argument("bad storage backend"))?; - - let (some_value, proof) = match storage_backend { - // Default backend is JMT. - proto::v1::key_value_request::StorageBackend::Unspecified - | proto::v1::key_value_request::StorageBackend::Jmt => { - // Don't generate the proof if the request doesn't ask for it. - let (v, p) = if request.proof { - let (v, p) = state - .get_with_proof(request.key.into_bytes()) - .await - .map_err(|e| tonic::Status::internal(e.to_string()))?; - (v, Some(p)) - } else { - ( - state - .get_raw(&request.key) - .await - .map_err(|e| tonic::Status::internal(e.to_string()))?, - None, - ) - }; - (v, p) - } - proto::v1::key_value_request::StorageBackend::Nonverifiable => { - // The key for nonverifiable queries is a base64-encoded string: - let key = BASE64_STANDARD.decode(&request.key).map_err(|e| { - tonic::Status::invalid_argument(format!("invalid base64: {}", e)) - })?; + let (some_value, proof) = { + // Don't generate the proof if the request doesn't ask for it. + let (v, p) = if request.proof { + let (v, p) = state + .get_with_proof(request.key.into_bytes()) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))?; + (v, Some(p)) + } else { ( state - .nonverifiable_get_raw(&key) + .get_raw(&request.key) .await .map_err(|e| tonic::Status::internal(e.to_string()))?, - // No proofs for nonverifiable storage. None, ) - } + }; + (v, p) }; Ok(tonic::Response::new(KeyValueResponse { - value: some_value.map(|value| Value { value }), + value: some_value.map(|value| JMTValue { value }), proof: if request.proof { Some(ibc_proto::ibc::core::commitment::v1::MerkleProof { proofs: proof diff --git a/crates/proto/src/gen/penumbra.cnidarium.v1.rs b/crates/proto/src/gen/penumbra.cnidarium.v1.rs index afed7a2d59..7a04181967 100644 --- a/crates/proto/src/gen/penumbra.cnidarium.v1.rs +++ b/crates/proto/src/gen/penumbra.cnidarium.v1.rs @@ -1,4 +1,69 @@ -/// Performs a key-value query, either by key or by key hash. +/// Performs a key-value query against the nonverifiable storage, +/// using a byte-encoded key. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonVerifiableKeyValueRequest { + #[prost(message, optional, tag = "1")] + pub key: ::core::option::Option, +} +/// Nested message and enum types in `NonVerifiableKeyValueRequest`. +pub mod non_verifiable_key_value_request { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Key { + #[prost(bytes = "vec", tag = "1")] + pub inner: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Key { + const NAME: &'static str = "Key"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.{}", Self::NAME + ) + } + } +} +impl ::prost::Name for NonVerifiableKeyValueRequest { + const NAME: &'static str = "NonVerifiableKeyValueRequest"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("penumbra.cnidarium.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonVerifiableKeyValueResponse { + /// The value corresponding to the specified key, if it was found. + #[prost(message, optional, tag = "1")] + pub value: ::core::option::Option, +} +/// Nested message and enum types in `NonVerifiableKeyValueResponse`. +pub mod non_verifiable_key_value_response { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Value { + #[prost(bytes = "vec", tag = "1")] + pub value: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Value { + const NAME: &'static str = "Value"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.{}", Self::NAME + ) + } + } +} +impl ::prost::Name for NonVerifiableKeyValueResponse { + const NAME: &'static str = "NonVerifiableKeyValueResponse"; + const PACKAGE: &'static str = "penumbra.cnidarium.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("penumbra.cnidarium.v1.{}", Self::NAME) + } +} +/// Performs a key-value query against the JMT, either by key or by key hash. /// /// Proofs are only supported by key. #[allow(clippy::derive_partial_eq_without_eq)] @@ -10,52 +75,6 @@ pub struct KeyValueRequest { /// whether to return a proof #[prost(bool, tag = "3")] pub proof: bool, - /// Which storage to query. - #[prost(enumeration = "key_value_request::StorageBackend", tag = "4")] - pub storage_backend: i32, -} -/// Nested message and enum types in `KeyValueRequest`. -pub mod key_value_request { - /// The storage type to query. - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum StorageBackend { - Unspecified = 0, - Jmt = 1, - Nonverifiable = 2, - } - impl StorageBackend { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - StorageBackend::Unspecified => "STORAGE_BACKEND_UNSPECIFIED", - StorageBackend::Jmt => "STORAGE_BACKEND_JMT", - StorageBackend::Nonverifiable => "STORAGE_BACKEND_NONVERIFIABLE", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "STORAGE_BACKEND_UNSPECIFIED" => Some(Self::Unspecified), - "STORAGE_BACKEND_JMT" => Some(Self::Jmt), - "STORAGE_BACKEND_NONVERIFIABLE" => Some(Self::Nonverifiable), - _ => None, - } - } - } } impl ::prost::Name for KeyValueRequest { const NAME: &'static str = "KeyValueRequest"; @@ -342,6 +361,38 @@ pub mod query_service_client { ); self.inner.unary(req, path, codec).await } + /// General-purpose key-value state query API, that can be used to query + /// arbitrary keys in the non-verifiable storage. + pub async fn non_verifiable_key_value( + &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.cnidarium.v1.QueryService/NonVerifiableKeyValue", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "penumbra.cnidarium.v1.QueryService", + "NonVerifiableKeyValue", + ), + ); + self.inner.unary(req, path, codec).await + } /// General-purpose prefixed key-value state query API, that can be used to query /// arbitrary prefixes in the JMT storage. pub async fn prefix_value( @@ -416,6 +467,15 @@ pub mod query_service_server { tonic::Response, tonic::Status, >; + /// General-purpose key-value state query API, that can be used to query + /// arbitrary keys in the non-verifiable storage. + async fn non_verifiable_key_value( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// Server streaming response type for the PrefixValue method. type PrefixValueStream: tonic::codegen::tokio_stream::Stream< Item = std::result::Result, @@ -568,6 +628,56 @@ pub mod query_service_server { }; Box::pin(fut) } + "/penumbra.cnidarium.v1.QueryService/NonVerifiableKeyValue" => { + #[allow(non_camel_case_types)] + struct NonVerifiableKeyValueSvc(pub Arc); + impl< + T: QueryService, + > tonic::server::UnaryService + for NonVerifiableKeyValueSvc { + type Response = super::NonVerifiableKeyValueResponse; + 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 { + ::non_verifiable_key_value( + &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 = NonVerifiableKeyValueSvc(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.cnidarium.v1.QueryService/PrefixValue" => { #[allow(non_camel_case_types)] struct PrefixValueSvc(pub Arc); diff --git a/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs b/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs index 9d620a2ceb..8751e80e6e 100644 --- a/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs @@ -12,9 +12,6 @@ impl serde::Serialize for KeyValueRequest { if self.proof { len += 1; } - if self.storage_backend != 0 { - len += 1; - } let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueRequest", len)?; if !self.key.is_empty() { struct_ser.serialize_field("key", &self.key)?; @@ -22,11 +19,6 @@ impl serde::Serialize for KeyValueRequest { if self.proof { struct_ser.serialize_field("proof", &self.proof)?; } - if self.storage_backend != 0 { - let v = key_value_request::StorageBackend::try_from(self.storage_backend) - .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.storage_backend)))?; - struct_ser.serialize_field("storageBackend", &v)?; - } struct_ser.end() } } @@ -39,15 +31,12 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { const FIELDS: &[&str] = &[ "key", "proof", - "storage_backend", - "storageBackend", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Key, Proof, - StorageBackend, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -72,7 +61,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { match value { "key" => Ok(GeneratedField::Key), "proof" => Ok(GeneratedField::Proof), - "storageBackend" | "storage_backend" => Ok(GeneratedField::StorageBackend), _ => Ok(GeneratedField::__SkipField__), } } @@ -94,7 +82,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { { let mut key__ = None; let mut proof__ = None; - let mut storage_backend__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Key => { @@ -109,12 +96,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { } proof__ = Some(map_.next_value()?); } - GeneratedField::StorageBackend => { - if storage_backend__.is_some() { - return Err(serde::de::Error::duplicate_field("storageBackend")); - } - storage_backend__ = Some(map_.next_value::()? as i32); - } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -123,88 +104,318 @@ impl<'de> serde::Deserialize<'de> for KeyValueRequest { Ok(KeyValueRequest { key: key__.unwrap_or_default(), proof: proof__.unwrap_or_default(), - storage_backend: storage_backend__.unwrap_or_default(), }) } } deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for key_value_request::StorageBackend { +impl serde::Serialize for KeyValueResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { - let variant = match self { - Self::Unspecified => "STORAGE_BACKEND_UNSPECIFIED", - Self::Jmt => "STORAGE_BACKEND_JMT", - Self::Nonverifiable => "STORAGE_BACKEND_NONVERIFIABLE", - }; - serializer.serialize_str(variant) + use serde::ser::SerializeStruct; + let mut len = 0; + if self.value.is_some() { + len += 1; + } + if self.proof.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse", len)?; + if let Some(v) = self.value.as_ref() { + struct_ser.serialize_field("value", v)?; + } + if let Some(v) = self.proof.as_ref() { + struct_ser.serialize_field("proof", v)?; + } + struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for key_value_request::StorageBackend { +impl<'de> serde::Deserialize<'de> for KeyValueResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "STORAGE_BACKEND_UNSPECIFIED", - "STORAGE_BACKEND_JMT", - "STORAGE_BACKEND_NONVERIFIABLE", + "value", + "proof", ]; - struct GeneratedVisitor; + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + Proof, + __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 { + "value" => Ok(GeneratedField::Value), + "proof" => Ok(GeneratedField::Proof), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = key_value_request::StorageBackend; + type Value = KeyValueResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) + formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse") } - fn visit_i64(self, v: i64) -> std::result::Result - where - E: serde::de::Error, + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, { - i32::try_from(v) - .ok() - .and_then(|x| x.try_into().ok()) - .ok_or_else(|| { - serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self) - }) + let mut value__ = None; + let mut proof__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Value => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("value")); + } + value__ = map_.next_value()?; + } + GeneratedField::Proof => { + if proof__.is_some() { + return Err(serde::de::Error::duplicate_field("proof")); + } + proof__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(KeyValueResponse { + value: value__, + proof: proof__, + }) } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for key_value_response::Value { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.value.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", len)?; + if !self.value.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("value", pbjson::private::base64::encode(&self.value).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for key_value_response::Value { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; - fn visit_u64(self, v: u64) -> std::result::Result + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result where - E: serde::de::Error, + D: serde::Deserializer<'de>, { - i32::try_from(v) - .ok() - .and_then(|x| x.try_into().ok()) - .ok_or_else(|| { - serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self) - }) + 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 { + "value" => Ok(GeneratedField::Value), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = key_value_response::Value; - fn visit_str(self, value: &str) -> std::result::Result + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse.Value") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut value__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Value => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("value")); + } + value__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(key_value_response::Value { + value: value__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for NonVerifiableKeyValueRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.key.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", len)?; + if let Some(v) = self.key.as_ref() { + struct_ser.serialize_field("key", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "key", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Key, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result where - E: serde::de::Error, + D: serde::Deserializer<'de>, { - match value { - "STORAGE_BACKEND_UNSPECIFIED" => Ok(key_value_request::StorageBackend::Unspecified), - "STORAGE_BACKEND_JMT" => Ok(key_value_request::StorageBackend::Jmt), - "STORAGE_BACKEND_NONVERIFIABLE" => Ok(key_value_request::StorageBackend::Nonverifiable), - _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + 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 { + "key" => Ok(GeneratedField::Key), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NonVerifiableKeyValueRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut key__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Key => { + if key__.is_some() { + return Err(serde::de::Error::duplicate_field("key")); + } + key__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } } + Ok(NonVerifiableKeyValueRequest { + key: key__, + }) } } - deserializer.deserialize_any(GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for KeyValueResponse { +impl serde::Serialize for non_verifiable_key_value_request::Key { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -212,23 +423,115 @@ impl serde::Serialize for KeyValueResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if self.value.is_some() { + if !self.inner.is_empty() { len += 1; } - if self.proof.is_some() { + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key", len)?; + if !self.inner.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("inner", pbjson::private::base64::encode(&self.inner).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_request::Key { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "inner", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Inner, + __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 { + "inner" => Ok(GeneratedField::Inner), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = non_verifiable_key_value_request::Key; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut inner__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Inner => { + if inner__.is_some() { + return Err(serde::de::Error::duplicate_field("inner")); + } + inner__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(non_verifiable_key_value_request::Key { + inner: inner__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest.Key", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for NonVerifiableKeyValueResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.value.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse", len)?; + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", len)?; if let Some(v) = self.value.as_ref() { struct_ser.serialize_field("value", v)?; } - if let Some(v) = self.proof.as_ref() { - struct_ser.serialize_field("proof", v)?; - } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for KeyValueResponse { +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -236,13 +539,11 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { { const FIELDS: &[&str] = &[ "value", - "proof", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Value, - Proof, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -266,7 +567,6 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { { match value { "value" => Ok(GeneratedField::Value), - "proof" => Ok(GeneratedField::Proof), _ => Ok(GeneratedField::__SkipField__), } } @@ -276,18 +576,17 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = KeyValueResponse; + type Value = NonVerifiableKeyValueResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse") + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { let mut value__ = None; - let mut proof__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Value => { @@ -296,27 +595,20 @@ impl<'de> serde::Deserialize<'de> for KeyValueResponse { } value__ = map_.next_value()?; } - GeneratedField::Proof => { - if proof__.is_some() { - return Err(serde::de::Error::duplicate_field("proof")); - } - proof__ = map_.next_value()?; - } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(KeyValueResponse { + Ok(NonVerifiableKeyValueResponse { value: value__, - proof: proof__, }) } } - deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for key_value_response::Value { +impl serde::Serialize for non_verifiable_key_value_response::Value { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -327,7 +619,7 @@ impl serde::Serialize for key_value_response::Value { if !self.value.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", len)?; + let mut struct_ser = serializer.serialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", len)?; if !self.value.is_empty() { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("value", pbjson::private::base64::encode(&self.value).as_str())?; @@ -335,7 +627,7 @@ impl serde::Serialize for key_value_response::Value { struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for key_value_response::Value { +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_response::Value { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -380,13 +672,13 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = key_value_response::Value; + type Value = non_verifiable_key_value_response::Value; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.cnidarium.v1.KeyValueResponse.Value") + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { @@ -406,12 +698,12 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { } } } - Ok(key_value_response::Value { + Ok(non_verifiable_key_value_response::Value { value: value__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("penumbra.cnidarium.v1.KeyValueResponse.Value", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", FIELDS, GeneratedVisitor) } } impl serde::Serialize for PrefixValueRequest { diff --git a/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto b/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto index a84a87a2f3..87ecd84fb3 100644 --- a/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto +++ b/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto @@ -9,6 +9,10 @@ service QueryService { // arbitrary keys in the JMT storage. rpc KeyValue(KeyValueRequest) returns (KeyValueResponse); + // General-purpose key-value state query API, that can be used to query + // arbitrary keys in the non-verifiable storage. + rpc NonVerifiableKeyValue(NonVerifiableKeyValueRequest) returns (NonVerifiableKeyValueResponse); + // General-purpose prefixed key-value state query API, that can be used to query // arbitrary prefixes in the JMT storage. rpc PrefixValue(PrefixValueRequest) returns (stream PrefixValueResponse); @@ -17,23 +21,32 @@ service QueryService { rpc Watch(WatchRequest) returns (stream WatchResponse); } -// Performs a key-value query, either by key or by key hash. +// Performs a key-value query against the nonverifiable storage, +// using a byte-encoded key. +message NonVerifiableKeyValueRequest { + message Key { + bytes inner = 1; + } + + Key key = 1; +} + +message NonVerifiableKeyValueResponse { + message Value { + bytes value = 1; + } + // The value corresponding to the specified key, if it was found. + Value value = 1; +} + +// Performs a key-value query against the JMT, either by key or by key hash. // // Proofs are only supported by key. message KeyValueRequest { - // The storage type to query. - enum StorageBackend { - STORAGE_BACKEND_UNSPECIFIED = 0; - STORAGE_BACKEND_JMT = 1; - STORAGE_BACKEND_NONVERIFIABLE = 2; - } - // If set, the key to fetch from storage. string key = 2; // whether to return a proof bool proof = 3; - // Which storage to query. - StorageBackend storage_backend = 4; } message KeyValueResponse {