Skip to content

Commit

Permalink
rpc: Fix AppHash decoding failure in /block_results response (#1449)
Browse files Browse the repository at this point in the history
* If AppHash decoding fails, try with decoding with base64

* Add changelog entry

* Update .changelog/unreleased/improvements/1449-allow-base64-apphash.md

Co-authored-by: Romain Ruetschi <romain@informal.systems>

---------

Co-authored-by: Romain Ruetschi <romain@informal.systems>
  • Loading branch information
ljoss17 and romac authored Jul 23, 2024
1 parent 981bc34 commit 58ff766
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `[tendermint-rpc]` If `AppHash` fails to decode as hex, try to decode it as base64.
([\#1449](https://github.com/informalsystems/tendermint-rs/issues/1449))
56 changes: 54 additions & 2 deletions tendermint/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use core::{

use bytes::Bytes;
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use subtle_encoding::{Encoding, Hex};
use subtle_encoding::{base64, Encoding, Hex};
use tendermint_proto::Protobuf;

use crate::{error::Error, prelude::*};
Expand Down Expand Up @@ -253,6 +253,12 @@ impl AppHash {
.map_err(Error::subtle_encoding)?;
Ok(AppHash(h))
}

/// Decode a `Hash` from base64-encoded string
pub fn from_base64(s: &str) -> Result<Self, Error> {
let h = base64::decode(s).map_err(Error::subtle_encoding)?;
Ok(AppHash(h))
}
}

impl AsRef<[u8]> for AppHash {
Expand Down Expand Up @@ -285,6 +291,52 @@ impl FromStr for AppHash {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
Self::from_hex_upper(s)
Self::from_hex_upper(s).or_else(|_| Self::from_base64(s))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[derive(Debug, serde::Deserialize)]
struct Test {
#[serde(default)]
#[serde(with = "crate::serializers::apphash")]
pub app_hash: AppHash,
}

#[test]
fn apphash_decode_base64() {
let test = serde_json::from_str::<Test>(
r#"{"app_hash":"MfX9f+bYoI8IioRb4YT/8/VhPvtNjgWFgTi4mmMSkBc="}"#,
)
.unwrap();

assert_eq!(
test.app_hash.as_ref(),
&[
0x31, 0xF5, 0xFD, 0x7F, 0xE6, 0xD8, 0xA0, 0x8F, 0x08, 0x8A, 0x84, 0x5B, 0xE1, 0x84,
0xFF, 0xF3, 0xF5, 0x61, 0x3E, 0xFB, 0x4D, 0x8E, 0x05, 0x85, 0x81, 0x38, 0xB8, 0x9A,
0x63, 0x12, 0x90, 0x17
]
);
}

#[test]
fn apphash_decode_hex() {
let test = serde_json::from_str::<Test>(
r#"{"app_hash":"31F5FD7FE6D8A08F088A845BE184FFF3F5613EFB4D8E05858138B89A63129017"}"#,
)
.unwrap();

assert_eq!(
test.app_hash.as_ref(),
&[
0x31, 0xF5, 0xFD, 0x7F, 0xE6, 0xD8, 0xA0, 0x8F, 0x08, 0x8A, 0x84, 0x5B, 0xE1, 0x84,
0xFF, 0xF3, 0xF5, 0x61, 0x3E, 0xFB, 0x4D, 0x8E, 0x05, 0x85, 0x81, 0x38, 0xB8, 0x9A,
0x63, 0x12, 0x90, 0x17
]
);
}
}
11 changes: 5 additions & 6 deletions tendermint/src/serializers/apphash.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! AppHash serialization with validation

use core::str::FromStr;

use alloc::borrow::Cow;

use serde::{de, ser, Deserialize, Deserializer, Serializer};
use subtle_encoding::hex;
use serde::{de, Deserialize, Deserializer, Serializer};

use crate::{prelude::*, AppHash};

Expand All @@ -13,16 +14,14 @@ where
D: Deserializer<'de>,
{
let hexstring = Option::<Cow<'_, str>>::deserialize(deserializer)?.unwrap_or(Cow::Borrowed(""));
AppHash::from_hex_upper(&hexstring).map_err(de::Error::custom)
AppHash::from_str(&hexstring).map_err(de::Error::custom)
}

/// Serialize from AppHash into hexstring
pub fn serialize<S>(value: &AppHash, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let hex_bytes = hex::encode_upper(value.as_ref());
let hex_string = String::from_utf8(hex_bytes).map_err(ser::Error::custom)?;
// Serialize as Option<String> for symmetry with deserialize
serializer.serialize_some(&hex_string)
serializer.serialize_some(&value.to_string())
}

0 comments on commit 58ff766

Please sign in to comment.