Skip to content

Commit

Permalink
Decouple RPC from ABCI domain types.
Browse files Browse the repository at this point in the history
The original contents of `tendermint::abci` were created only to model RPC
responses, not to model ABCI itself. The old types are shaped to fit the
details of the JSON encoding used by the RPC. Eventually, they should be
eliminated and merged with the new ABCI domain types. Moving them to the RPC
crate in the meantime disentangles that work from the work of finishing the
modeling of the ABCI domain types.
  • Loading branch information
hdevalence committed Sep 9, 2021
1 parent 70122e2 commit e19fb31
Show file tree
Hide file tree
Showing 20 changed files with 848 additions and 62 deletions.
1 change: 1 addition & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pin-project = "1.0.1"
serde = { version = "1", features = [ "derive" ] }
serde_bytes = "0.11"
serde_json = "1"
subtle = "2"
tendermint = { version = "0.21.0", path = "../tendermint" }
tendermint-proto = { version = "0.21.0", path = "../proto" }
thiserror = "1"
Expand Down
29 changes: 29 additions & 0 deletions rpc/src/abci.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Old ABCI structures, formerly defined in `tendermint::abci`.
//!
//! The original contents of `tendermint::abci` were created only to model RPC
//! responses, not to model ABCI itself. The old types should be eliminated and
//! merged with the new ABCI domain types. Moving them here in the meantime
//! disentangles improving the ABCI domain modeling from changes to the RPC
//! interface.

mod code;
mod data;
mod gas;
mod info;
mod log;
mod path;

pub mod responses;
pub mod tag;
pub mod transaction;

pub use self::{
code::Code,
data::Data,
gas::Gas,
info::Info,
log::Log,
path::Path,
responses::{DeliverTx, Event, Responses},
transaction::Transaction,
};
106 changes: 106 additions & 0 deletions rpc/src/abci/code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use serde::de::{Deserialize, Deserializer, Visitor};
use serde::{Serialize, Serializer};
use std::fmt;
use std::num::NonZeroU32;

/// ABCI application response codes.
///
/// These presently use 0 for success and non-zero for errors:
///
/// <https://tendermint.com/docs/spec/abci/abci.html#errors>
///
/// Note that in the future there may potentially be non-zero success codes.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum Code {
/// Success
Ok,

/// Error codes
Err(NonZeroU32),
}

impl Default for Code {
fn default() -> Code {
Code::Ok
}
}

impl Code {
/// Was the response OK?
pub fn is_ok(self) -> bool {
match self {
Code::Ok => true,
Code::Err(_) => false,
}
}

/// Was the response an error?
pub fn is_err(self) -> bool {
!self.is_ok()
}

/// Get the integer error value for this code
pub fn value(self) -> u32 {
u32::from(self)
}
}

impl From<u32> for Code {
fn from(value: u32) -> Code {
match NonZeroU32::new(value) {
Some(value) => Code::Err(value),
None => Code::Ok,
}
}
}

impl From<Code> for u32 {
fn from(code: Code) -> u32 {
match code {
Code::Ok => 0,
Code::Err(err) => err.get(),
}
}
}

impl Serialize for Code {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.value().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for Code {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct CodeVisitor;

impl<'de> Visitor<'de> for CodeVisitor {
type Value = Code;

fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str("integer or string")
}

fn visit_u64<E>(self, val: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Code::from(val as u32))
}

fn visit_str<E>(self, val: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match val.parse::<u64>() {
Ok(val) => self.visit_u64(val),
Err(_) => Err(E::custom("failed to parse integer")),
}
}
}

deserializer.deserialize_any(CodeVisitor)
}
}
58 changes: 58 additions & 0 deletions rpc/src/abci/data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use serde::{Deserialize, Serialize};

/// ABCI transaction data.
///
/// Transactions are opaque binary blobs which are validated according to
/// application-specific rules.
#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Data(#[serde(with = "tendermint::serializers::bytes::base64string")] Vec<u8>);

impl From<Vec<u8>> for Data {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}

impl From<Data> for Vec<u8> {
fn from(value: Data) -> Self {
value.0
}
}

impl Data {
/// Get value
pub fn value(&self) -> &Vec<u8> {
&self.0
}
}

#[cfg(test)]
mod tests {
use crate::abci::Data;

#[test]
fn test_deserialization() {
let json = "\"ChYKFGNvbm5lY3Rpb25fb3Blbl9pbml0\"";
let mydata: Data = serde_json::from_str(json).unwrap();
assert_eq!(
mydata.0,
vec![
// By chance this is a protobuf struct.
10, // Field 1 is a String
22, // Field 1 length is 22
10, // Sub-field 1 is String
20, // Sub-field 1 length is 20
99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 95, 111, 112, 101, 110, 95, 105,
110, 105, 116 // "connection_open_init"
]
);
}

#[test]
fn test_serialization() {
let mydata: Data = vec![1, 2, 3, 4].into();
let json = serde_json::to_string(&mydata).unwrap();
assert_eq!(json, "\"AQIDBA==\"");
}
}
63 changes: 63 additions & 0 deletions rpc/src/abci/gas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Gas: abstract representation for the cost of resources used by nodes when
//! processing transactions.
//!
//! For more information, see:
//!
//! <https://tendermint.com/docs/spec/abci/apps.html#gas>

use tendermint::{Error, Kind};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{self, Display},
str::FromStr,
};

/// Gas: representation of transaction processing resource costs
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub struct Gas(u64);

impl Gas {
/// Get the inner integer value
pub fn value(self) -> u64 {
self.0
}
}

impl From<u64> for Gas {
fn from(amount: u64) -> Gas {
Gas(amount)
}
}

impl From<Gas> for u64 {
fn from(gas: Gas) -> u64 {
gas.0
}
}

impl Display for Gas {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl FromStr for Gas {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
Ok(Self::from(s.parse::<u64>().map_err(|_| Kind::Parse)?))
}
}

impl<'de> Deserialize<'de> for Gas {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Self::from_str(&String::deserialize(deserializer)?)
.map_err(|e| D::Error::custom(format!("{}", e)))
}
}

impl Serialize for Gas {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}
24 changes: 24 additions & 0 deletions rpc/src/abci/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};

/// ABCI info
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Info(String);

impl AsRef<str> for Info {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}

impl Display for Info {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl Default for Info {
fn default() -> Self {
Self(String::new())
}
}
33 changes: 33 additions & 0 deletions rpc/src/abci/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;

/// ABCI log data
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(transparent)]
pub struct Log(String);

impl Log {
/// Convenience function: get value
pub fn value(&self) -> &String {
&self.0
}
}

impl From<&str> for Log {
fn from(s: &str) -> Self {
Log(s.to_owned())
}
}

impl AsRef<str> for Log {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}

impl Display for Log {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
26 changes: 26 additions & 0 deletions rpc/src/abci/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! Paths to ABCI data

use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::{
fmt::{self, Display},
str::FromStr,
};

/// Path to ABCI data
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Path(String);

impl Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.0)
}
}

impl FromStr for Path {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
Ok(Path(s.to_owned()))
}
}
Loading

0 comments on commit e19fb31

Please sign in to comment.