Skip to content

Commit

Permalink
Automatically de/serialize ABCI event attributes from/to base64 (#718)
Browse files Browse the repository at this point in the history
* Add defaults for structs used by deliver/check-tx results

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add base64 en/decoding for key and value fields of tag struct

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Extend TxResult from RPC to cover full range of returned fields for broadcast_tx_commit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Expand testing to ensure correct decoding of event attributes

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Update CHANGELOG

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Move base64 -> String deserialization to proto crate

Signed-off-by: Thane Thomson <connect@thanethomson.com>
  • Loading branch information
thanethomson committed Dec 4, 2020
1 parent 090b3d2 commit 68bb2e8
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
- `[rpc]` The `WebSocketClient` now adds support for all remaining RPC requests
by way of implementing the `Client` trait ([#646])
- `[rpc]` Support for the `tx_search` RPC endpoint has been added ([#701])
- `[rpc]` Responses that include events now automatically have their tag
key/value pairs decoded from base64, where previously tag key/value pairs
were Base64-encoded ([#717])
- `[tendermint]` (Since v0.17.0-rc3) Bech32 encoding fix ([#690])

[#425]: https://github.com/informalsystems/tendermint-rs/issues/425
[#646]: https://github.com/informalsystems/tendermint-rs/pull/646
[#690]: https://github.com/informalsystems/tendermint-rs/issues/690
[#701]: https://github.com/informalsystems/tendermint-rs/pull/701
[#717]: https://github.com/informalsystems/tendermint-rs/issues/717

## v0.17.0-rc3

Expand Down
10 changes: 10 additions & 0 deletions proto/src/serializers/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ pub mod base64string {
base64::decode(&string).map_err(serde::de::Error::custom)
}

/// Deserialize base64string into String
pub fn deserialize_to_string<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let s = Option::<String>::deserialize(deserializer)?.unwrap_or_default();
String::from_utf8(base64::decode(&s).map_err(serde::de::Error::custom)?)
.map_err(serde::de::Error::custom)
}

/// Serialize from T into base64string
pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
Expand Down
23 changes: 23 additions & 0 deletions rpc/src/endpoint/broadcast/tx_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

use serde::{Deserialize, Serialize};

use tendermint::abci::responses::Codespace;
use tendermint::abci::{Event, Gas, Info};
use tendermint::{
abci::{transaction, Code, Data, Log, Transaction},
block,
Expand Down Expand Up @@ -65,5 +67,26 @@ pub struct TxResult {
pub data: Option<Data>,

/// Log
#[serde(default)]
pub log: Log,

/// ABCI info (nondeterministic)
#[serde(default)]
pub info: Info,

/// Amount of gas wanted
#[serde(default, rename = "gasWanted")]
pub gas_wanted: Gas,

/// Amount of gas used
#[serde(default, rename = "gasUsed")]
pub gas_used: Gas,

/// Events
#[serde(default)]
pub events: Vec<Event>,

/// Codespace
#[serde(default)]
pub codespace: Codespace,
}
14 changes: 14 additions & 0 deletions rpc/tests/parse_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ fn block_results() {

assert_eq!(deliver_tx[0].gas_wanted.value(), 200_000);
assert_eq!(deliver_tx[0].gas_used.value(), 105_662);
assert_eq!(deliver_tx[0].events.len(), 1);
assert_eq!(deliver_tx[0].events[0].attributes.len(), 3);
assert_eq!(deliver_tx[0].events[0].attributes[0].key.as_ref(), "action");
assert_eq!(
deliver_tx[0].events[0].attributes[0].value.as_ref(),
"delegate"
);

assert_eq!(validator_updates[0].power.value(), 1_233_243);
}
Expand Down Expand Up @@ -194,6 +201,7 @@ fn broadcast_tx_commit() {
&response.hash.to_string(),
"EFA00D85332A8197CF290E4724BAC877EA93DDFE547A561828BAE45A29BF1DAD"
);
assert_eq!(5, response.deliver_tx.events.len());
}

#[test]
Expand Down Expand Up @@ -337,4 +345,10 @@ fn tx_search_with_prove() {
],
proof.root_hash
);

let events = &response.txs[0].tx_result.events;
assert_eq!(events.len(), 1);
assert_eq!(events[0].attributes.len(), 4);
assert_eq!(events[0].attributes[0].key.as_ref(), "creator");
assert_eq!(events[0].attributes[0].value.as_ref(), "Cosmoshi Netowoko");
}
6 changes: 6 additions & 0 deletions tendermint/src/abci/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ impl Display for Info {
write!(f, "{}", self.0)
}
}

impl Default for Info {
fn default() -> Self {
Self(String::new())
}
}
6 changes: 6 additions & 0 deletions tendermint/src/abci/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,9 @@ impl Display for Codespace {
write!(f, "{}", self.0)
}
}

impl Default for Codespace {
fn default() -> Self {
Self(String::new())
}
}
30 changes: 28 additions & 2 deletions tendermint/src/abci/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
use tendermint_proto::serializers::bytes::base64string;

/// Tags
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
Expand All @@ -16,7 +17,13 @@ pub struct Tag {

/// Tag keys
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
pub struct Key(String);
pub struct Key(
#[serde(
serialize_with = "base64string::serialize",
deserialize_with = "base64string::deserialize_to_string"
)]
String,
);

impl AsRef<str> for Key {
fn as_ref(&self) -> &str {
Expand All @@ -40,7 +47,13 @@ impl fmt::Display for Key {

/// Tag values
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Value(String);
pub struct Value(
#[serde(
serialize_with = "base64string::serialize",
deserialize_with = "base64string::deserialize_to_string"
)]
String,
);

impl AsRef<str> for Value {
fn as_ref(&self) -> &str {
Expand All @@ -61,3 +74,16 @@ impl fmt::Display for Value {
write!(f, "{}", &self.0)
}
}

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

#[test]
fn tag_serde() {
let json = r#"{"key": "cGFja2V0X3RpbWVvdXRfaGVpZ2h0", "value": "MC00ODQw"}"#;
let tag: Tag = serde_json::from_str(&json).unwrap();
assert_eq!("packet_timeout_height", tag.key.0);
assert_eq!("0-4840", tag.value.0);
}
}

0 comments on commit 68bb2e8

Please sign in to comment.