Tendermint 101 — Blockchain future is here
The distributed systems are plagued with fundamental problems like security, reliability, swiftness, and consistency. With the wind of Web 3.0 blowing all over the world, the applications of blockchain have extended from currencies to e-voting to infrastructure orchestration and so on!
Tendermint is an attempt to solve the problems distributed systems have to face. Tendermint emerged in the tradition of cryptocurrencies like Bitcoin, Ethereum, etc. with the goal of providing a more efficient and secure consensus algorithm than Bitcoin’s Proof of Work.
Tendermint is software for securely and consistently replicating an application on many machines.
→ Tendermint works even if up to 1/3 of machines fail in arbitrary ways — it is byzantine fault tolerant (BFT).
→ Every non-faulty machine sees the same transaction log and computes the same state.
Features of Tendermint
Tendermint consists of two chief technical components:
‡ Blockchain consensus engine and (Tendermint Core) ,
‡ Generic application interface (Application BlockChain Interface (ABCI)).
Tendermint core can host arbitrary application states. So blockchain software written in any language: Haskell, or GoLang, or Rust can be run as ABCI application.
Present blockchains are monoliths!
Take up bitcoin for an example. It has a monolithic design. The blockchain stack of bitcoin is a single program that handles everything from connectivity to broadcasting, to achieving consensus, to check account balances etc.
Monoliths (mostly) reflect bad practitioner’s approach.
Tendermint aims to decouple consensus engine (i.e. Tendermint Core) and P2P connectivity from the main application. Any programming language can be used to develop ABCI.
Without further delay let’s dive right into Tendermint core!
Step 1: Download Tendermint Core
Caution: you may have to add ‘tendermint’ command to your binary explicitly.
Step 2: Initialise Tendermint
$ tendermint init
Should get the following output:
I[10–18|20:14:08.996] Generated private validator module=main path=/Users/niharikasingh/.tendermint/config/priv_validator.jsonI[10–18|20:14:08.996] Generated node key module=main path=/Users/niharikasingh/.tendermint/config/node_key.jsonI[10–18|20:14:08.996] Generated genesis file module=main path=/Users/niharikasingh/.tendermint/config/genesis.json
Step 3: Start your machine as Tendermint node
$ tendermint node — proxy_app=kvstore
You will see the following (never ending) output:
I[10–18|20:16:40.037] Starting multiAppConn module=proxy impl=multiAppConnI[10–18|20:16:40.038] Starting localClient module=abci-client connection=query impl=localClientI[10–18|20:16:40.038] Starting localClient module=abci-client connection=mempool impl=localClientI[10–18|20:16:40.038] Starting localClient module=abci-client connection=consensus impl=localClientI[10–18|20:16:40.038] ABCI Handshake module=consensus appHeight=0 appHash=I[10–18|20:16:40.038] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0I[10–18|20:16:40.038] Completed ABCI Handshake — Tendermint and App are synced module=consensus appHeight=0 appHash=I[10–18|20:16:40.038] This node is a validator module=consensus addr=601302EBD1F8B4BCE9F99B219965F2796AB6BB10 pubKey=PubKeyEd25519{991BF1D2F72E5204B752B9A1EB604A9B07945F51891CE93D22DD14FB3E630B0C}I[10–18|20:16:40.044] P2P Node ID module=p2p ID=7cb3c8fa590fc5aadf16a475a544bbc1368acc4f file=/Users/niharikasingh/.tendermint/config/node_key.jsonI[10–18|20:16:40.044] Starting Node module=main impl=NodeI[10–18|20:16:40.044] Starting EventBus module=events impl=EventBusI[10–18|20:16:40.045] Starting RPC HTTP server on tcp://0.0.0.0:26657 module=rpc-serverI[10–18|20:16:40.046] Starting P2P Switch module=p2p impl=”P2P Switch”I[10–18|20:16:40.046] Starting MempoolReactor module=mempool impl=MempoolReactorI[10–18|20:16:40.046] Starting BlockchainReactor module=blockchain impl=BlockchainReactorI[10–18|20:16:40.046] Starting ConsensusReactor module=consensus impl=ConsensusReactorI[10–18|20:16:40.046] ConsensusReactor module=consensus fastSync=falseI[10–18|20:16:40.047] Starting ConsensusState module=consensus impl=ConsensusStateI[10–18|20:16:40.048] Starting baseWAL module=consensus wal=/Users/niharikasingh/.tendermint/data/cs.wal/wal impl=baseWALI[10–18|20:16:40.048] Starting TimeoutTicker module=consensus impl=TimeoutTickerI[10–18|20:16:40.048] Catchup by replaying consensus messages module=consensus height=1I[10–18|20:16:40.048] Replay: Done module=consensusI[10–18|20:16:40.048] Starting EvidenceReactor module=evidence impl=EvidenceReactorI[10–18|20:16:40.048] Starting PEXReactor module=p2p impl=PEXReactorI[10–18|20:16:40.048] Starting AddrBook module=p2p book=/Users/niharikasingh/.tendermint/config/addrbook.json impl=AddrBookI[10–18|20:16:40.048] Starting IndexerService module=txindex impl=IndexerServiceI[10–18|20:16:40.048] Started node module=main nodeInfo=”NodeInfo{id: 7cb3c8fa590fc5aadf16a475a544bbc1368acc4f, moniker: Niharikas-MacBook-Pro.local, network: test-chain-pmZKFH [listen tcp://0.0.0.0:26656], version: 0.25.0–0c9c3292 ({amino_version: 0.12.0, p2p_version: 0.5.0, consensus_version: v1/0.2.2, rpc_version: 0.7.0/3, tx_index: on, rpc_address: tcp://0.0.0.0:26657})}”I[10–18|20:16:40.049] Ensure peers module=p2p numOutPeers=0 numInPeers=0 numDialing=0 numToDial=10I[10–18|20:16:40.049] No addresses to dial nor connected peers. Falling back to seeds module=p2pI[10–18|20:16:41.042] Timed out module=consensus dur=993.37484ms height=1 round=0 step=RoundStepNewHeightI[10–18|20:16:41.042] enterNewRound(1/0). Current: 1/0/RoundStepNewHeight module=consensus height=1 round=0I[10–18|20:16:41.042] enterPropose(1/0). Current: 1/0/RoundStepNewRound module=consensus height=1 round=0I[10–18|20:16:41.042] enterPropose: Our turn to propose module=consensus height=1 round=0 proposer=601302EBD1F8B4BCE9F99B219965F2796AB6BB10 privValidator=”PrivValidator{601302EBD1F8B4BCE9F99B219965F2796AB6BB10 LH:0, LR:0, LS:0}”I[10–18|20:16:41.044] Signed proposal module=consensus height=1 round=0 proposal=”Proposal{1/0 1:43021D31FC0A (-1,:0:000000000000) B1C53868762E @ 2018–10–18T14:46:41.044131868Z}”I[10–18|20:16:41.045] Received proposal module=consensus proposal=”Proposal{1/0 1:43021D31FC0A (-1,:0:000000000000) B1C53868762E @ 2018–10–18T14:46:41.044131868Z}”I[10–18|20:16:41.045] Received complete proposal block module=consensus height=1 hash=7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6I[10–18|20:16:41.045] enterPrevote(1/0). Current: 1/0/RoundStepPropose module=consensusI[10–18|20:16:41.045] enterPrevote: ProposalBlock is valid module=consensus height=1 round=0I[10–18|20:16:41.046] Signed and pushed vote module=consensus height=1 round=0 vote=”Vote{0:601302EBD1F8 1/00/1(Prevote) 7C902D710CE0 2F3FB8AE1AB4 @ 2018–10–18T14:46:42.042791392Z}” err=nullI[10–18|20:16:41.047] Added to prevote module=consensus vote=”Vote{0:601302EBD1F8 1/00/1(Prevote) 7C902D710CE0 2F3FB8AE1AB4 @ 2018–10–18T14:46:42.042791392Z}” prevotes=”VoteSet{H:1 R:0 T:1 +2/3:7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6:1:43021D31FC0A(1) BA{1:x} map[]}”I[10–18|20:16:41.047] enterPrecommit(1/0). Current: 1/0/RoundStepPrevote module=consensus height=1 round=0I[10–18|20:16:41.047] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus height=1 round=0 hash=7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6I[10–18|20:16:41.048] Signed and pushed vote module=consensus height=1 round=0 vote=”Vote{0:601302EBD1F8 1/00/2(Precommit) 7C902D710CE0 AF3DD9DE247F @ 2018–10–18T14:46:42.042791392Z}” err=nullI[10–18|20:16:41.049] Added to precommit module=consensus vote=”Vote{0:601302EBD1F8 1/00/2(Precommit) 7C902D710CE0 AF3DD9DE247F @ 2018–10–18T14:46:42.042791392Z}” precommits=”VoteSet{H:1 R:0 T:2 +2/3:7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6:1:43021D31FC0A(1) BA{1:x} map[]}”I[10–18|20:16:41.049] enterCommit(1/0). Current: 1/0/RoundStepPrecommit module=consensus height=1 commitRound=0I[10–18|20:16:41.049] Commit is for locked block. Set ProposalBlock=LockedBlock module=consensus height=1 commitRound=0 blockHash=7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6I[10–18|20:16:41.049] Finalizing commit of block with 0 txs module=consensus height=1 hash=7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6 root=I[10–18|20:16:41.049] Block{Header{ChainID: test-chain-pmZKFHHeight: 1Time: 2018–10–18 14:46:41.042791392 +0000 UTCNumTxs: 0TotalTxs: 0LastBlockID: :0:000000000000LastCommit:Data:Validators: 6F80F8CDC51E593CACFD38A763836FAB50095B38NextValidators: 6F80F8CDC51E593CACFD38A763836FAB50095B38App:Consensus: 0E520AF30D47BE28F293E040E418D0361BFB5370Results:Evidence:Proposer: 601302EBD1F8B4BCE9F99B219965F2796AB6BB10}#7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6Data{}#EvidenceData{}#Commit{BlockID: :0:000000000000Precommits:}#}#7C902D710CE0BF50306E5BC1BDBB2F7CB5E703B6 module=consensusI[10–18|20:16:41.050] Executed block module=state height=1 validTxs=0 invalidTxs=0I[10–18|20:16:41.050] Committed state module=state height=1 txs=0 appHash=0000000000000000I[10–18|20:16:41.050] Recheck txs module=mempool numtxs=0 height=1I[10–18|20:16:41.051] Indexed block module=txindex height=1I[10–18|20:16:42.051] Timed out module=consensus dur=998.105755ms height=2 round=0 step=RoundStepNewHeightI[10–18|20:16:42.051] enterNewRound(2/0). Current: 2/0/RoundStepNewHeight module=consensus height=2 round=0I[10–18|20:16:42.051] enterPropose(2/0). Current: 2/0/RoundStepNewRound module=consensus height=2 round=0I[10–18|20:16:42.051] enterPropose: Our turn to propose module=consensus height=2 round=0 proposer=601302EBD1F8B4BCE9F99B219965F2796AB6BB10 privValidator=”PrivValidator{601302EBD1F8B4BCE9F99B219965F2796AB6BB10 LH:1, LR:0, LS:3}”I[10–18|20:16:42.055] Signed proposal module=consensus height=2 round=0 proposal=”Proposal{2/0 1:48B45F4423A5 (-1,:0:000000000000) F52DF1F111D8 @ 2018–10–18T14:46:42.051967933Z}”I[10–18|20:16:42.056] Received proposal module=consensus proposal=”Proposal{2/0 1:48B45F4423A5 (-1,
Step 4: Transactions
To send a transaction, use curl
to make requests to the Tendermint RPC server, for example:
$ curl http://localhost:26657/broadcast_tx_commit?tx=\"niharika\"
Note the value
in the result (bmloYXJpa2E
); this is the base64-encoding of the ASCII of niharika
Let’s query the same!
$ curl -s 'localhost:26657/abci_query?data="niharika"'
This clearly shows that everything is working fine!
This was a very basic primer to get started with Tendermint.