tendermint --help
abci-cli --help
bash launch_testnet_nodes.sh
broadcast_tx_commit
endpoint of the Tendermint ABCI (Tendermint's server-side Application BlockChain Interface API) that uses the Tendermint Core, which is a Byzantine Fault Tolerance (BFT) Blockchain Engine Middlware that processes a State Transition machine input from any language (i.e. Elixir) and replicates it on many Tendermint Testnet Nodes as output. Successful transactions are included in the Mempool, broadcast to Peers, and eventually committed in a Block with the return value containing check_tx
and deliver_tx
properties (each containing data
and log
sub-properties) to signify that the transaction was run through the CheckTx and DeliverTx ABCI messages of the TMSP (Simple Messaging Protocol)brew install elixir;
elixir --version
Tendermint
shopt
error, as shown in Dockerfilego get --help
go get -u -v github.com/tendermint/tendermint/cmd/tendermint
tendermint --help
brew install jsonpp
priv_validator.json
, and Genesis File genesis.json
containing associated Public Key
tendermint init
cd ~/.tendermint
rm -rf data/
tendermint unsafe_reset_priv_validator
View Tendermint Directory Root
$ ls ~/.tendermint
config.toml
data
genesis.json
priv_validator.json
View/Edit TOML Configuration File - https://github.com/tendermint/tendermint/wiki/Configuration
$ cat ~/.tendermint/config.toml
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
# ABCI Application Socket Address
proxy_app = "tcp://0.0.0.0:46658"
# Node Name
moniker = "<MY_NETWORK_NAME>.local"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"
# Allow Tendermint p2p library to make connections to peers with the same IP address
# https://tendermint.readthedocs.io/en/master/using-tendermint.html#local-network
addrbook_strict = false
[rpc]
# RPC Server Listening Address
laddr = "tcp://0.0.0.0:46657"
[p2p]
# Peer Listening Address on Tendermint
laddr = "tcp://0.0.0.0:46656"
seeds = ""
View New Private Key from initialising Tendermint
$ cat ~/.tendermint/priv_validator.json | python -m json.tool
{
"address": "E472...",
"last_height": 18,
"last_round": 0,
"last_signature": {
"type": "ed25519",
"data": "B755..."
},
"last_signbytes": "7B22...",
"last_step": 3,
"priv_key": {
"type": "ed25519",
"data": "D11C..."
},
"pub_key": {
"type": "ed25519",
"data": "8373..."
}
}
View Genesis File containing Public Key
$ cat ~/.tendermint/genesis.json | python -m json.tool
{
"app_hash": "",
"chain_id": "test-chain-gZoesi",
"genesis_time": "0001-01-01T00:00:00Z",
"validators": [
{
"pub_key": {
"type": "ed25519",
"data": "8373..."
},
"power": 10,
"name": ""
}
]
}
go get -u github.com/tendermint/abci/cmd/abci-cli;
abci-cli --help
Start Tendermint Single-Node Blockchain and Compile in-progress ABCI Application written in GoLang (i.e. Dummy App https://github.com/tendermint/abci)
$ tendermint node --proxy_app=dummy
I[01-22|21:35:03.283] Executed block module=state height=1 validTxs=0 invalidTxs=0
I[01-22|21:35:03.283] Committed state module=state height=1 txs=0 appHash=
I[01-22|21:35:04.295] Executed block module=state height=2 validTxs=0 invalidTxs=0
I[01-22|21:35:04.295] Committed state module=state height=2 txs=0 appHash=
Review Example Application built with ABCI Server - https://github.com/KrzysiekJ/abci_counter
brew install erlang git;
brew install make --with-default-names;
cd blockchain_tendermint;
defp deps do
[
# ABCI Server (Erlang) - https://github.com/KrzysiekJ/abci_server
# Tendermint List of ABCI Servers - http://tendermint.readthedocs.io/projects/tools/en/master/ecosystem.html?highlight=server#abci-servers
{:abci_server, git: "https://github.com/KrzysiekJ/abci_server.git", tag: "v0.4.0"}
]
end
mix deps.get
cd deps/abci_server/ && make docs && open doc/index.html && cd ../../
MIX_ENV=dev mix compile
Interactive Elixir (REPL) within context of Elixir App and dependencies injected into IEx runtime
iex -S mix
c("lib/blockchain_tendermint.ex")
BlockchainTendermint.start_server
Run Tendermint Node
tendermint node
Send cURL Request to ABCI Server endpoint.
curl -s 'localhost:46658/broadcast_tx_commit?tx="sender=___&receiver=___&data=___"'
View Logs from ABCI Server in IEx Terminal Window. Shows Outputs of handle_request
function in Elixir ABCI App
iex(1)> BlockchainTendermint.start_server
{:ok, #PID<0.168.0>}
iex(2)> Processing Transaction
Received Arguments to handle_request: "sender=a&receiver=b&data=''"
58c89d709329eb37285837b042ab6ff72c7c8f74de0446b091b6a0131c102cfd
Validity of Transaction: true
20:32:18.499 [error] GenServer #PID<0.176.0> terminating
** (FunctionClauseError) no function clause matching in :abci.e_msg_ResponseInfo/3
...
Last message: {:tcp, #Port<0.5191>, <<1, 10, 34, 8, 10, 6, 48, 46, 49, 53, 46, 48, 1, 2, 26, 0>>}
State: {:state, #Port<0.5191>, :ranch_tcp, "", BlockchainTendermint}
20:32:18.502 [error] Ranch listener BlockchainTendermint had connection process started with :abci_server:start_link/4 at #PID<0.176.0> exit with reason: ...
Stop Server
BlockchainTendermint.stop_server
mix test
Launch Testnet Nodes (4 OFF) (in separate Terminal Tabs using Shell Script)
$ bash launch_testnet_nodes.sh
Tendermint Testnet Location: /Users/Ls/code/blockchain/tendermint-elixir/mytestnet
Loading Nodes: mach0, mach1, mach2, mach3
Loading Seeds: 0.0.0.0:46656,0.0.0.0:46666,0.0.0.0:46676,0.0.0.0:46686
Successfully initialized 4 node directories
Troubleshooting: If nothing appears in each of the separate Terminal Tabs then in restart the server in IEx with:
k = BlockchainTendermint.stop_server
{ok, _} = BlockchainTendermint.start_server
Example output in each separate Terminal Tab
E[01-27|23:38:17.914] Stopping abci.socketClient for error: EOF module=abci-client connection=query
E[01-27|23:38:29.069] Stopping abci.socketClient for error: EOF module=abci-client connection=mempool
E[01-27|23:38:29.069] Stopping abci.socketClient for error: EOF module=abci-client connection=consensus
E[01-27|23:38:29.068] Stopping abci.socketClient for error: read tcp 127.0.0.1:63711->127.0.0.1:46658: read: connection reset by peer module=abci-client connection=query
E[01-27|23:38:29.068] Stopping abci.socketClient for error: read tcp 127.0.0.1:63713->127.0.0.1:46658: read: connection reset by peer module=abci-client connection=consensus
E[01-27|23:38:29.069] Stopping abci.socketClient for error: read tcp 127.0.0.1:63712->127.0.0.1:46658: read: connection reset by peer module=abci-client connection=mempool
UNRESOLVED
Optional Alternative Deployment
Show all available API endpoints by going to http://0.0.0.0:46658/
Send Request to ABCI Server endpoint. Note: Must use 0.0.0.0
NOT localhost
curl -v '0.0.0.0:46658/status' | jsonpp | grep app_hash
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (0.0.0.0) port 46658 (#0)
> GET /status HTTP/1.1
> Host: 0.0.0.0:46658
> User-Agent: curl/7.57.0
> Accept: */*
>
UNRESOLVED
CheckTx
abci-cli check_tx "0x00" --address tcp://0.0.0.0:46658 --abci "socket" --log_level "debug" --verbose
Echo
abci-cli echo "Hello" --address tcp://0.0.0.0:46658 --abci "socket" --log_level "debug" --verbose
DeliverTx
abci-cli deliver_tx "0x00" --address tcp://0.0.0.0:46658 --abci "socket" --log_level "debug" --verbose
Query
abci-cli query "0x00" --address tcp://0.0.0.0:46658 --abci "socket" --log_level "debug" --verbose
tendermint init;
tendermint unsafe_reset_all;
tendermint node --help;
tendermint node \
--abci "socket" \
--consensus.create_empty_blocks true \
--fast_sync true \
--moniker "LS.local" \
--p2p.laddr "tcp://0.0.0.0:46656" \
--p2p.pex true \
--p2p.seeds "tcp://127.0.0.1:46656, tcp://127.0.0.1:46666, tcp://127.0.0.1:46676, tcp://127.0.0.1:46686" \
--p2p.skip_upnp false \
--proxy_app "tcp://127.0.0.1:46658" \
--rpc.laddr "tcp://0.0.0.0:46657" \
--rpc.unsafe true \
--home "/Users/Ls/.tendermint" \
--log_level "state:info,*:error" \
--trace true
iex -S mix
Reference: Loading an Erlang Library into Elixir - https://elixirschool.com/en/lessons/advanced/erlang/
Important Note: __info__/1
is an Elixir thing the compiler adds, you probably want module_info/1
which is the erlang equivalent - https://elixir-lang.slack.com/archives/C03EPRA3B/p1517018221000028
Show ABCI Server Module Information. Start ABCI Server. Stop ABCI Server
iex> :abci_server.module_info
[
module: :abci_server,
exports: [
start_link: 4,
start_listener: 2,
child_spec: 2,
stop_listener: 1,
init: 1,
handle_call: 3,
handle_cast: 2,
handle_info: 2,
terminate: 2,
code_change: 3,
module_info: 0,
module_info: 1
],
attributes: [
vsn: [86973587470476204871336807197797490126],
behaviour: [:gen_server],
behaviour: [:ranch_protocol]
],
compile: [
options: [
:debug_info,
{:i,
'/Users/Ls/code/blockchain/tendermint-elixir/blockchain_tendermint/deps/abci_server/include'},
:warn_obsolete_guard,
:warn_shadow_vars,
:warn_export_vars
],
version: '7.1.4',
source: '/Users/Ls/code/blockchain/tendermint-elixir/blockchain_tendermint/deps/abci_server/src/abci_server.erl'
],
native: false,
md5: <<65, 110, ..., 206>>
]
iex> defmodule Foo do
def bar() do
IO.puts("Hello, World!")
end
end
iex> {ok, _} = :abci_server.start_listener(Foo, 46658)
{:ok, #PID<0.181.0>}
Integration Tests run against the ABCI Server (Erlang) (separate Bash Terminal Tab) https://github.com/tendermint/abci#tools
abci-cli test
iex>
14:27:53.961 [error] GenServer #PID<0.242.0> terminating
** (UndefinedFunctionError) function Foo.handle_request/1 is undefined (module Foo is not available)
Foo.handle_request(
{
:RequestInitChain,
[
{ :Validator, <<1, 229, ..., 36>>, 1647107211121726315 },
{ :Validator, <<1, 243, ..., 78>>, 8186817011543816184 },
{ :Validator, <<1, 102, ..., 16>>, 7982159435569315414 },
{ :Validator, <<1, 135, ..., 64>>, 2846252370576207682 },
{ :Validator, <<1, 241,..., 159>>, 637770835807807961 },
{ :Validator, <<1, 16, ..., 76>>, 4097788002864909056 },
{ :Validator, <<1, 152, ..., 70>>, 8116718250853054711 },
{ :Validator, <<1, 19, ..., 246>>, 3891949616163017026 },
{ :Validator, <<1, 179, ..., 254>>, 7045591847215797995 },
{ :Validator, <<1, 189, ..., 80>>, 4226073179895220771 }
]
}
)
(abci_server) src/abci_server.erl:117: :abci_server.handle_requests/2
(abci_server) src/abci_server.erl:83: :abci_server.handle_info/2
(stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:686: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: { :tcp, #Port<0.5297>, <<2, 1, ..., 148, ...>> }
State: { :state, #Port<0.5297>, :ranch_tcp, "", Foo}
14:27:53.965 [error] Ranch listener Foo had connection process started with :abci_server:start_link/4 at #PID<0.242.0> exit with reason:
{ :undef,
[
{
Foo,
:handle_request, [
RequestInitChain: [
{ :Validator, <<1, 229, ..., 36>>, 1647107211121726315 },
{ :Validator, <<1, 189, ..., 112, ...>>, 4226073179895220771 }
]
],
[]
},
{
:abci_server,
:handle_requests,
2,
[file: 'src/abci_server.erl', line: 117]
},
{
:abci_server,
:handle_info,
2,
[file: 'src/abci_server.erl', line: 83]
},
{
:gen_server,
:try_dispatch,
4,
[file: 'gen_server.erl', line: 616]
},
{
:gen_server,
:handle_msg,
6,
[file: 'gen_server.erl', line: 686]
},
{
:proc_lib,
:init_p_do_apply,
3,
[file: 'proc_lib.erl', line: 247]
}
]
}
Update Elixir App to define handle_request
Handle Request. Re-run the following in a separete Bash Terminal whilst ABCI Server (Erlang) is running and it will return Passed test: InitChain
.
abci-cli test
:sha256
iex> MerkleTree.__info__(:functions)
[__struct__: 0, __struct__: 1, build: 2, new: 1, new: 2]
iex> mt = MerkleTree.new ['a', 'b', 'c', 'd']
%MerkleTree{
blocks: ['a', 'b', 'c', 'd'],
hash_function: &MerkleTree.Crypto.sha256/1,
root:
%MerkleTree.Node {
children: [
%MerkleTree.Node {
children: [
%MerkleTree.Node {
children: [],
value: "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
},
%MerkleTree.Node {
children: [],
value: "3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d"
}
],
value: "62af5c3cb8da3e4f25061e829ebeea5c7513c54949115b1acc225930a90154da"
},
%MerkleTree.Node {
children: [
%MerkleTree.Node {
children: [],
value: "2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6"
},
%MerkleTree.Node {
children: [],
value: "18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4"}
],
value: "d3a0f1c792ccf7f1708d5422696263e35755a86917ea76ef9242bd4a8cf4891a"
}
],
value: "58c89d709329eb37285837b042ab6ff72c7c8f74de0446b091b6a0131c102cfd"
}
}
$ mt.blocks()
['a', 'b', 'c', 'd']
$ mt.hash_function()
&MerkleTree.Crypto.sha256/1
$ mt.root()
...
MerkleTree.Proof Module Example (requires merkle_tree >1.2.0)
iex> MerkleTree.Proof.__info__(:functions)
[__struct__: 0, __struct__: 1, prove: 2, proven?: 3]
iex> proof1 = MerkleTree.Proof.prove(mt, 1)
iex> proven1 = MerkleTree.Proof.proven?({"b", 1}, "58c89d709329eb37285837b042ab6ff72c7c8f74de0446b091b6a0131c102cfd", proof1)
true
iex> proof3 = MerkleTree.Proof.prove(mt, 3)
%MerkleTree.Proof{
hash_function: &MerkleTree.Crypto.sha256/1,
hashes: ["62af5c3cb8da3e4f25061e829ebeea5c7513c54949115b1acc225930a90154da",
"2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6"]
}
iex> proven3 = MerkleTree.Proof.proven?({"d", 3}, "58c89d709329eb37285837b042ab6ff72c7c8f74de0446b091b6a0131c102cfd", proof3)
true
MerkleTree.Crypto Module Example
iex> MerkleTree.Crypto.__info__(:functions)
[hash: 2, sha256: 1]
iex> MerkleTree.Crypto.hash("tendermint", :sha256)
Merkle Tree
ABCI Server
Problem: erlang.mk:26: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
Check Make and GMake installation directories
$ which -a make;
/usr/bin/make
$ which -a gmake;
/usr/local/bin/gmake
Add to Bash Profile
export PATH="/usr/local/bin/make:$PATH"
export PATH="/usr/local/opt/make/libexec/gnubin:$PATH"
Reset Bash Profile
source ~/.bash_profile
Create Symlink between version of GNU Make 4 in PATH and where it is installed (instead of default macOS GNU Make 3)
ln -s /usr/local/bin/gmake /usr/local/bin/make
Check PATH of Make has changed
$ which make
/usr/local/bin/make
Problem: ** (Mix) Could not start application ranch: could not find application file: ranch.app
Problem: Unable to communicate with Elixir ABCI App. After running ABCI Server, when I try and connect with tendermint node
, it gives the following error, which is not listed on the Tendermint how to read logs webpage -
https://tendermint.readthedocs.io/en/master/how-to-read-logs.html
E[01-27|05:47:23.729] Stopping abci.socketClient for error: EOF module=abci-client connection=query
Problem: Tendermint Testnet Node flags moniker and seed do not overwrite as expected
Tendermint
Background - https://blockchainhub.net/blog/blog/scaling-ethereum-2/
Diagrams
TendermintCore (BFT "consensus engine" Protocol that is mostly Asynchronous that uses a Simple State Machine https://tendermint.readthedocs.io/en/master/introduction.html#consensus-overview, and that communicates with an Application via TSP socket protocol that satisfies the ABCI)
About
Byzantine Fault Tolerant (BFT) Consensus engine
Block Data Structure - https://github.com/tendermint/tendermint/wiki/Block-Structure
Header
- chain info and Last State
ChainId
(string): name of the Blockchain, e.g. "tendermint"Height
(int): sequential Block Number starting with 1Time
(time): local time of the Proposer of this BlockLastBlockHash
([]byte): Block Hash of the Previous Block at Height - 1
LastBlockPartsetHeader
(PartSetHeader): Partset Header of the Previous BlockStateHash
([]byte): State Hash of the State after processing this Block
StateHash
is a hash derived from a encoding the State's Fields (i.e. BondedValidators
, UnbondingValidators
, Accounts
, ValidatorInfos
, and NameRegistry
), using a Simple Tree (Binary Tree) to merkelise a dictionary list of KV Pair structs.StateHash
is recursively included in the block Header
and indirectly into the BlockHash
Data
- Transactions of the Block that are any sequence of Bytes comprising:
Txs
([]Tx
): list of Transactions.LastCommit
- Comprises Precommit
Signatures of the Previous Block
Precommits
([]Vote
): confirms that Consensus Decided for the Last Block was performed by over 2/3 (i.e. +2/3) of valid Voting Participants
Vote
Height
(int): Block Height being Decided onRound
(int): Consensus Round number (starting with 0)Type
(byte): Vote Type, either:
Prevote
or Precommit
- Vote Type to Broadcast"type": 2
Prevote
's for a Block, or <nil>
at Node (H, R)
BlockHash
([]byte):
BlockPartsetHeader
(PartSetHeader):
{ "hash": "XYZ", "total": 123 }
Signature
(Signature): Signature
field of Vote's Transaction
sign-bytes
: JSON stringified (ordered) encoding (excluding the Signature
) of Vote's Transaction{ "chain_id": "tendermint","vote": { ... } }
Consensus Process (State Machine) - https://tendermint.readthedocs.io/en/master/introduction.html#consensus-overview
H
)
RoundStep
representation (aka "Step") comprising Optimally 3x Steps + 2x Special Steps:
Propose (H, R)
CommitTime
+ timeoutCommit
(after NewHeight
committed) ORtimeoutPrecommit
(H, R)
timeoutProposer
-> Step 2PoLC Round
receipt of Proposal Block and all Prevotes -> Step 2Prevote (H, R)
LastLockRound
PoLC
for something else at PoLC-Round
where LastLockRound < PoLC-Round < R
Propose (H, R)
happens when it is Valid<nil>
happens for a Proposed Block that is Invalid or received late<nil>
-> Step 3timeoutPrevote
after receiving any +2/3 Prevotes -> Step 3Precommit (H, R)
after +2/3 Precommits for Block found
B
and Sets LastLockRound = R
when Validator has PoLC (H, R)
for specific Block B
<nil>
(i.e. obtained +2/3 Prevotes and waited but did not see PoLC for the Round) when Validator has PoLC at (H, R)
for <nil>
<nil>
otherwise<nil>
-> Step 1 Propose (H, R + 1)
timeoutPrecommit
after receiving any +2/3 Precommits -> Step 1 Propose (H, R + 1)
Commit (H)
by:
CommitTime = now
NewHeight (H + 1)
NewHeight (H)
Precommits
to LastCommit
StartTime = CommitTime + timeoutCommit
StartTime
to receive straggling commits -> Step 1 Propose (H, 0)
NewHeight -> (Propose -> Prevote -> Precommit) + -> Commit -> NewHeight ->
<nil>
or performed a malicious votes) so there were Insufficient Validator Nodes when the Precommit step was reachedProposal
(H, R)
comprises:
R
), included if Proposer is aware of one, and provides a hint to the network to allow "unlocking" of Nodes when safe to ensure "Proof of Liveness"Proposer (NOT Validators)
Nodes - optionally connected in the network, are at a given:
H
- Block Height being decided uponR
- Consensus Round number (starting from 0)S
(H, R, S)
Peers - are Nodes that are directly connected to a Node
Channel
(Throttled information)
Connection
- between 2x NodesEpidemic Gossip Protocol - implemented by some Channels to update Peers on latest State of Consensus
Prevote
's for a Block, or <nil>
at Node (H, R)
HasVote
messages opportunistically to hint to Peers what Votes they haveGossip Protocol Participants are "Validators"
Validators
Blocks
Block Commit Failure
Voting Stages required for Block Commit
Nodes
H
, R
, S
)H
, R
, S
)Stake
Proofs
Proof of Safety
B
at Round R
after observing +2/3 (over 2/3) of Precommits at Round R
R' > R
to remain so until they observe PoLC at R' > R
B
Proof of Liveness
PoLC-Round
will Unlock Nodes that were Locked in previous RoundstimeoutProposalR
increments with Round R
whilst Proposal size is capped, so network eventually is able to fully gossip the whole Proposal (both Block and PoLC)TODO
Benefits:
Tendermint Generic Application BlockChain "Interface" (ABCI) is an API between Application Process and Consensus Process. ABCI has a Tendermint Socket Protocol (TSP aka Teaspoon) Implementation:
Run in separate Unix processes.
De-coupling (abstracting away) the Application State Details of a particular blockchain Application (using a socket protocol) from the Tendermint Core (consensus engine and P2P layers) to avoid a "monolithic" blockchain "stack" design so not limited to just using language of the blockchain stack that compile down to say the Turing-complete bytecode of the Ethereum Virtual Machine (EVM) (i.e. Serpent, Solidity)
Allows BFT replication of applications with Transactions processed in any programming language
Security guarantees
ABCI has 3x Primary Message Types (delivered from Tendermint Core to the ABCI, then ABCI replies with response messages)
Applications may have Multiple ABCI Socket Connections (3x created by Tendermint Core), including (see Diagram in https://tendermint.readthedocs.io/en/master/introduction.html#intro-to-abci):
Application Logic Deterministic
Communication between TendermintCore and Tendermint Applications
Tendermint
Erlang
cd $GOPATH/src/github.com/tendermint/tendermint
git tag -l
git checkout v0.14.0