github.com/soomindae/tendermint@v0.0.5-0.20210528140126-84a0c70c8162/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/soomindae/tm-db" 10 11 "github.com/soomindae/tendermint/abci/example/code" 12 "github.com/soomindae/tendermint/abci/types" 13 "github.com/soomindae/tendermint/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 }