Skip to content

Commit

Permalink
Add ABCI domain types.
Browse files Browse the repository at this point in the history
These types mirror the generated types in tendermint_proto, but have better
naming.  The documentation is filled in from the ABCI methods & types
documentation.
  • Loading branch information
hdevalence committed Sep 9, 2021
1 parent e19fb31 commit 573b895
Show file tree
Hide file tree
Showing 32 changed files with 3,135 additions and 5 deletions.
57 changes: 52 additions & 5 deletions tendermint/src/abci.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,58 @@
//! Application BlockChain Interface (ABCI)
//! Application BlockChain Interface ([ABCI]) is the interface between Tendermint
//! (a consensus engine for Byzantine-fault-tolerant replication of a state
//! machine) and an application (the state machine to be replicated).
//!
//! NOTE: This module contains types for ABCI responses as consumed from RPC
//! endpoints. It does not contain an ABCI protocol implementation.
//! Using ABCI involves writing an application driven by ABCI methods, exposing
//! that application as an ABCI server, and having Tendermint connect to the
//! server as an ABCI client.
//!
//! For that, see:
//! This module does not include an ABCI server implementation itself. Instead,
//! it provides a common set of Rust domain types that model the ABCI protocol,
//! which can be used by both ABCI applications and ABCI server implementations.
//!
//! <https://github.com/tendermint/rust-abci>
//! One ABCI server implementation is provided by the [`tendermint_abci`][tmabci]
//! crate.
//!
//! Each ABCI method corresponds to a request/response pair. ABCI requests are
//! modeled by the [`Request`] enum, and responses are modeled by the
//! [`Response`] enum. As described in the [methods and types][mat] page, ABCI
//! methods are split into four categories. Tendermint opens one ABCI connection
//! for each category of messages. These categories are modeled by the
//! [`MethodKind`] enum and by per-category request and response enums:
//!
//! * [`ConsensusRequest`] / [`ConsensusResponse`] for [`MethodKind::Consensus`] methods;
//! * [`MempoolRequest`] / [`MempoolResponse`] for [`MethodKind::Mempool`] methods;
//! * [`InfoRequest`] / [`InfoResponse`] for [`MethodKind::Info`] methods;
//! * [`SnapshotRequest`] / [`SnapshotResponse`] for [`MethodKind::Snapshot`] methods.
//!
//! The domain types in this module have conversions to and from the Protobuf
//! types defined in the [`tendermint_proto`] crate. These conversions are
//! required for ABCI server implementations, which use the protobufs to
//! communicate with Tendermint, but should not be required for ABCI
//! applications, which should use the domain types in an interface defined by
//! their choice of ABCI server implementation.
//!
//! [ABCI]: https://docs.tendermint.com/master/spec/abci/
//! [mat]: https://docs.tendermint.com/master/spec/abci/abci.html
//! [tmabci]: https://github.com/informalsystems/tendermint-rs/tree/master/abci

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

/// Events. Hide this later once types are merged.
pub mod event;
//pub use event::{Event, EventAttribute};

pub mod params;
pub mod request;
pub mod response;
pub mod types;

pub mod responses;
pub mod tag;
pub mod transaction;
Expand All @@ -27,3 +67,10 @@ pub use self::{
responses::{DeliverTx, Event, Responses},
transaction::Transaction,
};

#[doc(inline)]
pub use self::{
kind::MethodKind,
request::{ConsensusRequest, InfoRequest, MempoolRequest, Request, SnapshotRequest},
response::{ConsensusResponse, InfoResponse, MempoolResponse, Response, SnapshotResponse},
};
182 changes: 182 additions & 0 deletions tendermint/src/abci/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/// An event that occurred while processing a request.
///
/// Application developers can attach additional information to
/// [`BeginBlock`](super::response::BeginBlock),
/// [`EndBlock`](super::response::EndBlock),
/// [`CheckTx`](super::response::CheckTx), and
/// [`DeliverTx`](super::response::DeliverTx) responses. Later, transactions may
/// be queried using these events.
///
/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events)
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Event {
/// The kind of event.
///
/// Tendermint calls this the `type`, but we use `kind` to avoid confusion
/// with Rust types and follow Rust conventions.
pub kind: String,
/// A list of [`EventAttribute`]s describing the event.
pub attributes: Vec<EventAttribute>,
}

impl Event {
/// Construct an event from generic data.
///
/// The `From` impls on [`EventAttribute`] and the [`EventAttributeIndexExt`]
/// trait allow ergonomic event construction, as in this example:
///
/// ```
/// use tendermint::abci::event::{Event, EventAttributeIndexExt};
///
/// let event = Event::new(
/// "app",
/// vec![
/// ("key1", "value1").index(),
/// ("key2", "value2").index(),
/// ("key3", "value3").no_index(), // will not be indexed
/// ],
/// );
/// ```
// XXX(hdevalence): remove vec! from example after https://github.com/rust-lang/rust/pull/65819
pub fn new<K, I>(kind: K, attributes: I) -> Self
where
K: Into<String>,
I: IntoIterator,
I::Item: Into<EventAttribute>,
{
Self {
kind: kind.into(),
attributes: attributes.into_iter().map(Into::into).collect(),
}
}
}

/// A key-value pair describing an [`Event`].
///
/// Generic methods are provided for more ergonomic attribute construction, see
/// [`Event::new`] for details.
///
/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events)
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct EventAttribute {
/// The event key.
pub key: String,
/// The event value.
pub value: String,
/// Whether Tendermint's indexer should index this event.
///
/// **This field is nondeterministic**.
pub index: bool,
}

impl<K: Into<String>, V: Into<String>> From<(K, V, bool)> for EventAttribute {
fn from((key, value, index): (K, V, bool)) -> Self {
EventAttribute {
key: key.into(),
value: value.into(),
index,
}
}
}

/// Adds convenience methods to tuples for more ergonomic [`EventAttribute`]
/// construction.
///
/// See [`Event::new`] for details.
#[allow(missing_docs)]
pub trait EventAttributeIndexExt: private::Sealed {
type Key;
type Value;

/// Indicate that this key/value pair should be indexed by Tendermint.
fn index(self) -> (Self::Key, Self::Value, bool);
/// Indicate that this key/value pair should not be indexed by Tendermint.
fn no_index(self) -> (Self::Key, Self::Value, bool);
}

impl<K: Into<String>, V: Into<String>> EventAttributeIndexExt for (K, V) {
type Key = K;
type Value = V;
fn index(self) -> (K, V, bool) {
let (key, value) = self;
(key, value, true)
}
fn no_index(self) -> (K, V, bool) {
let (key, value) = self;
(key, value, false)
}
}

mod private {
pub trait Sealed {}

impl<K: Into<String>, V: Into<String>> Sealed for (K, V) {}
}

impl<K: Into<String>, V: Into<String>> From<(K, V)> for EventAttribute {
fn from((key, value): (K, V)) -> Self {
(key, value, false).into()
}
}

// =============================================================================
// Protobuf conversions
// =============================================================================

// XXX(hdevalence): these all use &'static str for now, this should be fixed
// to align with the crate's error-handling strategy.

use std::convert::{TryFrom, TryInto};

use tendermint_proto::abci as pb;
use tendermint_proto::Protobuf;

impl From<EventAttribute> for pb::EventAttribute {
fn from(event: EventAttribute) -> Self {
Self {
key: event.key,
value: event.value,
index: event.index,
}
}
}

impl TryFrom<pb::EventAttribute> for EventAttribute {
type Error = crate::Error;

fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
Ok(Self {
key: event.key,
value: event.value,
index: event.index,
})
}
}

impl Protobuf<pb::EventAttribute> for EventAttribute {}

impl From<Event> for pb::Event {
fn from(event: Event) -> Self {
Self {
r#type: event.kind,
attributes: event.attributes.into_iter().map(Into::into).collect(),
}
}
}

impl TryFrom<pb::Event> for Event {
type Error = crate::Error;

fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
Ok(Self {
kind: event.r#type,
attributes: event
.attributes
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
}

impl Protobuf<pb::Event> for Event {}
23 changes: 23 additions & 0 deletions tendermint/src/abci/kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/// A category of ABCI method.
///
/// ABCI methods are split into four categories. Tendermint opens one ABCI
/// connection for each category and refers to these categories as *connections*,
/// but nothing actually restricts an ABCI connection from calling methods in
/// multiple categories.
///
/// This enum breaks out the `Flush` method as a distinct category, since it is
/// used to control the execution of other methods.
pub enum MethodKind {
/// A consensus method, driven by the consensus protocol and responsible for
/// block execution.
Consensus,
/// A mempool method, used for validating new transactions before they're
/// shared or included in a block.
Mempool,
/// A snapshot method, used for serving and restoring state snapshots.
Snapshot,
/// An info method, used for initialization and user queries.
Info,
/// The flush method requests that all pending method requests are fully executed.
Flush,
}
Loading

0 comments on commit 573b895

Please sign in to comment.