A Clojure library which acts as an application host for Tendermint's ABCI --- allowing the exposure of plain functions as replicable state machines.
org.datopia/abci
uses org.datopia/stickler to provide
pure-data representations of
the protobuf-encoded messages
received from the Tendermint node process --- maps in, maps out.
| org.datopia/abci | Tendermint |
|---|---|
| 0.2.0 | 0.38.5 (CometBFT) |
| 0.1.0 | 0.34.12 |
There's a previous, deprecated artifact:
| io.datopia/abci | |
|---|---|
| 0.1.* | 0.26.0 |
The simplest possible service looks something like:
(nsmy.abci
(:require [abci.host :as host]
[abci.host.middleware :as mw]))
(defservice
(-> (constantly::mw/default)
;; Wrap handler invocations w/ (manifold.deferred/future).
mw/wrap-synchronous
;; Substitute ::mw/default for a default success response,;; appropriate to the incoming request.
mw/wrap-default
;; Return a Closeable, per aleph.tcp/start-server.
host/start))While this isn't a particularly dynamic application, we can successfully point a Tendermint node process at it and indefinitely operate a no-op blockchain.
Let's transform our handler from (constantly ::mw/default) to something
slightly different:
(fn [req]
(pprint req)
::mw/default)Once the Tendermint node connects, our first message:
{:stickler/msg:abci/Request:info {:stickler/msg:abci/RequestInfo:version"0.26.0-c086d0a3":block-version7:p2p-version4}
:stickler.one-of/value:info;; The node initiates 3 distinct connections: :info, :query, :consensus:abci.host/conn:info}The minimal success response to the above:
{:stickler/msg:abci/Response:info {:stickler/msg:abci/ResponseInfo:data"NO_INFO"}}Let's update our service to construct this response explictly, while
using middleware to strip incoming :abci/Request envelopes - and
wrap responses in :abci/Response envelopes. While this won't alter
how our service functions, it may make it a little clearer.
(defnhandler [{msg-type :stickler/msg:as req}]
(pprint req)
(case msg-type
:abci/RequestInfo {:stickler/msg:abci/ResponseInfo:data"NO_INFO"}
::mw/default))
(defservice
(-> handler
mw/wrap-synchronous
mw/wrap-default
;; Combines mw/wrap-request-envelope and mw/wrap-response-envelope
mw/wrap-envelope
host/start))Without the envelopes, our incoming request sequence:
{:stickler/msg:abci/RequestInfo:version"0.26.0-c086d0a3":block-version7:p2p-version4:abci.host/conn:info}
{:stickler/msg:abci/RequestFlush:abci.host/conn:info}
{:stickler/msg:abci/RequestInitChain:time {:stickler/msg:google.protobuf/Timestamp:seconds1544530118:nanos776243100}
:chain-id"test-chain-anZqUW":validators
[{:stickler/msg:abci/ValidatorUpdate:pub-key {:stickler/msg:abci/PubKey:type"ed25519":data <bytes>}
:power10}]
...
:abci.host/conn:consensus}
...The
project's
abci.edn resource
describes the full complement of messages. As Tendermint uses Go-specific
extensions in its protobuf files, abci.edn is generated by
org.datopia/stickler from
the types.proto maintained
by jabci.
- Moe Aboulkheir