Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: block_results RPC endpoint changes from #1061 #1086

Merged
merged 3 commits into from
Jan 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/unreleased/workarounds/1021-rpc-block_results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- `[tendermint-rpc]` Allow deserialization of public keys from validator updates
from `block_results` endpoint in multiple JSON formats until this is fixed in
Tendermint
([#1021](https://github.com/informalsystems/tendermint-rs/issues/1021))
8 changes: 7 additions & 1 deletion rpc/src/request.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! JSON-RPC requests

use super::{Id, Method, Version};
use crate::prelude::*;
use crate::{prelude::*, Error};
use core::fmt::Debug;
use serde::{de::DeserializeOwned, Deserialize, Serialize};

Expand All @@ -17,6 +17,12 @@ pub trait Request: Debug + DeserializeOwned + Serialize + Sized + Send {
fn into_json(self) -> String {
Wrapper::new(self).into_json()
}

/// Parse a JSON-RPC request from a JSON string.
fn from_string(s: impl AsRef<[u8]>) -> Result<Self, Error> {
let wrapper: Wrapper<Self> = serde_json::from_slice(s.as_ref()).map_err(Error::serde)?;
Ok(wrapper.params)
}
}

/// Simple JSON-RPC requests which correlate with a single response from the
Expand Down
72 changes: 68 additions & 4 deletions tendermint/src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ mod pub_key_request;
mod pub_key_response;
pub use pub_key_request::PubKeyRequest;
pub use pub_key_response::PubKeyResponse;
use serde_json::Value;

use crate::prelude::*;
use crate::{error::Error, signature::Signature};
use core::convert::TryFrom;
use core::{cmp::Ordering, fmt, ops::Deref, str::FromStr};
use serde::{de, ser, Deserialize, Serialize};
use serde::{de, ser, Deserialize, Deserializer, Serialize};
use subtle_encoding::{base64, bech32, hex};
use tendermint_proto::crypto::public_key::Sum;
use tendermint_proto::crypto::PublicKey as RawPublicKey;
Expand Down Expand Up @@ -56,6 +57,69 @@ pub enum PublicKey {
Secp256k1(Secp256k1),
}

// Internal thunk type to facilitate deserialization from the raw Protobuf data
// structure's JSON representation.
#[derive(Serialize, Deserialize)]
struct ProtobufPublicKeyWrapper {
#[serde(rename = "Sum")]
sum: ProtobufPublicKey,
}

impl From<ProtobufPublicKeyWrapper> for PublicKey {
fn from(wrapper: ProtobufPublicKeyWrapper) -> Self {
match wrapper.sum {
ProtobufPublicKey::Ed25519 { ed25519 } => PublicKey::Ed25519(ed25519),
#[cfg(feature = "secp256k1")]
ProtobufPublicKey::Secp256k1 { secp256k1 } => PublicKey::Secp256k1(secp256k1),
}
}
}

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "value")] // JSON custom serialization for priv_validator_key.json
enum ProtobufPublicKey {
#[serde(rename = "tendermint.crypto.PublicKey_Ed25519")]
Ed25519 {
#[serde(
serialize_with = "serialize_ed25519_base64",
deserialize_with = "deserialize_ed25519_base64"
)]
ed25519: Ed25519,
},

#[cfg(feature = "secp256k1")]
#[serde(rename = "tendermint.crypto.PublicKey_Secp256K1")]
Secp256k1 {
#[serde(
serialize_with = "serialize_secp256k1_base64",
deserialize_with = "deserialize_secp256k1_base64"
)]
secp256k1: Secp256k1,
},
}

/// Custom deserialization for public keys to handle multiple potential JSON
/// formats from Tendermint.
///
/// See <https://github.com/informalsystems/tendermint-rs/issues/1021> for
/// context.
// TODO(thane): Remove this once the serialization in Tendermint has been fixed.
pub fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
where
D: Deserializer<'de>,
{
let v = Value::deserialize(deserializer)?;
if v.as_object()
.map(|obj| obj.contains_key("Sum"))
.unwrap_or(false)
{
serde_json::from_value::<ProtobufPublicKeyWrapper>(v).map(Into::into)
} else {
serde_json::from_value::<PublicKey>(v)
}
.map_err(serde::de::Error::custom)
}

impl Protobuf<RawPublicKey> for PublicKey {}

impl TryFrom<RawPublicKey> for PublicKey {
Expand Down Expand Up @@ -326,7 +390,7 @@ impl Serialize for Algorithm {
}

impl<'de> Deserialize<'de> for Algorithm {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use de::Error;
let s = String::deserialize(deserializer)?;
s.parse().map_err(D::Error::custom)
Expand Down Expand Up @@ -356,7 +420,7 @@ where

fn deserialize_ed25519_base64<'de, D>(deserializer: D) -> Result<Ed25519, D::Error>
where
D: de::Deserializer<'de>,
D: Deserializer<'de>,
{
use de::Error;
let encoded = String::deserialize(deserializer)?;
Expand All @@ -367,7 +431,7 @@ where
#[cfg(feature = "secp256k1")]
fn deserialize_secp256k1_base64<'de, D>(deserializer: D) -> Result<Secp256k1, D::Error>
where
D: de::Deserializer<'de>,
D: Deserializer<'de>,
{
use de::Error;
let encoded = String::deserialize(deserializer)?;
Expand Down
60 changes: 30 additions & 30 deletions tendermint/src/validator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Tendermint validators

use serde::{de::Error as _, Deserialize, Deserializer, Serialize};
use subtle_encoding::base64;
use serde::{Deserialize, Serialize};

use crate::prelude::*;
use crate::public_key::deserialize_public_key;
use crate::{account, hash::Hash, merkle, vote, Error, PublicKey, Signature};

use core::convert::{TryFrom, TryInto};
Expand Down Expand Up @@ -326,34 +326,6 @@ pub struct Update {
pub power: vote::Power,
}

/// Validator updates use a slightly different public key format than the one
/// implemented in `tendermint::PublicKey`.
///
/// This is an internal thunk type to parse the `validator_updates` format and
/// then convert to `tendermint::PublicKey` in `deserialize_public_key` below.
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
enum Pk {
/// Ed25519 keys
#[serde(rename = "ed25519")]
Ed25519(String),
}

fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
where
D: Deserializer<'de>,
{
match &Pk::deserialize(deserializer)? {
Pk::Ed25519(base64_value) => {
let bytes =
base64::decode(base64_value).map_err(|e| D::Error::custom(format!("{}", e)))?;

PublicKey::from_raw_ed25519(&bytes)
.ok_or_else(|| D::Error::custom("error parsing Ed25519 key"))
}
}
}

#[cfg(test)]
mod tests {

Expand Down Expand Up @@ -438,4 +410,32 @@ mod tests {
148_151_478_422_287_875 + 158_095_448_483_785_107 + 770_561_664_770_006_272
);
}

#[test]
fn deserialize_validator_updates() {
const FMT1: &str = r#"{
"pub_key": {
"Sum": {
"type": "tendermint.crypto.PublicKey_Ed25519",
"value": {
"ed25519": "VqJCr3vjQdffcLIG6RMBl2MgXDFYNY6b3Joaa43gV3o="
}
}
},
"power": "573929"
}"#;
const FMT2: &str = r#"{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "VqJCr3vjQdffcLIG6RMBl2MgXDFYNY6b3Joaa43gV3o="
},
"power": "573929"
}"#;

let update1 = serde_json::from_str::<Update>(FMT1).unwrap();
let update2 = serde_json::from_str::<Update>(FMT2).unwrap();

assert_eq!(u64::from(update1.power), 573929);
assert_eq!(update1, update2);
}
}