Skip to content

Commit a2596d5

Browse files
thanethomsongreg-szaboromac
authored
Add minimal blocking ABCI crate (#794)
* Add minimal blocking ABCI library Signed-off-by: Thane Thomson <[email protected]> * Expand API to implement in-memory key/value store app Signed-off-by: Thane Thomson <[email protected]> * Add kvstore-rs ABCI app Signed-off-by: Thane Thomson <[email protected]> * Add rudimentary README Signed-off-by: Thane Thomson <[email protected]> * Bump proto version dependency to v0.18.0 Signed-off-by: Thane Thomson <[email protected]> * Replace manual default structs with Default::default() Signed-off-by: Thane Thomson <[email protected]> * Enable debug logging for all incoming ABCI requests Signed-off-by: Thane Thomson <[email protected]> * Improve CLI UX Signed-off-by: Thane Thomson <[email protected]> * Allow for read buffer size customization Signed-off-by: Thane Thomson <[email protected]> * Add crate description Signed-off-by: Thane Thomson <[email protected]> * Update README for ABCI crate Signed-off-by: Thane Thomson <[email protected]> * Add ABCI integration test for minimal ABCI crate (#797) * Add integration testing utility for ABCI key/value store Signed-off-by: Thane Thomson <[email protected]> * Add hacky bash script to demonstrate parallel execution Signed-off-by: Thane Thomson <[email protected]> * Created abci test harness (#800) * Created abci test harness * cargo make additions and docs Co-authored-by: Greg Szabo <[email protected]> * Update abci/src/codec.rs Co-authored-by: Romain Ruetschi <[email protected]> * Apply suggestion from https://github.com/informalsystems/tendermint-rs/pull/794\#discussion_r573100911 Signed-off-by: Thane Thomson <[email protected]> * Refactor error handing and expose eyre::Result as crate default Result type Signed-off-by: Thane Thomson <[email protected]> * Refactor to use tracing instead of log Signed-off-by: Thane Thomson <[email protected]> * Add newline Signed-off-by: Thane Thomson <[email protected]> * Remove comment relating to constraints on Codec struct params Signed-off-by: Thane Thomson <[email protected]> * Version tendermint-abci crate in line with other tendermint-rs crates Signed-off-by: Thane Thomson <[email protected]> * Update CHANGELOG Signed-off-by: Thane Thomson <[email protected]> * Expand crate documentation Signed-off-by: Thane Thomson <[email protected]> * Extract request dispatch functionality from Application trait Signed-off-by: Thane Thomson <[email protected]> * Move ABCI server example to crate root Signed-off-by: Thane Thomson <[email protected]> * Fix broken link in docs Signed-off-by: Thane Thomson <[email protected]> * Replace EchoApp example with KeyValueStoreApp example Signed-off-by: Thane Thomson <[email protected]> Co-authored-by: Greg Szabo <[email protected]> Co-authored-by: Romain Ruetschi <[email protected]>
1 parent 0269414 commit a2596d5

28 files changed

+1614
-0
lines changed

.cargo/config

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
build-all = "build --workspace --all-targets --"
33
build-wasm-tendermint = "build -p tendermint --manifest-path tendermint/Cargo.toml --target wasm32-unknown-unknown --release --no-default-features --"
44
build-wasm-light-client = "build -p tendermint-light-client --manifest-path light-client/Cargo.toml --target wasm32-unknown-unknown --release --no-default-features --"
5+
build-abci = "build --manifest-path abci/Cargo.toml --bin kvstore-rs --features binary,kvstore-app"
56
test-all-features = "test --all-features --no-fail-fast"

.dockerignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Cargo
2+
# will have compiled files and executables
3+
/target/
4+
5+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7+
Cargo.lock
8+
9+
# These are backup files generated by rustfmt
10+
**/*.rs.bk
11+
12+
# These are log files emitted by model-based tests
13+
**/*.log
14+
15+
# RPC probe results
16+
/rpc-probe/probe-results/
17+
18+
# Proptest regressions dumps
19+
**/*.proptest-regressions

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## Unreleased
2+
3+
### FEATURES
4+
5+
* `[tendermint-abci]` Release minimal framework for building ABCI applications
6+
in Rust ([#794])
7+
8+
[#794]: https://github.com/informalsystems/tendermint-rs/pull/794
9+
110
## v0.18.1
211

312
*Feb 10, 2021*

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22

33
members = [
4+
"abci",
45
"light-client",
56
"light-node",
67
"p2p",

abci/Cargo.toml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[package]
2+
name = "tendermint-abci"
3+
version = "0.18.1"
4+
authors = ["Thane Thomson <[email protected]>"]
5+
edition = "2018"
6+
description = """
7+
tendermint-abci provides a simple framework with which to build low-level
8+
applications on top of Tendermint.
9+
"""
10+
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[[bin]]
14+
name = "kvstore-rs"
15+
path = "src/application/kvstore/main.rs"
16+
required-features = [ "binary", "kvstore-app" ]
17+
18+
[features]
19+
client = []
20+
echo-app = []
21+
kvstore-app = []
22+
binary = [ "structopt", "tracing-subscriber" ]
23+
24+
[dependencies]
25+
bytes = "1.0"
26+
eyre = "0.6"
27+
prost = "0.7"
28+
tendermint-proto = { version = "0.18.0", path = "../proto" }
29+
thiserror = "1.0"
30+
tracing = "0.1"
31+
32+
structopt = { version = "0.3", optional = true }
33+
tracing-subscriber = { version = "0.2", optional = true }

abci/README.md

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
## tendermint-abci
2+
3+
[![Crate][crate-image]][crate-link]
4+
[![Docs][docs-image]][docs-link]
5+
[![Build Status][build-image]][build-link]
6+
[![Audit Status][audit-image]][audit-link]
7+
[![Apache 2.0 Licensed][license-image]][license-link]
8+
![Rust Stable][rustc-image]
9+
10+
[ABCI] framework for building low-level applications for Tendermint in Rust.
11+
12+
## Requirements
13+
14+
- The latest stable version of Rust
15+
16+
## API
17+
18+
At present, this crate only exposes a synchronous, blocking API based on Rust's
19+
standard library's networking capabilities. `async` client/server support is
20+
planned in future updates.
21+
22+
The primary trait to be implemented by an ABCI application is the
23+
[`Application`] trait. One of the core ideas here is that an ABCI application
24+
must be able to be cloned for use in different threads, since Tendermint opens
25+
4 connections to the ABCI server. See the [spec][tendermint-abci-spec] for
26+
details.
27+
28+
## Examples
29+
30+
See [`src/application`](./src/application/) for some example applications
31+
written using this crate.
32+
33+
To run the key/value store example application, from the `tendermint-abci`
34+
crate's directory:
35+
36+
```bash
37+
# Set your logging level through RUST_LOG (e.g. RUST_LOG=info)
38+
# Binds to 127.0.0.1:26658
39+
RUST_LOG=debug cargo run --bin kvstore-rs --features binary,kvstore-app
40+
41+
# Reset and run your Tendermint node (binds RPC to 127.0.0.1:26657 by default)
42+
tendermint unsafe_reset_all && tendermint start
43+
44+
# Submit a key/value pair (set "somekey" to "somevalue")
45+
curl 'http://127.0.0.1:26657/broadcast_tx_async?tx="somekey=somevalue"'
46+
47+
#{
48+
# "jsonrpc": "2.0",
49+
# "id": -1,
50+
# "result": {
51+
# "code": 0,
52+
# "data": "",
53+
# "log": "",
54+
# "codespace": "",
55+
# "hash": "17ED61261A5357FEE7ACDE4FAB154882A346E479AC236CFB2F22A2E8870A9C3D"
56+
# }
57+
#}
58+
59+
# Query for the value we just submitted ("736f6d656b6579" is the hex
60+
# representation of "somekey")
61+
curl 'http://127.0.0.1:26657/abci_query?data=0x736f6d656b6579'
62+
63+
#{
64+
# "jsonrpc": "2.0",
65+
# "id": -1,
66+
# "result": {
67+
# "response": {
68+
# "code": 0,
69+
# "log": "exists",
70+
# "info": "",
71+
# "index": "0",
72+
# "key": "c29tZWtleQ==",
73+
# "value": "c29tZXZhbHVl",
74+
# "proofOps": null,
75+
# "height": "189",
76+
# "codespace": ""
77+
# }
78+
# }
79+
#}
80+
```
81+
82+
## License
83+
84+
Copyright © 2021 Informal Systems
85+
86+
Licensed under the Apache License, Version 2.0 (the "License");
87+
you may not use the files in this repository except in compliance with the License.
88+
You may obtain a copy of the License at
89+
90+
https://www.apache.org/licenses/LICENSE-2.0
91+
92+
Unless required by applicable law or agreed to in writing, software
93+
distributed under the License is distributed on an "AS IS" BASIS,
94+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95+
See the License for the specific language governing permissions and
96+
limitations under the License.
97+
98+
[//]: # (badges)
99+
100+
[crate-image]: https://img.shields.io/crates/v/tendermint-abci.svg
101+
[crate-link]: https://crates.io/crates/tendermint-abci
102+
[docs-image]: https://docs.rs/tendermint-abci/badge.svg
103+
[docs-link]: https://docs.rs/tendermint-abci/
104+
[build-image]: https://github.com/informalsystems/tendermint-rs/workflows/Rust/badge.svg
105+
[build-link]: https://github.com/informalsystems/tendermint-rs/actions?query=workflow%3ARust
106+
[audit-image]: https://github.com/informalsystems/tendermint-rs/workflows/Audit-Check/badge.svg
107+
[audit-link]: https://github.com/informalsystems/tendermint-rs/actions?query=workflow%3AAudit-Check
108+
[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
109+
[license-link]: https://github.com/informalsystems/tendermint-rs/blob/master/LICENSE
110+
[rustc-image]: https://img.shields.io/badge/rustc-stable-blue.svg
111+
112+
[//]: # (general links)
113+
114+
[ABCI]: https://docs.tendermint.com/master/spec/abci/
115+
[`Application`]: ./src/application.rs
116+
[tendermint-abci-spec]: https://github.com/tendermint/spec/blob/master/spec/abci/abci.md

abci/src/application.rs

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//! ABCI application interface.
2+
3+
#[cfg(feature = "echo-app")]
4+
pub mod echo;
5+
#[cfg(feature = "kvstore-app")]
6+
pub mod kvstore;
7+
8+
use tendermint_proto::abci::request::Value;
9+
use tendermint_proto::abci::{
10+
response, Request, RequestApplySnapshotChunk, RequestBeginBlock, RequestCheckTx,
11+
RequestDeliverTx, RequestEcho, RequestEndBlock, RequestInfo, RequestInitChain,
12+
RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestQuery, RequestSetOption, Response,
13+
ResponseApplySnapshotChunk, ResponseBeginBlock, ResponseCheckTx, ResponseCommit,
14+
ResponseDeliverTx, ResponseEcho, ResponseEndBlock, ResponseFlush, ResponseInfo,
15+
ResponseInitChain, ResponseListSnapshots, ResponseLoadSnapshotChunk, ResponseOfferSnapshot,
16+
ResponseQuery, ResponseSetOption,
17+
};
18+
19+
/// An ABCI application.
20+
///
21+
/// Applications are `Send` + `Clone` + `'static` because they are cloned for
22+
/// each incoming connection to the ABCI [`Server`]. It is up to the
23+
/// application developer to manage shared state between these clones of their
24+
/// application.
25+
///
26+
/// [`Server`]: crate::Server
27+
pub trait Application: Send + Clone + 'static {
28+
/// Echo back the same message as provided in the request.
29+
fn echo(&self, request: RequestEcho) -> ResponseEcho {
30+
ResponseEcho {
31+
message: request.message,
32+
}
33+
}
34+
35+
/// Provide information about the ABCI application.
36+
fn info(&self, _request: RequestInfo) -> ResponseInfo {
37+
Default::default()
38+
}
39+
40+
/// Called once upon genesis.
41+
fn init_chain(&self, _request: RequestInitChain) -> ResponseInitChain {
42+
Default::default()
43+
}
44+
45+
/// Query the application for data at the current or past height.
46+
fn query(&self, _request: RequestQuery) -> ResponseQuery {
47+
Default::default()
48+
}
49+
50+
/// Check the given transaction before putting it into the local mempool.
51+
fn check_tx(&self, _request: RequestCheckTx) -> ResponseCheckTx {
52+
Default::default()
53+
}
54+
55+
/// Signals the beginning of a new block, prior to any `DeliverTx` calls.
56+
fn begin_block(&self, _request: RequestBeginBlock) -> ResponseBeginBlock {
57+
Default::default()
58+
}
59+
60+
/// Apply a transaction to the application's state.
61+
fn deliver_tx(&self, _request: RequestDeliverTx) -> ResponseDeliverTx {
62+
Default::default()
63+
}
64+
65+
/// Signals the end of a block.
66+
fn end_block(&self, _request: RequestEndBlock) -> ResponseEndBlock {
67+
Default::default()
68+
}
69+
70+
/// Signals that messages queued on the client should be flushed to the server.
71+
fn flush(&self) -> ResponseFlush {
72+
ResponseFlush {}
73+
}
74+
75+
/// Commit the current state at the current height.
76+
fn commit(&self) -> ResponseCommit {
77+
Default::default()
78+
}
79+
80+
/// Allows the Tendermint node to request that the application set an
81+
/// option to a particular value.
82+
fn set_option(&self, _request: RequestSetOption) -> ResponseSetOption {
83+
Default::default()
84+
}
85+
86+
/// Used during state sync to discover available snapshots on peers.
87+
fn list_snapshots(&self) -> ResponseListSnapshots {
88+
Default::default()
89+
}
90+
91+
/// Called when bootstrapping the node using state sync.
92+
fn offer_snapshot(&self, _request: RequestOfferSnapshot) -> ResponseOfferSnapshot {
93+
Default::default()
94+
}
95+
96+
/// Used during state sync to retrieve chunks of snapshots from peers.
97+
fn load_snapshot_chunk(&self, _request: RequestLoadSnapshotChunk) -> ResponseLoadSnapshotChunk {
98+
Default::default()
99+
}
100+
101+
/// Apply the given snapshot chunk to the application's state.
102+
fn apply_snapshot_chunk(
103+
&self,
104+
_request: RequestApplySnapshotChunk,
105+
) -> ResponseApplySnapshotChunk {
106+
Default::default()
107+
}
108+
}
109+
110+
/// Provides a mechanism for the [`Server`] to execute incoming requests while
111+
/// expecting the correct response types.
112+
pub trait RequestDispatcher {
113+
/// Executes the relevant application method based on the type of the
114+
/// request, and produces the corresponding response.
115+
fn handle(&self, request: Request) -> Response;
116+
}
117+
118+
// Implement `RequestDispatcher` for all `Application`s.
119+
impl<A: Application> RequestDispatcher for A {
120+
fn handle(&self, request: Request) -> Response {
121+
tracing::debug!("Incoming request: {:?}", request);
122+
Response {
123+
value: Some(match request.value.unwrap() {
124+
Value::Echo(req) => response::Value::Echo(self.echo(req)),
125+
Value::Flush(_) => response::Value::Flush(self.flush()),
126+
Value::Info(req) => response::Value::Info(self.info(req)),
127+
Value::SetOption(req) => response::Value::SetOption(self.set_option(req)),
128+
Value::InitChain(req) => response::Value::InitChain(self.init_chain(req)),
129+
Value::Query(req) => response::Value::Query(self.query(req)),
130+
Value::BeginBlock(req) => response::Value::BeginBlock(self.begin_block(req)),
131+
Value::CheckTx(req) => response::Value::CheckTx(self.check_tx(req)),
132+
Value::DeliverTx(req) => response::Value::DeliverTx(self.deliver_tx(req)),
133+
Value::EndBlock(req) => response::Value::EndBlock(self.end_block(req)),
134+
Value::Commit(_) => response::Value::Commit(self.commit()),
135+
Value::ListSnapshots(_) => response::Value::ListSnapshots(self.list_snapshots()),
136+
Value::OfferSnapshot(req) => {
137+
response::Value::OfferSnapshot(self.offer_snapshot(req))
138+
}
139+
Value::LoadSnapshotChunk(req) => {
140+
response::Value::LoadSnapshotChunk(self.load_snapshot_chunk(req))
141+
}
142+
Value::ApplySnapshotChunk(req) => {
143+
response::Value::ApplySnapshotChunk(self.apply_snapshot_chunk(req))
144+
}
145+
}),
146+
}
147+
}
148+
}

abci/src/application/echo.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! Trivial ABCI echo application
2+
3+
use crate::Application;
4+
5+
/// Trivial echo application, mainly for testing purposes.
6+
#[derive(Clone)]
7+
pub struct EchoApp;
8+
9+
impl Default for EchoApp {
10+
fn default() -> Self {
11+
Self {}
12+
}
13+
}
14+
15+
impl Application for EchoApp {}

0 commit comments

Comments
 (0)