1. 程式人生 > >tendermint執行流程

tendermint執行流程

首先放兩段程式碼

第一段是tendermint abci的程式碼

// +build trade

package trade

import (
	"dev.33.cn/33/btrade/tradeserver"
	"github.com/tendermint/abci/types"
)

type TradeApplication struct {
	state *tradeserver.TradeServer
}

func NewTradeApplication(dbDir string) *TradeApplication {
	state, err := tradeserver.Init(dbDir, "trade")
	if err != nil {
		panic(err)
	}
	return &TradeApplication{state: state}
}

func (app *TradeApplication) Info() (resInfo types.ResponseInfo) {
	return app.state.Info()
}

func (app *TradeApplication) SetOption(key string, value string) (log string) {
	return app.state.SetOption(key, value)
}

func (app *TradeApplication) DeliverTx(tx []byte) types.Result {
	result, err := app.state.Exec(tx)
	if err != nil {
		return types.NewResult(types.CodeType_InternalError, []byte(err.Error()), "")
	}
	return types.NewResultOK(result, "")
}

func (app *TradeApplication) CheckTx(tx []byte) types.Result {
	err := app.state.Check(tx)
	if err != nil {
		return types.NewResult(types.CodeType_InternalError, []byte(err.Error()), "")
	}
	return types.OK
}

func (app *TradeApplication) Commit() types.Result {
	return app.state.Commit()
}

func (app *TradeApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
	value, err := app.state.Query(reqQuery.Data)
	if err != nil {
		resQuery.Code = types.CodeType_InternalError
		resQuery.Log = err.Error()
	}

	resQuery.Key = reqQuery.Data
	resQuery.Value = value
	return
}

// Save the validators in the merkle tree
func (app *TradeApplication) InitChain(validators []*types.Validator) {

}

// Track the block hash and header information
func (app *TradeApplication) BeginBlock(hash []byte, header *types.Header) {
	app.state.BeginBlock(hash, header)
}

// Update the validator set
func (app *TradeApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
	return app.state.EndBlock(height)
}

第二段是做交易所的時候,測試呼叫的程式碼,用來模仿tendermint的執行步驟

func send(request *msq.WriteRequest) (*msq.Response, error) {
	height++
	hash := []byte("foo")
	header := &types.Header{
		Height: height,
	}

	trade.BeginBlock(hash, header)
	//先是req  再是request加密
	data, err := msq.MarshalMessage(request)
	if err != nil {
		return nil, err
	}

	err = trade.Check(data)
	if err != nil {
		return nil, err
	}

	res, err := trade.Exec(data)
	if err != nil {
		return nil, err
	}

	var resp msq.Response
	err = msq.UnmarshalMessage(res, &resp)
	if err != nil {
		return nil, err
	}

	trade.EndBlock(height)

	trade.Commit()
	return &resp, nil
}

可以看到,在測試程式碼中,先是定義了一個區塊header,然後呼叫BeginBlock,來準備區塊所需要的東西。可以看一下他的程式碼,頭部的height++了,然後替換頭部,別的東西都重置。


func (s *TradeServer) BeginBlock(hash []byte, header *types.Header) {
	// update latest block info
	s.blockHeader = header

	// reset valset changes
	s.changes = make([]*types.Validator, 0)

	// clear for new block
	txEvents = []*msq.Event{}
	txResults = []*msq.TxResult{}
	txInsIds = []int64{}
}

接下來是把資料編碼,第二步呼叫Check方法,對請求驗證他的合法性,基本上是對請求引數的一些校驗,是否大於0之類的。

第三步是Exec,具體執行請求裡的操作,一些業務程式碼,比如註冊使用者之類的。

第四步  EndBlock

// Update the validator set
func (s *TradeServer) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
	return types.ResponseEndBlock{Diffs: s.changes}
}

最後是commit,儲存所需要的資料及區塊

func (s *TradeServer) Commit() types.Result {
	batch := ldb.NewBatch()

	// save lastblock
	s.lastBlock.Height = s.blockHeader.Height
	s.lastBlock.AppHash = s.Hash() //塊hash
	buf, err := msq.MarshalMessage(s.lastBlock)
	if err != nil {
		panic("cannot save last block")
	}
	batch.Put(lastBlockKey, buf)

	// save events
	putItems, delItems := s.processEvents(txEvents)
	for key, value := range putItems {
		//log.Println("process.put", key)
		batch.Put([]byte(key), value)
	}
	for key, _ := range delItems {
		//log.Println("process.del", key)
		batch.Delete([]byte(key))
	}

	batch.Commit()

	// after Commit, the matchcommon event has been set to db
	// when next time restart, app will known the last exec id
	// So, there is no need to store lastExecId additionly

	log.Printf("Saving block height: %v appHash: %X", s.lastBlock.Height, s.lastBlock.AppHash)
	return types.NewResultOK(s.lastBlock.AppHash, "")
}