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