首先放两段代码
第一段是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, "")
}