github.com/okex/exchain@v1.8.0/libs/tendermint/abci/example/kvstore/kvstore.go (about)

     1  package kvstore
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"fmt"
     8  	"math/big"
     9  
    10  	sdk "github.com/okex/exchain/libs/cosmos-sdk/types"
    11  
    12  	dbm "github.com/okex/exchain/libs/tm-db"
    13  
    14  	"github.com/okex/exchain/libs/tendermint/abci/example/code"
    15  	"github.com/okex/exchain/libs/tendermint/abci/types"
    16  	"github.com/okex/exchain/libs/tendermint/libs/kv"
    17  	"github.com/okex/exchain/libs/tendermint/version"
    18  )
    19  
    20  var (
    21  	stateKey        = []byte("stateKey")
    22  	kvPairPrefixKey = []byte("kvPairKey:")
    23  
    24  	ProtocolVersion version.Protocol = 0x1
    25  )
    26  
    27  type State struct {
    28  	db      dbm.DB
    29  	Size    int64  `json:"size"`
    30  	Height  int64  `json:"height"`
    31  	AppHash []byte `json:"app_hash"`
    32  }
    33  
    34  func loadState(db dbm.DB) State {
    35  	var state State
    36  	state.db = db
    37  	stateBytes, err := db.Get(stateKey)
    38  	if err != nil {
    39  		panic(err)
    40  	}
    41  	if len(stateBytes) == 0 {
    42  		return state
    43  	}
    44  	err = json.Unmarshal(stateBytes, &state)
    45  	if err != nil {
    46  		panic(err)
    47  	}
    48  	return state
    49  }
    50  
    51  func saveState(state State) {
    52  	stateBytes, err := json.Marshal(state)
    53  	if err != nil {
    54  		panic(err)
    55  	}
    56  	state.db.Set(stateKey, stateBytes)
    57  }
    58  
    59  func prefixKey(key []byte) []byte {
    60  	return append(kvPairPrefixKey, key...)
    61  }
    62  
    63  //---------------------------------------------------
    64  
    65  type MockExTxInfo struct {
    66  	Sender      string   `json:"sender"`
    67  	SenderNonce uint64   `json:"sender_nonce"`
    68  	GasPrice    *big.Int `json:"gas_price"`
    69  	Nonce       uint64   `json:"nonce"`
    70  }
    71  
    72  var _ types.Application = (*Application)(nil)
    73  
    74  type Application struct {
    75  	types.BaseApplication
    76  
    77  	state        State
    78  	RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight)
    79  }
    80  
    81  func NewApplication() *Application {
    82  	state := loadState(dbm.NewMemDB())
    83  	return &Application{state: state}
    84  }
    85  
    86  func (app *Application) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
    87  	return types.ResponseInfo{
    88  		Data:             fmt.Sprintf("{\"size\":%v}", app.state.Size),
    89  		Version:          version.ABCIVersion,
    90  		AppVersion:       ProtocolVersion.Uint64(),
    91  		LastBlockHeight:  app.state.Height,
    92  		LastBlockAppHash: app.state.AppHash,
    93  	}
    94  }
    95  
    96  // tx is either "key=value" or just arbitrary bytes
    97  func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
    98  	var key, value []byte
    99  	parts := bytes.Split(req.Tx, []byte("="))
   100  	if len(parts) == 2 {
   101  		key, value = parts[0], parts[1]
   102  	} else {
   103  		key, value = req.Tx, req.Tx
   104  	}
   105  
   106  	app.state.db.Set(prefixKey(key), value)
   107  	app.state.Size++
   108  
   109  	events := []types.Event{
   110  		{
   111  			Type: "app",
   112  			Attributes: []kv.Pair{
   113  				{Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")},
   114  				{Key: []byte("key"), Value: key},
   115  			},
   116  		},
   117  	}
   118  
   119  	return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events}
   120  }
   121  
   122  type tx struct {
   123  	sdk.BaseTx
   124  }
   125  
   126  func (tx *tx) GetGasPrice() *big.Int {
   127  	return big.NewInt(1)
   128  }
   129  
   130  func (app *Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
   131  	data, _ := json.Marshal(&MockExTxInfo{Sender: fmt.Sprintf("%x", req.Tx), GasPrice: big.NewInt(1)})
   132  	return types.ResponseCheckTx{Tx: &tx{BaseTx: sdk.BaseTx{Raw: req.Tx, From: fmt.Sprintf("%x", req.Tx)}}, Code: code.CodeTypeOK, GasWanted: 1, Data: data}
   133  }
   134  
   135  func (app *Application) Commit(req types.RequestCommit) types.ResponseCommit {
   136  	// Using a memdb - just return the big endian size of the db
   137  	appHash := make([]byte, 8)
   138  	binary.PutVarint(appHash, app.state.Size)
   139  	app.state.AppHash = appHash
   140  	app.state.Height++
   141  	saveState(app.state)
   142  
   143  	resp := types.ResponseCommit{Data: appHash}
   144  	if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks {
   145  		resp.RetainHeight = app.state.Height - app.RetainBlocks + 1
   146  	}
   147  	return resp
   148  }
   149  
   150  // Returns an associated value or nil if missing.
   151  func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
   152  	if reqQuery.Prove {
   153  		value, err := app.state.db.Get(prefixKey(reqQuery.Data))
   154  		if err != nil {
   155  			panic(err)
   156  		}
   157  		if value == nil {
   158  			resQuery.Log = "does not exist"
   159  		} else {
   160  			resQuery.Log = "exists"
   161  		}
   162  		resQuery.Index = -1 // TODO make Proof return index
   163  		resQuery.Key = reqQuery.Data
   164  		resQuery.Value = value
   165  		resQuery.Height = app.state.Height
   166  
   167  		return
   168  	}
   169  
   170  	resQuery.Key = reqQuery.Data
   171  	value, err := app.state.db.Get(prefixKey(reqQuery.Data))
   172  	if err != nil {
   173  		panic(err)
   174  	}
   175  	if value == nil {
   176  		resQuery.Log = "does not exist"
   177  	} else {
   178  		resQuery.Log = "exists"
   179  	}
   180  	resQuery.Value = value
   181  	resQuery.Height = app.state.Height
   182  
   183  	return resQuery
   184  }