tendermint執行流程
阿新 • • 發佈:2018-12-14
首先放兩段程式碼
第一段是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, "")
}