github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/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/franono/tendermint/abci/example/code" 12 "github.com/franono/tendermint/abci/types" 13 "github.com/franono/tendermint/version" 14 ) 15 16 var ( 17 stateKey = []byte("stateKey") 18 kvPairPrefixKey = []byte("kvPairKey:") 19 20 ProtocolVersion version.Protocol = 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 state.db.Set(stateKey, stateBytes) 53 } 54 55 func prefixKey(key []byte) []byte { 56 return append(kvPairPrefixKey, key...) 57 } 58 59 //--------------------------------------------------- 60 61 var _ types.Application = (*Application)(nil) 62 63 type Application struct { 64 types.BaseApplication 65 66 state State 67 RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight) 68 } 69 70 func NewApplication() *Application { 71 state := loadState(dbm.NewMemDB()) 72 return &Application{state: state} 73 } 74 75 func (app *Application) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { 76 return types.ResponseInfo{ 77 Data: fmt.Sprintf("{\"size\":%v}", app.state.Size), 78 Version: version.ABCIVersion, 79 AppVersion: ProtocolVersion.Uint64(), 80 LastBlockHeight: app.state.Height, 81 LastBlockAppHash: app.state.AppHash, 82 } 83 } 84 85 // tx is either "key=value" or just arbitrary bytes 86 func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { 87 var key, value []byte 88 parts := bytes.Split(req.Tx, []byte("=")) 89 if len(parts) == 2 { 90 key, value = parts[0], parts[1] 91 } else { 92 key, value = req.Tx, req.Tx 93 } 94 95 app.state.db.Set(prefixKey(key), value) 96 app.state.Size++ 97 98 events := []types.Event{ 99 { 100 Type: "app", 101 Attributes: []types.EventAttribute{ 102 {Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")}, 103 {Key: []byte("key"), Value: key}, 104 {Key: []byte("index_key"), Value: []byte("index is working"), Index: true}, 105 {Key: []byte("noindex_key"), Value: []byte("index is working"), Index: false}, 106 }, 107 }, 108 } 109 110 return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events} 111 } 112 113 func (app *Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { 114 return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} 115 } 116 117 func (app *Application) Commit() types.ResponseCommit { 118 // Using a memdb - just return the big endian size of the db 119 appHash := make([]byte, 8) 120 binary.PutVarint(appHash, app.state.Size) 121 app.state.AppHash = appHash 122 app.state.Height++ 123 saveState(app.state) 124 125 resp := types.ResponseCommit{Data: appHash} 126 if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks { 127 resp.RetainHeight = app.state.Height - app.RetainBlocks + 1 128 } 129 return resp 130 } 131 132 // Returns an associated value or nil if missing. 133 func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { 134 if reqQuery.Prove { 135 value, err := app.state.db.Get(prefixKey(reqQuery.Data)) 136 if err != nil { 137 panic(err) 138 } 139 if value == nil { 140 resQuery.Log = "does not exist" 141 } else { 142 resQuery.Log = "exists" 143 } 144 resQuery.Index = -1 // TODO make Proof return index 145 resQuery.Key = reqQuery.Data 146 resQuery.Value = value 147 resQuery.Height = app.state.Height 148 149 return 150 } 151 152 resQuery.Key = reqQuery.Data 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.Value = value 163 resQuery.Height = app.state.Height 164 165 return resQuery 166 }