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.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:
(ns my.abci
(:require [abci.host :as host]
[abci.host.middleware :as mw]))
(def service
(-> (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-version 7
:p2p-version 4}
: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.
(defn handler [{msg-type :stickler/msg :as req}]
(pprint req)
(case msg-type
:abci/RequestInfo {:stickler/msg :abci/ResponseInfo
:data "NO_INFO"}
::mw/default))
(def service
(-> 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-version 7
:p2p-version 4
:abci.host/conn :info}
{:stickler/msg :abci/RequestFlush
:abci.host/conn :info}
{:stickler/msg :abci/RequestInitChain
:time {:stickler/msg :google.protobuf/Timestamp
:seconds 1544530118
:nanos 776243100}
:chain-id "test-chain-anZqUW"
:validators
[{:stickler/msg :abci/ValidatorUpdate
:pub-key {:stickler/msg :abci/PubKey
:type "ed25519"
:data <bytes>}
:power 10}]
...
: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