diff --git a/docs/_static/custom_collapsible_code.css b/docs/_static/custom_collapsible_code.css
new file mode 100644
index 00000000..695268a8
--- /dev/null
+++ b/docs/_static/custom_collapsible_code.css
@@ -0,0 +1,17 @@
+.toggle {
+ padding-bottom: 1em ;
+}
+
+.toggle .header {
+ display: block;
+ clear: both;
+ cursor: pointer;
+}
+
+.toggle .header:after {
+ content: " ▼";
+}
+
+.toggle .header.open:after {
+ content: " ▲";
+}
\ No newline at end of file
diff --git a/docs/_static/custom_collapsible_code.js b/docs/_static/custom_collapsible_code.js
new file mode 100644
index 00000000..f4ff22ad
--- /dev/null
+++ b/docs/_static/custom_collapsible_code.js
@@ -0,0 +1,10 @@
+let makeCodeBlocksCollapsible = function() {
+ $(".toggle > *").hide();
+ $(".toggle .header").show();
+ $(".toggle .header").click(function() {
+ $(this).parent().children().not(".header").toggle({"duration": 400});
+ $(this).parent().children(".header").toggleClass("open");
+ });
+};
+// we could use the }(); way if we would have access to jQuery in HEAD, i.e. we would need to force the theme
+// to load jQuery before our custom scripts
diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
new file mode 100644
index 00000000..736460bc
--- /dev/null
+++ b/docs/_templates/layout.html
@@ -0,0 +1,20 @@
+{% extends "!layout.html" %}
+
+{% set css_files = css_files + ["_static/custom_collapsible_code.css"] %}
+
+# sadly, I didn't find a css style way to add custom JS to a list that is automagically added to head like CSS (above) #}
+{% block extrahead %}
+
+{% endblock %}
+
+{% block footer %}
+
+{% endblock %}
+
+
diff --git a/docs/app-development.rst b/docs/app-development.rst
index 1d2db8fd..2614a264 100644
--- a/docs/app-development.rst
+++ b/docs/app-development.rst
@@ -194,11 +194,37 @@ through all transactions in the mempool, removing any that were included
in the block, and re-run the rest using CheckTx against the post-Commit
mempool state.
-::
+.. container:: toggle
- func (app *DummyApplication) CheckTx(tx []byte) types.Result {
- return types.OK
- }
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ func (app *DummyApplication) CheckTx(tx []byte) types.Result {
+ return types.OK
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ ResponseCheckTx requestCheckTx(RequestCheckTx req) {
+ byte[] transaction = req.getTx().toByteArray();
+
+ // validate transaction
+
+ if (notValid) {
+ return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
+ } else {
+ return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
+ }
+ }
Consensus Connection
~~~~~~~~~~~~~~~~~~~~
@@ -228,18 +254,48 @@ The block header will be updated (TODO) to include some commitment to
the results of DeliverTx, be it a bitarray of non-OK transactions, or a
merkle root of the data returned by the DeliverTx requests, or both.
-::
+.. container:: toggle
- // tx is either "key=value" or just arbitrary bytes
- func (app *DummyApplication) DeliverTx(tx []byte) types.Result {
- parts := strings.Split(string(tx), "=")
- if len(parts) == 2 {
- app.state.Set([]byte(parts[0]), []byte(parts[1]))
- } else {
- app.state.Set(tx, tx)
- }
- return types.OK
- }
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ // tx is either "key=value" or just arbitrary bytes
+ func (app *DummyApplication) DeliverTx(tx []byte) types.Result {
+ parts := strings.Split(string(tx), "=")
+ if len(parts) == 2 {
+ app.state.Set([]byte(parts[0]), []byte(parts[1]))
+ } else {
+ app.state.Set(tx, tx)
+ }
+ return types.OK
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ /**
+ * Using Protobuf types from the protoc compiler, we always start with a byte[]
+ */
+ ResponseDeliverTx deliverTx(RequestDeliverTx request) {
+ byte[] transaction = request.getTx().toByteArray();
+
+ // validate your transaction
+
+ if (notValid) {
+ return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
+ } else {
+ ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
+ }
+
+ }
Commit
^^^^^^
@@ -263,12 +319,35 @@ It is expected that the app will persist state to disk on Commit. The
option to have all transactions replayed from some previous block is the
job of the `Handshake <#handshake>`__.
-::
+.. container:: toggle
- func (app *DummyApplication) Commit() types.Result {
- hash := app.state.Hash()
- return types.NewResultOK(hash, "")
- }
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ func (app *DummyApplication) Commit() types.Result {
+ hash := app.state.Hash()
+ return types.NewResultOK(hash, "")
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ ResponseCommit requestCommit(RequestCommit requestCommit) {
+
+ // update the internal app-state
+ byte[] newAppState = calculateAppState();
+
+ // and return it to the node
+ return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
+ }
BeginBlock
^^^^^^^^^^
@@ -281,16 +360,45 @@ The app should remember the latest height and header (ie. from which it
has run a successful Commit) so that it can tell Tendermint where to
pick up from when it restarts. See information on the Handshake, below.
-::
+.. container:: toggle
- // Track the block hash and header information
- func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) {
- // update latest block info
- app.blockHeader = params.Header
+ .. container:: header
- // reset valset changes
- app.changes = make([]*types.Validator, 0)
- }
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ // Track the block hash and header information
+ func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) {
+ // update latest block info
+ app.blockHeader = params.Header
+
+ // reset valset changes
+ app.changes = make([]*types.Validator, 0)
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ /*
+ * all types come from protobuf definition
+ */
+ ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
+
+ Header header = req.getHeader();
+ byte[] prevAppHash = header.getAppHash().toByteArray();
+ long prevHeight = header.getHeight();
+ long numTxs = header.getNumTxs();
+
+ // run your pre-block logic. Maybe prepare a state snapshot, message components, etc
+
+ return ResponseBeginBlock.newBuilder().build();
+ }
EndBlock
^^^^^^^^
@@ -304,12 +412,39 @@ EndBlock response. To remove one, include it in the list with a
validator set. Note validator set changes are only available in v0.8.0
and up.
-::
+.. container:: toggle
- // Update the validator set
- func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
- return types.ResponseEndBlock{Diffs: app.changes}
- }
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ // Update the validator set
+ func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
+ return types.ResponseEndBlock{Diffs: app.changes}
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ /*
+ * Assume that one validator changes. The new validator has a power of 10
+ */
+ ResponseEndBlock requestEndBlock(RequestEndBlock req) {
+ final long currentHeight = req.getHeight();
+ final byte[] validatorPubKey = getValPubKey();
+
+ ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
+ builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
+
+ return builder.build();
+ }
Query Connection
~~~~~~~~~~~~~~~~
@@ -332,33 +467,72 @@ cause Tendermint to not connect to the corresponding peer:
Note: these query formats are subject to change!
-::
+.. container:: toggle
- func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
- if reqQuery.Prove {
- value, proof, exists := app.state.Proof(reqQuery.Data)
- resQuery.Index = -1 // TODO make Proof return index
- resQuery.Key = reqQuery.Data
- resQuery.Value = value
- resQuery.Proof = proof
- if exists {
- resQuery.Log = "exists"
- } else {
- resQuery.Log = "does not exist"
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
+ if reqQuery.Prove {
+ value, proof, exists := app.state.Proof(reqQuery.Data)
+ resQuery.Index = -1 // TODO make Proof return index
+ resQuery.Key = reqQuery.Data
+ resQuery.Value = value
+ resQuery.Proof = proof
+ if exists {
+ resQuery.Log = "exists"
+ } else {
+ resQuery.Log = "does not exist"
+ }
+ return
+ } else {
+ index, value, exists := app.state.Get(reqQuery.Data)
+ resQuery.Index = int64(index)
+ resQuery.Value = value
+ if exists {
+ resQuery.Log = "exists"
+ } else {
+ resQuery.Log = "does not exist"
+ }
+ return
+ }
}
- return
- } else {
- index, value, exists := app.state.Get(reqQuery.Data)
- resQuery.Index = int64(index)
- resQuery.Value = value
- if exists {
- resQuery.Log = "exists"
- } else {
- resQuery.Log = "does not exist"
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ ResponseQuery requestQuery(RequestQuery req) {
+ final boolean isProveQuery = req.getProve();
+ final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
+
+ if (isProveQuery) {
+ com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
+ final byte[] proofAsByteArray = proofResult.getAsByteArray();
+
+ responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
+ responseBuilder.setKey(req.getData());
+ responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
+ responseBuilder.setLog(result.getLogValue());
+ } else {
+ byte[] queryData = req.getData().toByteArray();
+
+ final com.app.example.QueryResult result = generateQueryResult(queryData);
+
+ responseBuilder.setIndex(result.getIndex());
+ responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
+ responseBuilder.setLog(result.getLogValue());
+ }
+
+ return responseBuilder.build();
}
- return
- }
- }
Handshake
~~~~~~~~~
@@ -377,11 +551,31 @@ the app are synced to the latest block height.
If the app returns a LastBlockHeight of 0, Tendermint will just replay
all blocks.
-::
+.. container:: toggle
- func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
- return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
- }
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
+ return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ ResponseInfo requestInfo(RequestInfo req) {
+ final byte[] lastAppHash = getLastAppHash();
+ final long lastHeight = getLastHeight();
+ return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
+ }
Genesis
~~~~~~~
@@ -390,14 +584,45 @@ Genesis
initial validator set. Later on, it may be extended to take parts of the
consensus params.
-::
+.. container:: toggle
- // Save the validators in the merkle tree
- func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) {
- for _, v := range params.Validators {
- r := app.updateValidator(v)
- if r.IsErr() {
- app.logger.Error("Error updating validators", "r", r)
+ .. container:: header
+
+ **Show/Hide Go Example**
+
+ .. code-block:: go
+
+ // Save the validators in the merkle tree
+ func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) {
+ for _, v := range params.Validators {
+ r := app.updateValidator(v)
+ if r.IsErr() {
+ app.logger.Error("Error updating validators", "r", r)
+ }
+ }
+ }
+
+.. container:: toggle
+
+ .. container:: header
+
+ **Show/Hide Java Example**
+
+ .. code-block:: java
+
+ /*
+ * all types come from protobuf definition
+ */
+ ResponseInitChain requestInitChain(RequestInitChain req) {
+ final int validatorsCount = req.getValidatorsCount();
+ final List validatorsList = req.getValidatorsList();
+
+ validatorsList.forEach((validator) -> {
+ long power = validator.getPower();
+ byte[] validatorPubKey = validator.getPubKey().toByteArray();
+
+ // do somehing for validator setup in app
+ });
+
+ return ResponseInitChain.newBuilder().build();
}
- }
- }