github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/example/kvstore/persistent_kvstore.go (about) 1 package kvstore 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "fmt" 7 "strconv" 8 "strings" 9 10 "github.com/tendermint/tendermint/abci/types" 11 pc "github.com/tendermint/tendermint/proto/tendermint/crypto" 12 dbm "github.com/tendermint/tm-db" 13 14 "github.com/line/ostracon/abci/example/code" 15 ocabci "github.com/line/ostracon/abci/types" 16 cryptoenc "github.com/line/ostracon/crypto/encoding" 17 "github.com/line/ostracon/libs/log" 18 ) 19 20 const ( 21 ValidatorSetChangePrefix string = "val:" 22 ) 23 24 //----------------------------------------- 25 26 var _ ocabci.Application = (*PersistentKVStoreApplication)(nil) 27 28 type PersistentKVStoreApplication struct { 29 app *Application 30 31 // validator set 32 ValUpdates []types.ValidatorUpdate 33 34 valAddrToPubKeyMap map[string]pc.PublicKey 35 36 logger log.Logger 37 } 38 39 func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication { 40 name := "kvstore" 41 db, err := dbm.NewGoLevelDB(name, dbDir) 42 if err != nil { 43 panic(err) 44 } 45 46 state := loadState(db) 47 48 return &PersistentKVStoreApplication{ 49 app: &Application{state: state}, 50 valAddrToPubKeyMap: make(map[string]pc.PublicKey), 51 logger: log.NewNopLogger(), 52 } 53 } 54 55 func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) { 56 app.logger = l 57 } 58 59 func (app *PersistentKVStoreApplication) Info(req types.RequestInfo) types.ResponseInfo { 60 res := app.app.Info(req) 61 res.LastBlockHeight = app.app.state.Height 62 res.LastBlockAppHash = app.app.state.AppHash 63 return res 64 } 65 66 func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption { 67 return app.app.SetOption(req) 68 } 69 70 // tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes 71 func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { 72 // if it starts with "val:", update the validator set 73 // format is "val:pubkey!power" 74 if isValidatorTx(req.Tx) { 75 // update validators in the merkle tree 76 // and in app.ValUpdates 77 return app.execValidatorTx(req.Tx) 78 } 79 80 // otherwise, update the key-value store 81 return app.app.DeliverTx(req) 82 } 83 84 func (app *PersistentKVStoreApplication) CheckTxSync(req types.RequestCheckTx) ocabci.ResponseCheckTx { 85 return app.app.CheckTxSync(req) 86 } 87 88 func (app *PersistentKVStoreApplication) CheckTxAsync(req types.RequestCheckTx, callback ocabci.CheckTxCallback) { 89 app.app.CheckTxAsync(req, callback) 90 } 91 92 func (app *PersistentKVStoreApplication) BeginRecheckTx(req ocabci.RequestBeginRecheckTx) ocabci.ResponseBeginRecheckTx { 93 return app.app.BeginRecheckTx(req) 94 } 95 96 func (app *PersistentKVStoreApplication) EndRecheckTx(req ocabci.RequestEndRecheckTx) ocabci.ResponseEndRecheckTx { 97 return app.app.EndRecheckTx(req) 98 } 99 100 // Commit will panic if InitChain was not called 101 func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit { 102 return app.app.Commit() 103 } 104 105 // When path=/val and data={validator address}, returns the validator update (types.ValidatorUpdate) varint encoded. 106 // For any other path, returns an associated value or nil if missing. 107 func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { 108 switch reqQuery.Path { 109 case "/val": 110 key := []byte("val:" + string(reqQuery.Data)) 111 value, err := app.app.state.db.Get(key) 112 if err != nil { 113 panic(err) 114 } 115 116 resQuery.Key = reqQuery.Data 117 resQuery.Value = value 118 return 119 default: 120 return app.app.Query(reqQuery) 121 } 122 } 123 124 // Save the validators in the merkle tree 125 func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain { 126 for _, v := range req.Validators { 127 r := app.updateValidator(v) 128 if r.IsErr() { 129 app.logger.Error("Error updating validators", "r", r) 130 } 131 } 132 return types.ResponseInitChain{} 133 } 134 135 // Track the block hash and header information 136 func (app *PersistentKVStoreApplication) BeginBlock(req ocabci.RequestBeginBlock) types.ResponseBeginBlock { 137 // reset valset changes 138 app.ValUpdates = make([]types.ValidatorUpdate, 0) 139 140 // Punish validators who committed equivocation. 141 for _, ev := range req.ByzantineValidators { 142 if ev.Type == types.EvidenceType_DUPLICATE_VOTE { 143 addr := string(ev.Validator.Address) 144 if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok { 145 app.updateValidator(types.ValidatorUpdate{ 146 PubKey: pubKey, 147 Power: ev.Validator.Power - 1, 148 }) 149 app.logger.Info("Decreased val power by 1 because of the equivocation", 150 "val", addr) 151 } else { 152 app.logger.Error("Wanted to punish val, but can't find it", 153 "val", addr) 154 } 155 } 156 } 157 158 return types.ResponseBeginBlock{} 159 } 160 161 // Update the validator set 162 func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock { 163 return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates} 164 } 165 166 func (app *PersistentKVStoreApplication) ListSnapshots( 167 req types.RequestListSnapshots) types.ResponseListSnapshots { 168 return types.ResponseListSnapshots{} 169 } 170 171 func (app *PersistentKVStoreApplication) LoadSnapshotChunk( 172 req types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk { 173 return types.ResponseLoadSnapshotChunk{} 174 } 175 176 func (app *PersistentKVStoreApplication) OfferSnapshot( 177 req types.RequestOfferSnapshot) types.ResponseOfferSnapshot { 178 return types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT} 179 } 180 181 func (app *PersistentKVStoreApplication) ApplySnapshotChunk( 182 req types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk { 183 return types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT} 184 } 185 186 //--------------------------------------------- 187 // update validators 188 189 func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) { 190 itr, err := app.app.state.db.Iterator(nil, nil) 191 if err != nil { 192 panic(err) 193 } 194 for ; itr.Valid(); itr.Next() { 195 if isValidatorTx(itr.Key()) { 196 validator := new(types.ValidatorUpdate) 197 err := ocabci.ReadMessage(bytes.NewBuffer(itr.Value()), validator) 198 if err != nil { 199 panic(err) 200 } 201 validators = append(validators, *validator) 202 } 203 } 204 if err = itr.Error(); err != nil { 205 panic(err) 206 } 207 return 208 } 209 210 func MakeValSetChangeTx(pubkey pc.PublicKey, power int64) []byte { 211 _, tx := MakeValSetChangeTxAndMore(pubkey, power) 212 return []byte(tx) 213 } 214 215 func MakeValSetChangeTxAndMore(pubkey pc.PublicKey, power int64) (string, string) { 216 pkBytes, err := pubkey.Marshal() 217 if err != nil { 218 panic(err) 219 } 220 pubStr := base64.StdEncoding.EncodeToString(pkBytes) 221 return pubStr, fmt.Sprintf("val:%s!%d", pubStr, power) 222 } 223 224 func isValidatorTx(tx []byte) bool { 225 return strings.HasPrefix(string(tx), ValidatorSetChangePrefix) 226 } 227 228 // format is "val:pubkey!power" 229 // pubkey is a base64-encoded proto.ostracon.crypto.PublicKey bytes 230 // See MakeValSetChangeTx 231 func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { 232 tx = tx[len(ValidatorSetChangePrefix):] 233 234 // get the pubkey and power 235 pubKeyAndPower := strings.Split(string(tx), "!") 236 if len(pubKeyAndPower) != 2 { 237 return types.ResponseDeliverTx{ 238 Code: code.CodeTypeEncodingError, 239 Log: fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)} 240 } 241 pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1] 242 243 // decode the pubkey 244 pkBytes, err := base64.StdEncoding.DecodeString(pubkeyS) 245 if err != nil { 246 return types.ResponseDeliverTx{ 247 Code: code.CodeTypeEncodingError, 248 Log: fmt.Sprintf("pubkeyS (%s) is invalid base64", pubkeyS)} 249 } 250 var pkProto pc.PublicKey 251 err = pkProto.Unmarshal(pkBytes) 252 if err != nil { 253 return types.ResponseDeliverTx{ 254 Code: code.CodeTypeEncodingError, 255 Log: fmt.Sprintf("pkBytes (%x) is invalid binary", pkBytes)} 256 } 257 pubkey, err := cryptoenc.PubKeyFromProto(&pkProto) 258 if err != nil { 259 return types.ResponseDeliverTx{ 260 Code: code.CodeTypeEncodingError, 261 Log: fmt.Sprintf("pkProto (%s) is invalid binary", pkProto)} 262 } 263 264 // decode the power 265 power, err := strconv.ParseInt(powerS, 10, 64) 266 if err != nil { 267 return types.ResponseDeliverTx{ 268 Code: code.CodeTypeEncodingError, 269 Log: fmt.Sprintf("Power (%s) is not an int", powerS)} 270 } 271 272 // update 273 return app.updateValidator(ocabci.NewValidatorUpdate(pubkey, power)) 274 } 275 276 // add, update, or remove a validator 277 // See MakeValSetChangeTx 278 func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx { 279 pubkey, err := cryptoenc.PubKeyFromProto(&v.PubKey) 280 if err != nil { 281 return types.ResponseDeliverTx{ 282 Code: code.CodeTypeEncodingError, 283 Log: fmt.Sprintf("Error encoding Public Key: %s", err)} 284 } 285 pubStr, _ := MakeValSetChangeTxAndMore(v.PubKey, v.Power) 286 key := []byte("val:" + pubStr) 287 288 if v.Power == 0 { 289 // remove validator 290 hasKey, err := app.app.state.db.Has(key) 291 if err != nil { 292 panic(err) 293 } 294 if !hasKey { 295 return types.ResponseDeliverTx{ 296 Code: code.CodeTypeUnauthorized, 297 Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)} 298 } 299 if err = app.app.state.db.Delete(key); err != nil { 300 panic(err) 301 } 302 delete(app.valAddrToPubKeyMap, string(pubkey.Address())) 303 } else { 304 // add or update validator 305 value := bytes.NewBuffer(make([]byte, 0)) 306 if err := types.WriteMessage(&v, value); err != nil { 307 return types.ResponseDeliverTx{ 308 Code: code.CodeTypeEncodingError, 309 Log: fmt.Sprintf("Error encoding validator: %v", err)} 310 } 311 if err = app.app.state.db.Set(key, value.Bytes()); err != nil { 312 panic(err) 313 } 314 app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey 315 } 316 317 // we only update the changes array if we successfully updated the tree 318 app.ValUpdates = append(app.ValUpdates, v) 319 320 return types.ResponseDeliverTx{Code: code.CodeTypeOK} 321 }