github.com/vipernet-xyz/tm@v0.34.24/abci/example/kvstore/kvstore.go (about)

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