From 927312ca6430c354bfb7ab9bf663f1daf46206e9 Mon Sep 17 00:00:00 2001 From: Chris Czub Date: Thu, 16 May 2024 10:59:22 -0700 Subject: [PATCH] Support nonverifiable storage in the `pcli query key` interface (#4380) ## Describe your changes This PR adds support for querying nonverifiable storage in the `pcli query key` interface. It also changes the API to only generate proofs if requested. ## Issue ticket number and link Closes #4359 and closes #2647 ## Checklist before requesting a review - [x] If this code contains consensus-breaking changes, I have added the "consensus-breaking" label. Otherwise, I declare my belief that there are not consensus-breaking changes, for the following reason: > Only changes the key value query API --- Cargo.lock | 1 + crates/bin/pcli/src/command/query.rs | 76 +++- crates/cnidarium/Cargo.toml | 1 + .../src/gen/penumbra.cnidarium.v1.rs | 158 ++++++- .../src/gen/penumbra.cnidarium.v1.serde.rs | 386 ++++++++++++++++++ .../src/gen/proto_descriptor.bin.no_lfs | Bin 95074 -> 99467 bytes crates/cnidarium/src/rpc.rs | 57 ++- crates/proto/src/gen/penumbra.cnidarium.v1.rs | 158 ++++++- .../src/gen/penumbra.cnidarium.v1.serde.rs | 386 ++++++++++++++++++ .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 406962 -> 411355 bytes crates/view/src/worker.rs | 1 + .../penumbra/cnidarium/v1/cnidarium.proto | 24 +- 12 files changed, 1221 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1dc1dd197b..f0343c58fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1394,6 +1394,7 @@ version = "0.75.0" dependencies = [ "anyhow", "async-trait", + "base64 0.21.7", "borsh", "futures", "hex", diff --git a/crates/bin/pcli/src/command/query.rs b/crates/bin/pcli/src/command/query.rs index b79fe5a933..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; @@ -40,7 +42,18 @@ pub enum QueryCmd { /// Queries an arbitrary key. Key { /// The key to query. + /// + /// When querying the JMT, keys are plain string values. + /// + /// When querying nonverifiable storage, keys should be base64-encoded strings. key: String, + /// The storage backend to query. + /// + /// Valid arguments are "jmt" and "nonverifiable". + /// + /// Defaults to the JMT. + #[clap(long, default_value = "jmt")] + storage_backend: String, }, /// Queries shielded pool data. #[clap(subcommand)] @@ -145,7 +158,7 @@ impl QueryCmd { return Ok(()); } - let key = match self { + let (key, storage_backend) = match self { QueryCmd::Tx(_) | QueryCmd::Chain(_) | QueryCmd::Validator(_) @@ -157,28 +170,61 @@ impl QueryCmd { | QueryCmd::Ibc(_) => { unreachable!("query handled in guard"); } - QueryCmd::ShieldedPool(p) => p.key().clone(), - QueryCmd::Key { key } => key.clone(), + QueryCmd::ShieldedPool(p) => (p.key().clone(), "jmt".to_string()), + QueryCmd::Key { + key, + storage_backend, + } => (key.clone(), storage_backend.clone()), }; use penumbra_proto::cnidarium::v1::query_service_client::QueryServiceClient; let mut client = QueryServiceClient::new(app.pd_channel().await?); - let req = penumbra_proto::cnidarium::v1::KeyValueRequest { - key: key.clone(), - ..Default::default() - }; + // Using an enum in the clap arguments was annoying; this is workable: + match storage_backend.as_str() { + "nonverifiable" => { + let key_bytes = BASE64_STANDARD + .decode(&key) + .map_err(|e| anyhow::anyhow!(format!("invalid base64: {}", e)))?; - tracing::debug!(?req); + let req = penumbra_proto::cnidarium::v1::NonVerifiableKeyValueRequest { + key: Some(NVKey { inner: key_bytes }), + ..Default::default() + }; - let value = client - .key_value(req) - .await? - .into_inner() - .value - .context(format!("key not found! key={}", key))?; + tracing::debug!(?req); + + let value = client + .non_verifiable_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/Cargo.toml b/crates/cnidarium/Cargo.toml index 9e7a9bfbbe..bd039898d7 100644 --- a/crates/cnidarium/Cargo.toml +++ b/crates/cnidarium/Cargo.toml @@ -12,6 +12,7 @@ rpc = ["dep:tonic", "dep:prost", "dep:serde", "dep:pbjson", "dep:ibc-proto"] [dependencies] anyhow = {workspace = true} async-trait = {workspace = true} +base64 = {workspace = true} borsh = { version = "1.3.0" , features = ["derive", "de_strict_order"]} futures = {workspace = true} hex = {workspace = true} diff --git a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.rs index 590f79e139..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)] @@ -296,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( @@ -370,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, @@ -522,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 7a58283430..8751e80e6e 100644 --- a/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs +++ b/crates/cnidarium/src/gen/penumbra.cnidarium.v1.serde.rs @@ -320,6 +320,392 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { 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 + 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 { + "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_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for non_verifiable_key_value_request::Key { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.inner.is_empty() { + len += 1; + } + 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.NonVerifiableKeyValueResponse", len)?; + if let Some(v) = self.value.as_ref() { + struct_ser.serialize_field("value", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __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), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NonVerifiableKeyValueResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse") + } + + 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__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(NonVerifiableKeyValueResponse { + value: value__, + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for non_verifiable_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.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())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_response::Value { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __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), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + 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.NonVerifiableKeyValueResponse.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(non_verifiable_key_value_response::Value { + value: value__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for PrefixValueRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/cnidarium/src/gen/proto_descriptor.bin.no_lfs b/crates/cnidarium/src/gen/proto_descriptor.bin.no_lfs index d50e1bd8818eb8b4d521b590c16703d42bad4e23..cb5f8b9377fc15a61da07be47f5ef6ad339b0300 100644 GIT binary patch delta 26575 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 delta 22429 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 diff --git a/crates/cnidarium/src/rpc.rs b/crates/cnidarium/src/rpc.rs index 992fb35a38..cfb3ee3bee 100644 --- a/crates/cnidarium/src/rpc.rs +++ b/crates/cnidarium/src/rpc.rs @@ -23,9 +23,10 @@ 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 futures::{StreamExt, TryStreamExt}; use regex::Regex; @@ -37,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, @@ -52,17 +76,32 @@ impl QueryService for Server { return Err(Status::invalid_argument("key is empty")); } - // TODO(erwan): Don't generate the proof if the request doesn't ask for it. Tracked in #2647. - let (some_value, proof) = state - .get_with_proof(request.key.into_bytes()) - .await - .map_err(|e| tonic::Status::internal(e.to_string()))?; + 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 + .get_raw(&request.key) + .await + .map_err(|e| tonic::Status::internal(e.to_string()))?, + 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 + .expect("proof should be present") .proofs .into_iter() .map(|p| { diff --git a/crates/proto/src/gen/penumbra.cnidarium.v1.rs b/crates/proto/src/gen/penumbra.cnidarium.v1.rs index 590f79e139..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)] @@ -296,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( @@ -370,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, @@ -522,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 7a58283430..8751e80e6e 100644 --- a/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.cnidarium.v1.serde.rs @@ -320,6 +320,392 @@ impl<'de> serde::Deserialize<'de> for key_value_response::Value { 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 + 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 { + "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_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for non_verifiable_key_value_request::Key { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.inner.is_empty() { + len += 1; + } + 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.NonVerifiableKeyValueResponse", len)?; + if let Some(v) = self.value.as_ref() { + struct_ser.serialize_field("value", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for NonVerifiableKeyValueResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __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), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = NonVerifiableKeyValueResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.cnidarium.v1.NonVerifiableKeyValueResponse") + } + + 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__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(NonVerifiableKeyValueResponse { + value: value__, + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for non_verifiable_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.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())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for non_verifiable_key_value_response::Value { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + __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), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + 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.NonVerifiableKeyValueResponse.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(non_verifiable_key_value_response::Value { + value: value__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("penumbra.cnidarium.v1.NonVerifiableKeyValueResponse.Value", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for PrefixValueRequest { #[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 e7a8642ba9db143fea7da87f93371e2066a1851e..93fdbd3571bb385b337ce449cfd082c38c517ffe 100644 GIT binary patch delta 26756 zcmb7s33yaRwtuSb)_t?zgssEUgjHl0H6W-gVJ3ovL3G>@o1`HvA&FU39JL)3!2vfw zFStDw7j)ca248esa79MkPzPC@fl(xgA_l=hcrx#IYPq*{miPbjefj9$sXC`lojSFh zs$0MRvh((vx?H=Ky?dXr>t@s6bRWBpue4gvxt}HZfZ?u_tWGtj>gzh^#aaKn;hhK1 zsjr_?n;hKGSl?Vft7Z1!a+%(`aW$K0m~WeI2Y>7u*1;d}2;0x*`LQ+Zd~2MsnDN%; zH7w8UI<hUd9?lgExLpFDF~`7h3{mM|{trEW^+(n%TDa78-HFLWb4z3L ztYovdg!yY;W8)@`T+G-?X0BxH0ha4U%FmrTxng|9^v=5-fh*2bxJ}|9eUCFOcXldS zTRpWg*_5oSN_w;X&abnwp}mAaTL{ed?D3N;&nllF#6>-`a%%aM&TY=LNV2YF-t_qm zNpG3o@;aM7z285nF?PxX#Rn-<$Bv%_1kMhP$p+LSSv`>oFR!bruTIs?@vd<5TbgH= z&Acp8o2pKT<}>}T9%Y^Vp|7y$#KC7H@th!SoK#ZQ8fs0coKh}SL{aCIPpg=C9u<{G z5<@2M3V-n%Y~qM`hN{J0{^as0Kb!vZnNz2gPpmjsXgn={Ub3kvF((;lca?9gXQNL) zL2w!?nY2zP&zoHSv$5mPn>nuhtm!k$Cr+%KCJpa;*4W9DD}PB^xR*55*G;T%oR?_! zpI*iQDPy=@ZLlo-=lG#e}ibD=MeVJbTKdDV4vRQjq1juBwuM@J;s8 z&-Xe7UpfQq$dV}qw@$(Tw@xXzZOR#Tq|GVVb3DyK|7 zuYhHy44)Y$aIc%&NwbW^zmqc(@43nTX>wUc^1c6@JTfEszq%m3$j#Q$&m4Ph!9VcN z8F!8U_0zngKY1fdxd;AWxQ=J_&3d(U-9~n8G(E`WR+eXu&N`39dd8>MB;&JNYHQ<7 z$>w;#hOs5&K*b&01zbo*F%x*F*g+PboSc)W znjdePU)P+tB3`QXDIFXqjhmA3hQ{O|j6BIe)q(L@EzR*+$(qDv$@sjMs+xFhYHrev zSErhqQ*~9%I%B#ikWrPW>qji{mZoHNytzK!)R3%7&7L1`hEY_JWD_wJx$)Vl+GLX~ zX0{yJ3$Yh z9i$1>N;JpQ!a%c<$+~!BvNoAO)ulmH?Ne2<$(rJc#w7k`rJ5TPjq}m8)o#40roN@N zI$l>V%vO!Lp{^RHtFM!0f!*fEFHgan61A5n<~NaTn^W_WgX$aMA;<^=q~j=azANkA zn8IiqRNO#)&fs~fb_VKmMqvk4anmy<@s29VM8-)$MiUt)A>-Ae-j-)n@>r}PJ~hKP zwf{hYbYr|ZNiH!bu3HgPYJF`BwPN5JKdPuJ9R&Uji_I7Hjn}s{H>IkRC2Y@_n&vT;W+Ug+Ag7I-Q-hq2XZ(Vfs*DaY{(^VW2pNCTwWksp z@r-Ff#t1S_3o=HKaaxcu>KSKI#vyk&y={l}woXbmMkFg)wkovdk zAY{Cjx>@xtqV;u8A>XfWY!)_5HECa|ud79CCojVYA$nRG_M3$sQrj{oRi}Is9WkD~ z0-dj!dSRW&+?c$yB}E=sH$Prglc<}MMCN+9pe!YxK)%a$FAc2kqF(3nTxBX3^*Xm8 z<_>W=T*kaG>lzmGC;gcX%DXU9;*NAplhJ54!@uFr?3A(@T(lw4;6yXB#UMopGG}#{qL%oDQV>3Ic zED>;Xa3=z8>f#BnE7b?w9NdW>arYO38=%VHxS5?*Ruym?;I7IRLjv+shpH+RZbXO} z`t)=NX4!BKl&kne$m(4DWtFldS&+gU7JxOpQW6F28u(EV2;I`o2 zschlhD2@6-s-&Odw!obla6CCJ(va~IK1e0m(BzUph)rqP-E|s-T+*YzMi6qz@dK3* zbdkAya9X7~kTN%r;y}vW?4CLeQs$N(rxB#g9XLoyLI0`cgH=)lLTUpc5eTWx?xE8l zq&BXFL?EP=gj9+Op>x&oUMgo4QtARJQAnxF*4{(?uC9}=UKCR5dX~Be(eGdu^t)#N zmaXi&k<9_S3-;!0?LOq*&9MQhAQ$Y-g9f_~N%m~dxYB=V8+&B*l>vJ;8tTfxo3g=v zrPo=pXM_F9l5XxGQFxALT+QRLxPSk4)_tPvP|zs{mAN_)mji9C&i0gm9EiKRNH=&6 z#9iI3hkG148~5yAv#k659-pvyI@>i&__6tG7NMETG&wq*xq$H!Hy3UVWD4kNXc2_c zDiy2?82X=r20?T{DG7U!3n+LV;yp1F|0 z7i3-zQGi+$d=@Z}MDS7Bh4g1tDoMJ3c+o%_C7pmS_Kl@mrW7 z(+!Mk=5Me(ce}baB~z;`VP?0|oOnfDRc%XC>M~lm<-3kdMcGKs2?=PYCD_G=1PENh ziaLh`Xs{&BO1cI`AjeW>mY|68EB?6%YCa@~MPMieA)^Rt!KDnV9IX`wR1n|}d8!g< z#haNqB*?(i5DH2tl$!EpmRlNTfzX>-uK^(eLT_g1S+WhF#I2!YSda&(+W?~;AbO}Y zI)**Kn05?|duianGmv>1<3m(t41EN(D2#o8fsbK*02m*jGefCmp;C@#u3%hy4Te90 zS`x-TK&jLKco9|*z_Ff6lmk&KSnodW<1}*6G-0n|{BGtyw1eH378Nnb(yN%;Ni`i! zl;)lP&O49_1(2+*3<9inXV`QgK;WIBcYpvb?hKs+lcQ(e#mw}-_6{VY2&9Kakaygb zSp?~PSLhvLx&YhI_!$-f%09>C|4$vOZ=E6Om!kAh4Q6y16u; z7-Z{*m|s`T&!_@`o)76V0HNnYy3+%K^C8yb1feD%%6N#KG*a|=gZlga@6h*%CdsZxm9+Hiv5(pT1-qoT&X!U!3&fYI?%@lWzsHYQl+L^sP45 zcle7eH&2%srB?be0d^3C-;whfRNL458Exzj7rquc2F-S_F)THx10xS*Vx3AgMA5IY zK61c7D*D1}tY5hpHwhLJ?mxjDBMLxy>zO~SKQAjc#3ZynEQiL=^(?!C5=_dhXC37v zgv=;sJ?q)aJ)H!ZaKJw?{*M2}9(G#cpP19x9ZyCyh39SbM}EP|&)>+*d|7uQqv(x{ zVl*lr#J|l<ygt)yNAJUe0t@C zNgL#Fkw08QG@=>D^0cQIQ zx`pb%PlPbLRVt{{XQ*nfkdInt;D8uts#Ie|=?L_x?631Inx6%+=Az&rM4)|#de z5W-qxOAZ2Ry`C#WVFdVw8toY#pPraAs5)7jnwM%$`j6~qx&Gr{vwiC` z|F^H%uk+$qs0HDjfqu{ZtYaFH73`+5)lSwWX4PV|eOW3Y0$LZVF!@XzABZUa#X10@ z7J1Z8G-E-Z&;t+}0HLr5y9C*?u4lG0vtwyD#OUhgG}i}SQL$Pro= zyKt}+b#Qwr5!s%3(En^d>ytj1SyVRi9i*bP6Xkg3q0BbJeuD_1Idi~qh&Wj=B2<1I)?2*iTg~)eARuvr# z6N~)=;|sZ5%L7C156sCySb~}fi!0CkhyO@B8<_q_=))jG%s*HMT^X_Fx|Rpk2ZkUJ zwAX_Gj%ztM-lo})M1ycWSYppJ5j|Fcn(BCMVpg&?j>r#%d=LSwt-l=Zmrm9F)Oish zg5Rp(DB_BN3%l`DbH64E1=XgiQq3}uT7&o~4xl8Hj%5y%E-jA4W+E0W&cp(T4*CEB zyJ->GorCCha}9J80pe7BW8m|s52}m`g;ka{M3rgzZ;~k&akW_m!axNmrw>33MT@xF ztO7#TBCa;8fVBKy#MJ^+?1C3_F6SB`hyv82XnnMpBm6>h4N@U$F)!?-A%@h&JXWHV zLL=N5Dn;8wKrM-8=o^)!eh@`9ypb2ml>s0`-AGE^O%o>8|DJgh_fP(o-E-2m7_lA1V=zlL)3v-j(5OOas?d4uC4T6#X0q(E(J6kyBfl!7684rZ*L({Lx~52{2oG2aVg0@y>Fm0ai$Qj4JeW)XgPX7`0#Fyie?Y|F7S(M`N}1h~OgBnm}Jo zQV1JoAE`NYiCWix@CcjWNV4niKf=aF;`5U8W+fY4zw9V0kI1AZ*I$^4T0c0-Uf^R; zhE~u;RbL6+2azkl0<9oISAf_Gm14zXdG;GTYlFY>80(pSgJZ=*2C%5(t=B#d1Oos{ z`GAnWUYimS^4IH*2MGD=b-%O7|KHT61cD?$Es566Z)#KaL9)oLYzYWaZ;~zhK~>wc zH}b5{{agRR`ldH>M3G5Vn^b*Us|thy0A&GyQ1xxCDj?*)tyKkt{I|8LHmUk9S5YV+ zNCMQ7NY!_Fb}!)*X(Wr>z2#~Z5Tf4YD%fDtF!nx2Sdw}Q5JUlLQ8Y%p&(&@LsgU$O zM`TiEhNSm7LX%HXx3Xy;xrOuX+=$)FAf0XmbNAC;`K;zp^t``2t10g$kHyN}8BpyC^h+8Wpz;?U* z+YDYeW>+YI2De>dyVBscOAjhG?eGENH^bmJj%V-TS^NCaCLfyK!y~efJ7lacwBG=s z7(m%XfRO)%mJbN|UueGpg#0hG-#BEfy?SZ^f+RpKiA=Uv4_#>_i`;5%0feZ%dTw#Z zZ@%JOh7o}v3Q&t8zxj%1%OM=8kn|PrDxC}vlD^`7`(cC2&4_1z!?O-?7V8+FkZh`I zOwGa}PQt&$;zj9ic!a{>rWqk4v}+>(p*%oY9w1b2*G2$DdF|Q=fGDqB8zDl<9N;Rz z0|ZHcS`u}h13Wt}I}egY?w&Hh0|-$EI08IUDH!1(SMjxoFan?!MMgNtRnQNqkaUo% z@ERZ_9pnhF4TTY+o_&~S-C(d->5q)?E1UN?|1d|$hsukR5x&<(0D|g(vOGW-;d^Za zK$Q2rHUc2Z`<{&8le~`wFZ|VcOZxY)S{>lUTb8_Z81_I z>RO|VY#~6w+pD)*E~*9A>kR**2%mVtb)g9Agx48rnGzM}eAgKTS_E~%>kNpH%E2W8 z`<$d9Ci?4)p(m;WAZelD{{sai7KR1T`eR{O0Ife3h6T|2V_{f;g#s3a1z0FxAr@`N#f0=Cfl(=7b4L{X?Im#`+@){m#ZIAN5+68NjNRd1Za>*f{^mi2S@5Vf- zl>rgTJZY%K5FnI!((vS{4G3kPG%#wn)2QuI%RJNSdAx~@c_x%Wp72a4gFN9G!;^s_ z@Ib~h2F@QIHiQhg!i%l17I9p&doh$jKJcP8iYq$6i$;MQ1pp!AMWe4AK>%Tt7med& z)C3SldC?d#5+_BZB=WxOzuti#f8ooad>;(svEDU8Q`q`FhttghA&6e$PLx3$IAOXQ(YU<)b0{Jp;iD>b|(& zo!wS)geRn#n zQ4|I34r>%eLA$9&$0M@nqMSB^=aqKHg_L>qmtmLPP}4ATPO>i9n5vrDlx(~#Rh4X_ z+YGR86h7EyWamniMQk|}!gB=t7ITM#`eUcC&%dh&Z@`pUr^sVJk}7{ZI?k}oLt!B~DCAJ+ zog%7aczW!@F*Z^$c2x)|h$tQQ??m*+ytVFQ;f z#J1F>DATdl?!95RTI9!88zeN@9HnC8<(mz(s$NIlLj*m~@ zZK_iNSDXp}%2NSAbj`a>oC?qZA@%utO>yUq`ZS8U*9`hJP2rgki~`a*Bkow=Z)UZd z@|=-+)ct0pqr1gI4YN6|7}mBn4B*@N1#5M!K!&DkP1O$p!L`;bkSk@yWMZXE{SXlS zaILB4MnLq#wI=4qFR4khX%X_A|Ii@5?}F#T0%#5LTvz~A;5oBEo{NJ8T0LhDmuH%Q zDBwABM7b)Dp@8Si(bR|sg+(a^JD}K z=qFI*MXJmz5Kd5pMq&;BiWx$kdY>(93j1s^dH2|DcKnw$NnE?crN)|M>*d4vCER~+ zD8IX0R*aV0$S9pPo71XlOK22YRc$c~;NeRVD<7fPbOYWj5_B+Dtw*_HOIjBd|>MC!fUUTnLKRm=kW0bO6zeZRY4ou?{3ClE+LDgF-fW#9lEdl#K^K$$P_cXi>CR z4+}^|IeYc600=>QX;`4s`E1(!?ZYUH)A?}#5VS88M0Ys$>G1%m5VTK^2Y?W?kH!N! zAf(Ce8&ez*PRXVN!f*6Y00cfjITQfGY~RpOKs`Fgv%fX77TB^!)AaSN87X#`8Bi{V zR-8v#|5(OXu=EkF7N}6`h^b=BfM7aes(>&c)H-5f14&*32s<7zOXO-B5Z&R3*;ig8 z%Av*XQIjvW44GS;LmoA8^w0xqStOGNKbU5I%ztMT@75bX+44AvC;etmZrPobUe{OSAFTA0!I+)bRnLqbAw}u;4*XkV*UBcOnK7qPe*6^uub0} zud1(WZmh3$#qmVOSpyk%LJp^h1ay+#4G0go)l$POpwPHLhUo#(Dz{o_l{;yy&LIQb zW3?_jmEXnF_k=>o9q+N!%#U0Ua*u^eezGlTdv%Y6pqhec+Iy^Sa_5B1Xxe+M{=;Bo zlz`r4-)r$lf~FPoisj^r!zm^NTRN5=7vy;f)Drq*4ZnH%_Fk$XVDhch!$tskb0t6X6ORA-&VkB|MvZCy5w|H(+# zCa#>12(|bE2v_v+m6E)XNYX{*@^E?nQ z$3L&gM%$8b#Qz<&a^0@qN#!|{p0(lE0bn5?hpF!Pvjl4Xjl2`xke_d8Z6IaTIx8}i?= zVsZrp2>I_=eFn$?Tn_cBP5wud`KpUHg$2-tc2igYZD==H1-fI>hIW&MO|i5PZD==H zrE&!Xfh5ryG~9hdScvY|ZnpR~f8&|__4H=Tk>|NNINC!G{n#?))RcZE|RN z^@*h}=>oxl0JUqbaFCjgoSb=jS%7Ii}qLbd2t%K@-RqnRO@M2O+FG-HrJov+i)m zy_q$2@L3m8wBH%r0n1+Mkh<>hfiE-bPVEdrSa-S`^JQk;u>$?dGD`w;(sCLEdH0tZ zk?y8^MH3{vEDbjU!+wj)!!RJ&4p6%p9hmO7)VT#xA#A^;&Mg4ZH2W>=`o%;@$6f6f zKd2`{ac#2Q!c}Qe8yro+!46nvPpKQ>pP@8Rcq&1dD(p z70a(}|H#>V)TCeAYLlHKwr`mbECSM!JlC_Yx3g|iOLB?@U2kJaPQi#=@|7EG-pemM znV&Qc5Hzy>Hy35ZHAwbi~O&$Dl_vz}0u zq(IdzHjXPUhGuzG$>p}+|6HCpb-7jwRFt&bRtsD}@GQ3ruL@<5`>zURP{mf+YT^eEWUR8)#1DvJW|gfben1RVt87gCv_Z{-E8J!K z^GEQKk$JR{zf1cA5Ts^8)LXy}H0$KiK)%}Mm9giz|IP@$+mcsF&$@u0cM<5aDUl1B zt`0S&`mDCqW)-Q>bhSNFo`L{E)7ADVQ^b%$P$Z8&Q#ef?Wqw#Yjeq)u+#U_63!-DQ zhqccjFA92C`wXDqRX&qP%e6_}(#@6^`$Q-mThxfFiT% zbATwPmHOP1QdLyy1%JUz{`C1Tgd(V|UkFvD@?NmjYA#>2^$RwJb{bUyq3R2EPr2&? zgsLyt1BZ!Doli}@PBsvJqk-0irBFk!(+z}Fl(J4Y5Fl~t2BHA)tD;l;^$BkM{8imb z`J$BoWh()qLa*vpD)8(#?5u67l_=c%hK=wb8L@yGX@g&Q2_HEgzoFR%tr;LR+n`Md z2*wTC1p%Si1{-t0R@#aekaa$6Et|!+v-3X;Wl+!lP}>lhD3@+-K*;#eRwoL8u;GWc zI#B?G4L`JTqCf*<0d>&LWW!&avN@Db3!BZ_ddLI$o3-@-A%C;B9)-QPO6yHca;svi zwqAj-9-y=yAd1?mtyk#TpV(RdqpU}v@eE;~bY_$Rgoyy*4s}-=o zCDD52fv2W)ws3JmuFLH0mEG)=p$R%3uDyxs09BTj%YLc6WbcL8C9 z1GX*CeE?yE19oBWY~iJaRPZ74($hvC3JajNKcxK=c~HP1?Vo@s;1KyIc~2ps!?FkB zHxxY_%Ap>3So;uCA?L96Awc5PK13%SM^z7;%dMeDwGR~v9|Dv<1PGOnk`FC0uz2vW zcyO{-I;{`RmA%G+eq_{CqCJ8_)Sjh*m=ByrGMkN^5!y9sD+m}ru>RcU?>iP zT!9J#^jYGlb)z6a`6Z6}-bN6h{t^dwHE3B&1Y}t1n7zcZR$#~kLCH*04hUW9peFPY zkH{i;a1av^AatpN;gc4l9;tb=!{zM|AP5AMZU6}D-t1%-iE$DTvTkv zgV>e0r|OBQ>@vr{sh;1HUKR==m6kaw0Oko_TIQ&uFF*)c=HR;*sSYf^+);Z3Ph9H( z)auZnzC4sf=2-4{ax03=khI*v(YV-dcm!8C>Y5Y~Y6hr9QK)@|qXMo-g{T!yp*&;+ z6uh`5C6xl}?T)&r=ZQ%gP)nl7%I%?2G)dp?q}9n6azoVZp}CRy4u{KQNLUP#0ktTK zq1+KFMLWYgoI>3;6y?5yyg@33rtuwh0m>6$Za^(*424VlP%GLp`i>{V7sw4!zJnW3 z+sVO;JbSg1^*;`a9mSpD7pGhz(Z}4P*~G{82{+m4PAeLC29d`vJ*~ z9&*&)T3}=VL2ft&0WJHGqc)sGK!%4Mbt_+BNszuwgfwmGA9nH!g!>Z(_>oK&k^w@< zB6DPLpq^_SQ+{?v5~!U)kY6ha0%WdnFnrR+vIy0%A9MKcaL}IXrY$0%g+P&Z1%%Zf zbJT?>K!|(HQQsf}qPWK#9K7YDe*ofw-DA$c-@$6sfr)?v+21;9UrM+Lc6&Uq8xbJ! z3CE=FnueaB6eG)%j#(}`upl7IQ$Hf04o^Ggc-2-I+W+A2)}XCu@cx72QxzvL&33?RtyTV#L@UU4waXbxm}Ra-4hIjEkXXtRL;mRB8NHdzSp zYhkC5!9Ealr;x$E*JL*$7l+nwIDBJZXxe~)pzTZ(^BWG`!o*7?lpPAZ;q)w1hNe;V z4d;~6%FtLjL)w^)fuT9M`1;V$Tp0Sz(8WQ3#0{aLx$y8mg$@k@a=hiF1CK^f@g0Y6 z3JguX_#MaTpv)%XiSIgQTp3!%6G4!%sUj6meAmHNRvKEw6W`N@rVM0f5VV~so(TVY zFEljbhq!Vb_5m4B1VQ%!5JV2q2guOS`U8h=2@Fj$5D41Pv~u~tQGp)hh5{cr>cezE z`ZDDM2Y1D#N8=71qz&E@7+QYJ7#bQk6XC8Og@y(JBL5s3+7O1`O#Uk!8U*C{I2<|X z(?*B?RoNN-N0Zq$M}@BeX_((0lt~yFK#)N*kpVW??qFvn4J|&Y`6L{H=n)tM zaC{<1U~*{qyl4IeYZo^yv?Kxw0Y#o=i5wz}_^^)*4YhYTd{Fr*7rc*zZRMGtI({LFzdbyLR#4M>(H*q zf;#NaB#@!Mam-5P(U$P&cE=ni?uHa0qIA&V@(M>0g_I6D*?Dp#6CtHc2+;ykD5?mn zr|5F zRqtuWe^xIq1{Tpdz~N99HCHCoslAk}BMy(1c8pK-r(DfT(y;y!Cs&?_6~B;FZr=EvedS`Cb~(AbokU zh+N{RgRe%Ug{W@{C?2UmDhd+#agKFnv1c!cWUYv>*gYJN)6wf{;@DNbT9s1guJn!- zJ@6L1%7*Ws68iZ&^(0vEMx6WOqgHXn60goxr{l95>*vKM1}}W#qs8WAT@yW;M^B~w z^j<(yGKuHY{?&tH)1gZE02z*=n``8kiuJQDp$D;MB~cIhYyw|s&1znWdSscgLRW)=ztsdkWh#cx8OnPvO+8?!P#6wy*CZ;=OG&(PcL}KpKwA(DE z-R7bQ|4qb*{TJD9UZO$PlHTK*|Kpbd(^Am{ol9TZLQ09(0|T3)s8n+kUf)ws-wlpW z!JBb7KiA4NLHvCBOqp&>BvN%a9mg%EB);;Th-xL`bMW{~@DAZXd7nZHizk}Xq6z<9 z&2fq^-|YWG#vGmJg=@(%NB3WXMo=0fnK8NxfSlykD$Yd=RW&5!ElX zp=I?8asBZSqkLEBIB~5(I{M{_Ch`KyMpxcAU$B14n0fj zOc5q%5kOSomI&UeqLBkC3LT5&#uyOBxFv$9zzwnn7)O><4X%(ikZ+j&*yZDy+NbGO z#=llSxJKm0<){1qtUWq>ZHg{z{Btd6wG2&kEoim8Jgfy-dU*u3=qhU^LK0MsZ4BdI z2hYAMl67~~zx!(bQE><4y(@xP?t(%zQU_}AH(Rg2hF=p&0ic*SbuoY_=FNzT!vKQy z%}BAln*a!7y(!%hf@ql75aD-4@*nL>EVrgSQ5XT%&B``(|sh<|J)8t{g2 z>%_%;HBXCV6pnQ1yn=w~aA!n)8z=}6xieBEy;%^T!p;bmC)5K30Wrayk*>$P1#UzU z(1trB-3GWH(Ax@>2SVCNjaM+5$@eI0T5Fc@F-&Md{(|IC3yLGPWokh_)0U|Pc}-iU z78IFm%hZC=DRvua@lcM^0)&-O^SIawJgbE-ZN-&^-uRN*7FtkyBRSN9Vo)oiDr!fy zplYm)7R!qUf&eX6MiGai5r+uCtD@$Bz&5lI03nn~!KhW4GHJeEm5vt65k<%lLU5N! z3x$k3qvr5HC@p$H2!&Ey>ds7|G&$cHEv6lXY$ICw-WeS_j4tYOv!iF zNgno8X1&P6o{Fk3aRm!RJ{3jab-T6|2y3JM&sXw~Dk%eb*xF2)gUuqEULMSEZ{-p%(1R=ir zzf4rMj*Sp`a(|S^Z{KRZ7rik~w_UQbn*5zNo5lWLwwWFAGTFgx=I`=pB|Pfa#r(KL^C3~1RwqOs!e4|>CS4p!Pt z&HhE7nlU^Ji8U~-Z4+46HYW#(yUkdvfeDLSJo`-@(&!SA3&t!AsAkOUYLQ+h5Yl2f z^l5`l2tCy(!RdY;d|E)fJ6aN<*nq;wUXn*o`zUPS9qjBdXjT-Oojp(T{p&^-iYGM6vxA_xb*a%BqfFo{vFOd;MSG0I_z8BhbGTt(J7{id1@L6X%}F>Kphf@RJ;%YTXG!%QQU=ylm{5uFGN@^z$o%U@oAJ~NAe8idBjfI z&QP8wFr_QU1MVnPo`LpzfEG8D14Hb^{w=%AyUPRu|HX*9>*B~17f0!kP^yRJwsA4O zGNDHa99rg7@swZnxp^9DMz+Buu4*O*i)uugB`Y9AR|l%eaibcUgLH_lMw3dz(5aY_ z3~B}l_GAV-m6i-@#$?TsVa@1}%%~AHI|;hc{WPN{s2MQ$Yl4~qqxc$KGisGuKiX!F z9WGuX!Nb_nal}I;MlJZLJBW8kj9PHfGty23EnUwW{j1u{erX_-P!EqK-=-%@j0RLk zvQd9+z(Q9cO3Rf!OuR>8G@u@m4VAbQx6NfnnnN!|SXv>lU7D{&lKz)=>O9MqPm3)o O-D9?v?y+t?^M3)}Q9027 delta 22655 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 diff --git a/crates/view/src/worker.rs b/crates/view/src/worker.rs index 81d3a6f34f..887ee79a97 100644 --- a/crates/view/src/worker.rs +++ b/crates/view/src/worker.rs @@ -502,6 +502,7 @@ async fn sct_divergence_check( let value = client .key_value(penumbra_proto::cnidarium::v1::KeyValueRequest { key: sct_state_key::tree::anchor_by_height(height), + proof: false, ..Default::default() }) .await? diff --git a/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto b/proto/penumbra/penumbra/cnidarium/v1/cnidarium.proto index b313003731..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,7 +21,25 @@ 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 {