Tendermint profile

What is a Tendermint

Tendermint is a piece of software that can securely and consistently replicate an application on many machines. Security-wise, Tendermint works even if as many as a third of the machines fail in any way. In terms of consistency, every machine without a failure can see the same transaction log and calculate the same state. Security and consistent replication are basic problems in distributed systems. It plays a key role in the fault tolerance of a wide range of applications, from currency to elections to infrastructure orchestration.

The ability to tolerate machines failing in arbitrary ways, including becoming malicious, is known as Byzantine fault tolerance (BFT). The theory of BFT has been around for decades, but software implementations have only recently become popular, largely due to the success of “blockchain technologies” like Bitcoin and Ethereum. Blockchain technology is simply a re-formalization of BFT in a more modern environment, with an emphasis on peer-to-peer networking and cryptography. The name comes from the way transactions are batched in blocks, where each block contains a cryptographic hash of the previous block, forming a chain. In fact, blockchain data structures actually optimize THE BFT design.

The Tendermint consists of two major technology components: a blockchain consensus engine and a common application programming interface. The consistent engine, called Tendermint Core, ensures that the same transactions are recorded in the same order on every machine. Application program interfaces, called application Block Linking interfaces (ABCI), allow transactions to be processed in any programming language. Chain is different from other blocks and negotiate consistent solutions, these solutions are prepackaged in state machine (such as a fancy key repository, or a bizarre scripting language), the developer can use Tendermint to reproducing BFT state machine, these applications written in any programming language and development environment are correct.

Tendermint is designed to be easy to use, easy to understand, high-performance, and suitable for a wide range of distributed applications.

ABCI

Blockchain application interface, which allows any language to implement Byzantine fault-tolerant replication

So far, all blockchain “stacks” (like bitcoin) have had an overall design. That is, each blockchain stack is a single program that handles all the concerns of a decentralized ledger; This includes P2P connections, “mempool” transaction broadcasts, recent block consensus, account balances, Turing-complete contracts, user-level permissions, and so on.

Using monolithic architectures is generally a bad habit in computer science. This makes it difficult to reuse code components and very complex maintenance procedures for forking the code base. This is especially true when the code is not modular, as bad as “spaghetti code”.

Another problem with monolithic architecture design is that it limits the language in which you develop blockchain stacks (or vice versa). For example, Ethereum, which supports a Turing-complete bytecode virtual machine, limits the language you compile to bytecode; Currently, it only supports Serpent and Solidity.

In contrast, our approach is to decouple the consistent engine and P2P layer from the application-specific details of the current popular blockchain applications. We do this by abstracting the details of the application into an interface, which is implemented as the Socket protocol.

So we have an interface, the Blockchain Application Interface, which basically implements the Tendermint Socket protocol (TSP or Teaspoon)

For example, take bitcoin, one of the more well-known cryptocurrencies. Bitcoin is a blockchain cryptocurrency, and each node maintains a fully audited Unspent Transaction Output (UTXO) database. If you want to create a bitcoin-like system on ABCI, Tendermint Core will have a role to play

  • Spread blocks and transactions between different nodes
  • Establish authoritative/non-modifiable transaction orders (in blockchain)

The application needs to be responsible

  • Maintain the UTXO database
  • Verify the encrypted signature of the transaction
  • Block transactions that do not exist
  • Allows clients to query UTXO databases

From core delivery to application, ABCI contains three main message types.

  • DeliverTx messages are the heart of the app. Every transaction in the blockchain is delivered through this message. The app needs to verify that each DeliverTx message received for each transaction contains the current status, application protocol and password credentials. An authenticated transaction needs to update the state of the application, either by binding a value to the key-values store or by updating the UTXO database instance.

  • The CheckTx message is similar to DeliverTx, but it is only used to verify transactions. The memory pool at the Tendermint core first checks the validity of transactions using CheckTx and forwards only valid transactions to its peers. For example, an application can check the increasing sequence number in a transaction and return an error on CheckTx if the sequence number is old. Alternatively, they may use a functional-based system that needs to update functionality with each transaction.

  • The Commit message is used to calculate the encryption commitment for the current application state and place it in the next block title. This has some handy properties. Inconsistencies in updating status will now show up as blockchain forks, which catch a whole class of programming errors. This also simplifies the development of secure lightweight clients, because merkle-Hash proofs can be verified by examining block hashes, which are signed by a legal number.

The sample analysis

Notice the following code

Github.com/tendermint/…

The ABCI Application only needs to implement the Application interface

type Application interface {
		// Info/Query Connection
		Info(RequestInfo) ResponseInfo                // Return application info
		SetOption(RequestSetOption) ResponseSetOption // Set application option
		Query(RequestQuery) ResponseQuery             // Query for state


		// Mempool Connection
		CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool


		// Consensus Connection
		InitChain(RequestInitChain) ResponseInitChain    // Initialize blockchain with validators and other info from TendermintCore
		BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
		DeliverTx(tx []byte) ResponseDeliverTx           // Deliver a tx for full processing
		EndBlock(RequestEndBlock) ResponseEndBlock       // Signals the end of a block, returns changes to the validator set
		Commit() ResponseCommit                          // Commit the state and return the application Merkle root hash
	}
Copy the code

For example, the ABCI app Counter


typeCounterApplication struct {// Inherits basic blockchain types.BaseApplicationhashCount int
	txCount   int
	serial    bool
}

func NewCounterApplication(serial bool) *CounterApplication {
	return&CounterApplication{serial: Func (app *CounterApplication) Info(req types.requestInfo) types.responseinfo {return types.ResponseInfo{Data: cmn.Fmt("{\"hashes\":%v,\"txs\":%v}", app.hashCount, Func (app *CounterApplication) SetOption(req types.requestsetoption) types.responsesetOption {  key, value := req.Key, req.Valueif key == "serial" && value == "on" {
		app.serial = true
	} else {
		/*
			TODO Panic and have the ABCI server pass an exception.
			The client can call SetOptionSync() and get an `error`.
			return types.ResponseSetOption{
				Error: cmn.Fmt("Unknown key (%s) or value (%s)", key, value),
			}
		*/
		return types.ResponseSetOption{}
	}

	returnFunc (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {if app.serial {
		if len(tx) > 8 {
			return types.ResponseDeliverTx{
				Code: code.CodeTypeEncodingError,
				Log:  fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
		}
		tx8 := make([]byte, 8)
		copy(tx8[len(tx8)-len(tx):], tx)
		txValue := binary.BigEndian.Uint64(tx8)
		iftxValue ! = uint64(app.txCount) {return types.ResponseDeliverTx{
				Code: code.CodeTypeBadNonce,
				Log:  fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
		}
	}
	app.txCount++
	returntypes.ResponseDeliverTx{Code: Func (app *CounterApplication) CheckTx(tx []byte) types.ResponseCheckTx {if app.serial {
		if len(tx) > 8 {
			return types.ResponseCheckTx{
				Code: code.CodeTypeEncodingError,
				Log:  fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
		}
		tx8 := make([]byte, 8)
		copy(tx8[len(tx8)-len(tx):], tx)
		txValue := binary.BigEndian.Uint64(tx8)
		if txValue < uint64(app.txCount) {
			return types.ResponseCheckTx{
				Code: code.CodeTypeBadNonce,
				Log:  fmt.Sprintf("Invalid nonce. Expected >= %v, got %v", app.txCount, txValue)}
		}
	}
	returntypes.ResponseCheckTx{Code: Func (app *CounterApplication) Commit() (resp types.responsecommit) {app.hashcount ++if app.txCount == 0 {
		return types.ResponseCommit{}
	}
	hash := make([]byte, 8)
	binary.BigEndian.PutUint64(hash, uint64(app.txCount))
	return types.ResponseCommit{Data: hashFunc (app *CounterApplication) Query(reqQuery types.requestQuery) types.responsequery {switch reqQuery.Path {case "hash":
		return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.hashCount))}
	case "tx":
		return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.txCount))}
	default:
		return types.ResponseQuery{Log: cmn.Fmt("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
	}
}

Copy the code

The specific blockchain application data flow is shown in the figure

Customize your own blockchain program

The following example implements a Contract, namely ABCI Application

// empty implementation import ("github.com/tendermint/abci/example/code"
	"github.com/tendermint/tendermint/types"
)

typeStruct {app types.BaseApplication ValUpdates []types.Validator // Validator} func (app *Example) Info(req types.RequestInfo) types.ResponseInfo {return app.app.Info(req)
}

func (app *Example) SetOption(req types.RequestSetOption) types.ResponseSetOption {
	return app.app.SetOption(req)
}

func (app *Example) Query(req types.RequestQuery) types.ResponseQuery {
	returnReq} func (app *Example) CheckTx(tx []byte) types.ResponseCheckTx {// Own business logic processing layerreturn types.ResponseCheckTx{Code: code.CodeTypeOK}
}

func (app *Example) InitChain(req types.RequestInitChain) types.ResponseInitChain {
	for _, v := range req.Validators {
		r := app.updateValidator(v)
		if r.IsErr() {
			// Add Log
		}
	}
	return types.ResponseInitChain{}
}

func (app *Example) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
	app.ValUpdates = make([]types.Validator, 0)
	return types.ResponseBeginBlock{}
}

func (app *Example) DeliverTx(tx []byte) types.ResponseDeliverTx {
	return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}

func (app *Example) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
	return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
}

func (app *Example) Commit() types.ResponseCommit {
	returnFunc (app *Example) updateValidator(v types.validator) types.responseDelivertx {key := []byte("val:" + string(v.PubKey))
	ifVar power == 0 {// If the value of the validator is 0, remove it, otherwise add (persistent part) // remove the validator from db}else {
		// add or update validator to db
	}

	// we only update the changes array if we successfully updated the tree
	app.ValUpdates = append(app.ValUpdates, v)
	return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}

Copy the code

over…