Skip to content

Commit fa9dfa7

Browse files
committed
Add deserialization workaround for validator updates
This introduces a (somewhat hacky, yet temporary) approach to being able to deserialize public keys in validator updates until such time that Tendermint addresses the problem. Signed-off-by: Thane Thomson <[email protected]>
1 parent e170520 commit fa9dfa7

File tree

2 files changed

+98
-34
lines changed

2 files changed

+98
-34
lines changed

tendermint/src/public_key.rs

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ mod pub_key_request;
88
mod pub_key_response;
99
pub use pub_key_request::PubKeyRequest;
1010
pub use pub_key_response::PubKeyResponse;
11+
use serde_json::Value;
1112

1213
use crate::prelude::*;
1314
use crate::{error::Error, signature::Signature};
1415
use core::convert::TryFrom;
1516
use core::{cmp::Ordering, fmt, ops::Deref, str::FromStr};
16-
use serde::{de, ser, Deserialize, Serialize};
17+
use serde::{de, ser, Deserialize, Deserializer, Serialize};
1718
use subtle_encoding::{base64, bech32, hex};
1819
use tendermint_proto::crypto::public_key::Sum;
1920
use tendermint_proto::crypto::PublicKey as RawPublicKey;
@@ -56,6 +57,69 @@ pub enum PublicKey {
5657
Secp256k1(Secp256k1),
5758
}
5859

60+
// Internal thunk type to facilitate deserialization from the raw Protobuf data
61+
// structure's JSON representation.
62+
#[derive(Serialize, Deserialize)]
63+
struct ProtobufPublicKeyWrapper {
64+
#[serde(rename = "Sum")]
65+
sum: ProtobufPublicKey,
66+
}
67+
68+
impl From<ProtobufPublicKeyWrapper> for PublicKey {
69+
fn from(wrapper: ProtobufPublicKeyWrapper) -> Self {
70+
match wrapper.sum {
71+
ProtobufPublicKey::Ed25519 { ed25519 } => PublicKey::Ed25519(ed25519),
72+
#[cfg(feature = "secp256k1")]
73+
ProtobufPublicKey::Secp256k1 { secp256k1 } => PublicKey::Secp256k1(secp256k1),
74+
}
75+
}
76+
}
77+
78+
#[derive(Serialize, Deserialize)]
79+
#[serde(tag = "type", content = "value")] // JSON custom serialization for priv_validator_key.json
80+
enum ProtobufPublicKey {
81+
#[serde(rename = "tendermint.crypto.PublicKey_Ed25519")]
82+
Ed25519 {
83+
#[serde(
84+
serialize_with = "serialize_ed25519_base64",
85+
deserialize_with = "deserialize_ed25519_base64"
86+
)]
87+
ed25519: Ed25519,
88+
},
89+
90+
#[cfg(feature = "secp256k1")]
91+
#[serde(rename = "tendermint.crypto.PublicKey_Secp256K1")]
92+
Secp256k1 {
93+
#[serde(
94+
serialize_with = "serialize_secp256k1_base64",
95+
deserialize_with = "deserialize_secp256k1_base64"
96+
)]
97+
secp256k1: Secp256k1,
98+
},
99+
}
100+
101+
/// Custom deserialization for public keys to handle multiple potential JSON
102+
/// formats from Tendermint.
103+
///
104+
/// See <https://github.com/informalsystems/tendermint-rs/issues/1021> for
105+
/// context.
106+
// TODO(thane): Remove this once the serialization in Tendermint has been fixed.
107+
pub fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
108+
where
109+
D: Deserializer<'de>,
110+
{
111+
let v = Value::deserialize(deserializer)?;
112+
if v.as_object()
113+
.map(|obj| obj.contains_key("Sum"))
114+
.unwrap_or(false)
115+
{
116+
serde_json::from_value::<ProtobufPublicKeyWrapper>(v).map(Into::into)
117+
} else {
118+
serde_json::from_value::<PublicKey>(v)
119+
}
120+
.map_err(serde::de::Error::custom)
121+
}
122+
59123
impl Protobuf<RawPublicKey> for PublicKey {}
60124

61125
impl TryFrom<RawPublicKey> for PublicKey {
@@ -326,7 +390,7 @@ impl Serialize for Algorithm {
326390
}
327391

328392
impl<'de> Deserialize<'de> for Algorithm {
329-
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
393+
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
330394
use de::Error;
331395
let s = String::deserialize(deserializer)?;
332396
s.parse().map_err(D::Error::custom)
@@ -356,7 +420,7 @@ where
356420

357421
fn deserialize_ed25519_base64<'de, D>(deserializer: D) -> Result<Ed25519, D::Error>
358422
where
359-
D: de::Deserializer<'de>,
423+
D: Deserializer<'de>,
360424
{
361425
use de::Error;
362426
let encoded = String::deserialize(deserializer)?;
@@ -367,7 +431,7 @@ where
367431
#[cfg(feature = "secp256k1")]
368432
fn deserialize_secp256k1_base64<'de, D>(deserializer: D) -> Result<Secp256k1, D::Error>
369433
where
370-
D: de::Deserializer<'de>,
434+
D: Deserializer<'de>,
371435
{
372436
use de::Error;
373437
let encoded = String::deserialize(deserializer)?;

tendermint/src/validator.rs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Tendermint validators
22
3-
use serde::{de::Error as _, Deserialize, Deserializer, Serialize};
4-
use subtle_encoding::base64;
3+
use serde::{Deserialize, Serialize};
54

65
use crate::prelude::*;
6+
use crate::public_key::deserialize_public_key;
77
use crate::{account, hash::Hash, merkle, vote, Error, PublicKey, Signature};
88

99
use core::convert::{TryFrom, TryInto};
@@ -326,34 +326,6 @@ pub struct Update {
326326
pub power: vote::Power,
327327
}
328328

329-
/// Validator updates use a slightly different public key format than the one
330-
/// implemented in `tendermint::PublicKey`.
331-
///
332-
/// This is an internal thunk type to parse the `validator_updates` format and
333-
/// then convert to `tendermint::PublicKey` in `deserialize_public_key` below.
334-
#[derive(Serialize, Deserialize)]
335-
#[serde(tag = "type", content = "data")]
336-
enum Pk {
337-
/// Ed25519 keys
338-
#[serde(rename = "ed25519")]
339-
Ed25519(String),
340-
}
341-
342-
fn deserialize_public_key<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
343-
where
344-
D: Deserializer<'de>,
345-
{
346-
match &Pk::deserialize(deserializer)? {
347-
Pk::Ed25519(base64_value) => {
348-
let bytes =
349-
base64::decode(base64_value).map_err(|e| D::Error::custom(format!("{}", e)))?;
350-
351-
PublicKey::from_raw_ed25519(&bytes)
352-
.ok_or_else(|| D::Error::custom("error parsing Ed25519 key"))
353-
}
354-
}
355-
}
356-
357329
#[cfg(test)]
358330
mod tests {
359331

@@ -438,4 +410,32 @@ mod tests {
438410
148_151_478_422_287_875 + 158_095_448_483_785_107 + 770_561_664_770_006_272
439411
);
440412
}
413+
414+
#[test]
415+
fn deserialize_validator_updates() {
416+
const FMT1: &str = r#"{
417+
"pub_key": {
418+
"Sum": {
419+
"type": "tendermint.crypto.PublicKey_Ed25519",
420+
"value": {
421+
"ed25519": "VqJCr3vjQdffcLIG6RMBl2MgXDFYNY6b3Joaa43gV3o="
422+
}
423+
}
424+
},
425+
"power": "573929"
426+
}"#;
427+
const FMT2: &str = r#"{
428+
"pub_key": {
429+
"type": "tendermint/PubKeyEd25519",
430+
"value": "VqJCr3vjQdffcLIG6RMBl2MgXDFYNY6b3Joaa43gV3o="
431+
},
432+
"power": "573929"
433+
}"#;
434+
435+
let update1 = serde_json::from_str::<Update>(FMT1).unwrap();
436+
let update2 = serde_json::from_str::<Update>(FMT2).unwrap();
437+
438+
assert_eq!(u64::from(update1.power), 573929);
439+
assert_eq!(update1, update2);
440+
}
441441
}

0 commit comments

Comments
 (0)