Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minimal blocking ABCI crate #794

Merged
merged 28 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cbb855f
Add minimal blocking ABCI library
thanethomson Jan 28, 2021
f766e16
Expand API to implement in-memory key/value store app
thanethomson Jan 28, 2021
247e054
Add kvstore-rs ABCI app
thanethomson Jan 28, 2021
14a5cd1
Add rudimentary README
thanethomson Jan 28, 2021
1f1ce87
Merge latest changes from master
thanethomson Jan 29, 2021
de60b30
Bump proto version dependency to v0.18.0
thanethomson Jan 29, 2021
71fb034
Replace manual default structs with Default::default()
thanethomson Jan 29, 2021
569f6ae
Enable debug logging for all incoming ABCI requests
thanethomson Jan 29, 2021
4497249
Improve CLI UX
thanethomson Jan 29, 2021
e877c90
Allow for read buffer size customization
thanethomson Jan 29, 2021
d4eaa56
Add crate description
thanethomson Feb 8, 2021
7cee7a9
Update README for ABCI crate
thanethomson Feb 8, 2021
b429e5b
Add ABCI integration test for minimal ABCI crate (#797)
thanethomson Feb 9, 2021
e237a9a
Merge latest changes from master
thanethomson Feb 9, 2021
7e106f8
Update abci/src/codec.rs
thanethomson Feb 9, 2021
88f3247
Apply suggestion from https://github.com/informalsystems/tendermint-r…
thanethomson Feb 9, 2021
a7d2903
Refactor error handing and expose eyre::Result as crate default Resul…
thanethomson Feb 9, 2021
98d0368
Refactor to use tracing instead of log
thanethomson Feb 9, 2021
7950597
Add newline
thanethomson Feb 11, 2021
ad2501a
Remove comment relating to constraints on Codec struct params
thanethomson Feb 11, 2021
d935482
Merge latest changes from master
thanethomson Feb 18, 2021
fefa9bc
Version tendermint-abci crate in line with other tendermint-rs crates
thanethomson Feb 18, 2021
723c506
Update CHANGELOG
thanethomson Feb 18, 2021
a77b8da
Expand crate documentation
thanethomson Feb 18, 2021
17ee9d9
Extract request dispatch functionality from Application trait
thanethomson Feb 18, 2021
a30d19d
Move ABCI server example to crate root
thanethomson Feb 18, 2021
56a339f
Fix broken link in docs
thanethomson Feb 18, 2021
5f8cbe2
Replace EchoApp example with KeyValueStoreApp example
thanethomson Feb 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
build-all = "build --workspace --all-targets --"
build-wasm-tendermint = "build -p tendermint --manifest-path tendermint/Cargo.toml --target wasm32-unknown-unknown --release --no-default-features --"
build-wasm-light-client = "build -p tendermint-light-client --manifest-path light-client/Cargo.toml --target wasm32-unknown-unknown --release --no-default-features --"
build-abci = "build --manifest-path abci/Cargo.toml --bin kvstore-rs --features binary,kvstore-app"
test-all-features = "test --all-features --no-fail-fast"
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Cargo
# will have compiled files and executables
/target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# These are log files emitted by model-based tests
**/*.log

# RPC probe results
/rpc-probe/probe-results/

# Proptest regressions dumps
**/*.proptest-regressions
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## Unreleased

### FEATURES

* `[tendermint-abci]` Release minimal framework for building ABCI applications
in Rust ([#794])

[#794]: https://github.com/informalsystems/tendermint-rs/pull/794

## v0.18.1

*Feb 10, 2021*
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]

members = [
"abci",
"light-client",
"light-node",
"p2p",
Expand Down
33 changes: 33 additions & 0 deletions abci/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "tendermint-abci"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name = "tendermint-abci"
name = "abci"

it can be called just this and published as a new breaking version of https://crates.io/crates/abci -- @xla @liamsi can help with that

version = "0.18.1"
authors = ["Thane Thomson <thane@informal.systems>"]
edition = "2018"
description = """
tendermint-abci provides a simple framework with which to build low-level
applications on top of Tendermint.
"""

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bin]]
name = "kvstore-rs"
path = "src/application/kvstore/main.rs"
required-features = [ "binary", "kvstore-app" ]

[features]
client = []
echo-app = []
kvstore-app = []
binary = [ "structopt", "tracing-subscriber" ]

[dependencies]
bytes = "1.0"
eyre = "0.6"
prost = "0.7"
tendermint-proto = { version = "0.18.0", path = "../proto" }
thiserror = "1.0"
tracing = "0.1"

structopt = { version = "0.3", optional = true }
tracing-subscriber = { version = "0.2", optional = true }
116 changes: 116 additions & 0 deletions abci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
## tendermint-abci

[![Crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Build Status][build-image]][build-link]
[![Audit Status][audit-image]][audit-link]
[![Apache 2.0 Licensed][license-image]][license-link]
![Rust Stable][rustc-image]

[ABCI] framework for building low-level applications for Tendermint in Rust.

## Requirements

- The latest stable version of Rust

## API

At present, this crate only exposes a synchronous, blocking API based on Rust's
standard library's networking capabilities. `async` client/server support is
planned in future updates.

The primary trait to be implemented by an ABCI application is the
[`Application`] trait. One of the core ideas here is that an ABCI application
must be able to be cloned for use in different threads, since Tendermint opens
4 connections to the ABCI server. See the [spec][tendermint-abci-spec] for
details.

## Examples

See [`src/application`](./src/application/) for some example applications
written using this crate.

To run the key/value store example application, from the `tendermint-abci`
crate's directory:

```bash
# Set your logging level through RUST_LOG (e.g. RUST_LOG=info)
# Binds to 127.0.0.1:26658
RUST_LOG=debug cargo run --bin kvstore-rs --features binary,kvstore-app

# Reset and run your Tendermint node (binds RPC to 127.0.0.1:26657 by default)
tendermint unsafe_reset_all && tendermint start

# Submit a key/value pair (set "somekey" to "somevalue")
curl 'http://127.0.0.1:26657/broadcast_tx_async?tx="somekey=somevalue"'

#{
# "jsonrpc": "2.0",
# "id": -1,
# "result": {
# "code": 0,
# "data": "",
# "log": "",
# "codespace": "",
# "hash": "17ED61261A5357FEE7ACDE4FAB154882A346E479AC236CFB2F22A2E8870A9C3D"
# }
#}

# Query for the value we just submitted ("736f6d656b6579" is the hex
# representation of "somekey")
curl 'http://127.0.0.1:26657/abci_query?data=0x736f6d656b6579'

#{
# "jsonrpc": "2.0",
# "id": -1,
# "result": {
# "response": {
# "code": 0,
# "log": "exists",
# "info": "",
# "index": "0",
# "key": "c29tZWtleQ==",
# "value": "c29tZXZhbHVl",
# "proofOps": null,
# "height": "189",
# "codespace": ""
# }
# }
#}
```

## License

Copyright © 2021 Informal Systems

Licensed under the Apache License, Version 2.0 (the "License");
you may not use the files in this repository except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

[//]: # (badges)

[crate-image]: https://img.shields.io/crates/v/tendermint-abci.svg
[crate-link]: https://crates.io/crates/tendermint-abci
[docs-image]: https://docs.rs/tendermint-abci/badge.svg
[docs-link]: https://docs.rs/tendermint-abci/
[build-image]: https://github.com/informalsystems/tendermint-rs/workflows/Rust/badge.svg
[build-link]: https://github.com/informalsystems/tendermint-rs/actions?query=workflow%3ARust
[audit-image]: https://github.com/informalsystems/tendermint-rs/workflows/Audit-Check/badge.svg
[audit-link]: https://github.com/informalsystems/tendermint-rs/actions?query=workflow%3AAudit-Check
[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
[license-link]: https://github.com/informalsystems/tendermint-rs/blob/master/LICENSE
[rustc-image]: https://img.shields.io/badge/rustc-stable-blue.svg

[//]: # (general links)

[ABCI]: https://docs.tendermint.com/master/spec/abci/
[`Application`]: ./src/application.rs
[tendermint-abci-spec]: https://github.com/tendermint/spec/blob/master/spec/abci/abci.md
148 changes: 148 additions & 0 deletions abci/src/application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//! ABCI application interface.

#[cfg(feature = "echo-app")]
pub mod echo;
#[cfg(feature = "kvstore-app")]
pub mod kvstore;

use tendermint_proto::abci::request::Value;
use tendermint_proto::abci::{
response, Request, RequestApplySnapshotChunk, RequestBeginBlock, RequestCheckTx,
RequestDeliverTx, RequestEcho, RequestEndBlock, RequestInfo, RequestInitChain,
RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestQuery, RequestSetOption, Response,
ResponseApplySnapshotChunk, ResponseBeginBlock, ResponseCheckTx, ResponseCommit,
ResponseDeliverTx, ResponseEcho, ResponseEndBlock, ResponseFlush, ResponseInfo,
ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot,
ResponseQuery, ResponseSetOption,
};

/// An ABCI application.
///
/// Applications are `Send` + `Clone` + `'static` because they are cloned for
/// each incoming connection to the ABCI [`Server`]. It is up to the
/// application developer to manage shared state between these clones of their
/// application.
///
/// [`Server`]: crate::Server
pub trait Application: Send + Clone + 'static {
/// Echo back the same message as provided in the request.
fn echo(&self, request: RequestEcho) -> ResponseEcho {
ResponseEcho {
message: request.message,
}
}

/// Provide information about the ABCI application.
fn info(&self, _request: RequestInfo) -> ResponseInfo {
Default::default()
}

/// Called once upon genesis.
fn init_chain(&self, _request: RequestInitChain) -> ResponseInitChain {
Default::default()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand why the default pattern in here is preferred over not having a default implementation for these methods?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was modelling it on what the BaseApplication does in the Go version: https://github.com/tendermint/tendermint/blob/master/abci/types/application.go#L34

}

/// Query the application for data at the current or past height.
fn query(&self, _request: RequestQuery) -> ResponseQuery {
Default::default()
}

/// Check the given transaction before putting it into the local mempool.
fn check_tx(&self, _request: RequestCheckTx) -> ResponseCheckTx {
Default::default()
}

/// Signals the beginning of a new block, prior to any `DeliverTx` calls.
fn begin_block(&self, _request: RequestBeginBlock) -> ResponseBeginBlock {
Default::default()
}

/// Apply a transaction to the application's state.
fn deliver_tx(&self, _request: RequestDeliverTx) -> ResponseDeliverTx {
Default::default()
}

/// Signals the end of a block.
fn end_block(&self, _request: RequestEndBlock) -> ResponseEndBlock {
Default::default()
}

/// Signals that messages queued on the client should be flushed to the server.
fn flush(&self) -> ResponseFlush {
ResponseFlush {}
}

/// Commit the current state at the current height.
fn commit(&self) -> ResponseCommit {
Default::default()
}

/// Allows the Tendermint node to request that the application set an
/// option to a particular value.
fn set_option(&self, _request: RequestSetOption) -> ResponseSetOption {
Default::default()
}

/// Used during state sync to discover available snapshots on peers.
fn list_snapshots(&self) -> ResponseListSnapshots {
Default::default()
}

/// Called when bootstrapping the node using state sync.
fn offer_snapshot(&self, _request: RequestOfferSnapshot) -> ResponseOfferSnapshot {
Default::default()
}

/// Used during state sync to retrieve chunks of snapshots from peers.
fn load_snapshot_chunk(&self, _request: RequestLoadSnapshotChunk) -> ResponseLoadSnapshotChunk {
Default::default()
}

/// Apply the given snapshot chunk to the application's state.
fn apply_snapshot_chunk(
&self,
_request: RequestApplySnapshotChunk,
) -> ResponseApplySnapshotChunk {
Default::default()
}
}

/// Provides a mechanism for the [`Server`] to execute incoming requests while
/// expecting the correct response types.
pub trait RequestDispatcher {
/// Executes the relevant application method based on the type of the
/// request, and produces the corresponding response.
fn handle(&self, request: Request) -> Response;
}

// Implement `RequestDispatcher` for all `Application`s.
impl<A: Application> RequestDispatcher for A {
fn handle(&self, request: Request) -> Response {
tracing::debug!("Incoming request: {:?}", request);
Response {
value: Some(match request.value.unwrap() {
Value::Echo(req) => response::Value::Echo(self.echo(req)),
Value::Flush(_) => response::Value::Flush(self.flush()),
Value::Info(req) => response::Value::Info(self.info(req)),
Value::SetOption(req) => response::Value::SetOption(self.set_option(req)),
Value::InitChain(req) => response::Value::InitChain(self.init_chain(req)),
Value::Query(req) => response::Value::Query(self.query(req)),
Value::BeginBlock(req) => response::Value::BeginBlock(self.begin_block(req)),
Value::CheckTx(req) => response::Value::CheckTx(self.check_tx(req)),
Value::DeliverTx(req) => response::Value::DeliverTx(self.deliver_tx(req)),
Value::EndBlock(req) => response::Value::EndBlock(self.end_block(req)),
Value::Commit(_) => response::Value::Commit(self.commit()),
Value::ListSnapshots(_) => response::Value::ListSnapshots(self.list_snapshots()),
Value::OfferSnapshot(req) => {
response::Value::OfferSnapshot(self.offer_snapshot(req))
}
Value::LoadSnapshotChunk(req) => {
response::Value::LoadSnapshotChunk(self.load_snapshot_chunk(req))
}
Value::ApplySnapshotChunk(req) => {
response::Value::ApplySnapshotChunk(self.apply_snapshot_chunk(req))
}
}),
}
}
}
15 changes: 15 additions & 0 deletions abci/src/application/echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Trivial ABCI echo application

use crate::Application;

/// Trivial echo application, mainly for testing purposes.
#[derive(Clone)]
pub struct EchoApp;

impl Default for EchoApp {
fn default() -> Self {
Self {}
}
}

impl Application for EchoApp {}
Loading