github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go (about) 1 package kvstore 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "log/slog" 7 "strconv" 8 "strings" 9 10 "github.com/gnolang/gno/tm2/pkg/amino" 11 "github.com/gnolang/gno/tm2/pkg/bft/abci/example/errors" 12 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 13 "github.com/gnolang/gno/tm2/pkg/crypto" 14 "github.com/gnolang/gno/tm2/pkg/db" 15 _ "github.com/gnolang/gno/tm2/pkg/db/goleveldb" 16 "github.com/gnolang/gno/tm2/pkg/log" 17 ) 18 19 const ( 20 ValidatorUpdatePrefix string = "val:" 21 ValidatorKeyPrefix string = "/val/" 22 ) 23 24 const dbBackend = db.GoLevelDBBackend 25 26 // ----------------------------------------- 27 28 var _ abci.Application = (*PersistentKVStoreApplication)(nil) 29 30 type PersistentKVStoreApplication struct { 31 app *KVStoreApplication 32 33 // validator set 34 ValSetChanges []abci.ValidatorUpdate 35 36 logger *slog.Logger 37 } 38 39 func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication { 40 name := "kvstore" 41 db, err := db.NewDB(name, dbBackend, dbDir) 42 if err != nil { 43 panic(err) 44 } 45 46 state := loadState(db) 47 48 return &PersistentKVStoreApplication{ 49 app: &KVStoreApplication{state: state}, 50 logger: log.NewNoopLogger(), 51 } 52 } 53 54 func (app *PersistentKVStoreApplication) SetLogger(l *slog.Logger) { 55 app.logger = l 56 } 57 58 func (app *PersistentKVStoreApplication) Info(req abci.RequestInfo) abci.ResponseInfo { 59 res := app.app.Info(req) 60 res.LastBlockHeight = app.app.state.Height 61 res.LastBlockAppHash = app.app.state.AppHash 62 return res 63 } 64 65 func (app *PersistentKVStoreApplication) SetOption(req abci.RequestSetOption) abci.ResponseSetOption { 66 return app.app.SetOption(req) 67 } 68 69 // tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes 70 func (app *PersistentKVStoreApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 71 // if it starts with "val:", update the validator set 72 // format is "val:pubkey!power" 73 if isValidatorTx(req.Tx) { 74 // update validators in the merkle tree 75 // and in app.ValSetChanges 76 return app.execValidatorTx(req.Tx) 77 } 78 79 // otherwise, update the key-value store 80 return app.app.DeliverTx(req) 81 } 82 83 func (app *PersistentKVStoreApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 84 return app.app.CheckTx(req) 85 } 86 87 // Commit will panic if InitChain was not called 88 func (app *PersistentKVStoreApplication) Commit() abci.ResponseCommit { 89 return app.app.Commit() 90 } 91 92 // When path=/val and data={validator address}, returns the validator update (abci.ValidatorUpdate) varint encoded. 93 // For any other path, returns an associated value or nil if missing. 94 func (app *PersistentKVStoreApplication) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { 95 switch reqQuery.Path { 96 case "/val": 97 key := []byte(ValidatorUpdatePrefix + string(reqQuery.Data)) 98 value := app.app.state.db.Get(key) 99 100 resQuery.Key = reqQuery.Data 101 resQuery.Value = value 102 return 103 default: 104 return app.app.Query(reqQuery) 105 } 106 } 107 108 // Save the validators in the merkle tree 109 func (app *PersistentKVStoreApplication) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { 110 for _, v := range req.Validators { 111 r := app.updateValidator(v) 112 if r.IsErr() { 113 app.logger.Error("Error updating validators", "r", r) 114 } 115 } 116 return abci.ResponseInitChain{} 117 } 118 119 // Track the block hash and header information 120 func (app *PersistentKVStoreApplication) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { 121 // reset valset changes 122 app.ValSetChanges = make([]abci.ValidatorUpdate, 0) 123 124 /* REMOVE 125 for _, vio := range req.Violations { 126 if _, ok := vio.Evidence.(*tmtypes.DuplicateVoteEvidence); ok { 127 for _, val := range vio.Validators { 128 // decrease voting power of each by 1 129 if val.Power == 0 { 130 continue 131 } 132 app.updateValidator(abci.ValidatorUpdate{ 133 Address: val.PubKey.Address(), 134 PubKey: val.PubKey, 135 Power: val.Power - 1, 136 }) 137 } 138 } 139 } 140 */ 141 return abci.ResponseBeginBlock{} 142 } 143 144 // Update the validator set 145 func (app *PersistentKVStoreApplication) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 146 return abci.ResponseEndBlock{ValidatorUpdates: app.ValSetChanges} 147 } 148 149 // --------------------------------------------- 150 // update validators 151 152 func (app *PersistentKVStoreApplication) Validators() (validators []abci.ValidatorUpdate) { 153 itr := app.app.state.db.Iterator(nil, nil) 154 for ; itr.Valid(); itr.Next() { 155 if isValidatorKey(itr.Key()) { 156 validator := new(abci.ValidatorUpdate) 157 amino.MustUnmarshal(itr.Value(), validator) 158 validators = append(validators, *validator) 159 } 160 } 161 return 162 } 163 164 func makeValidatorKey(val abci.ValidatorUpdate) []byte { 165 return []byte(fmt.Sprintf("%s%X", ValidatorKeyPrefix, val.PubKey.Address())) 166 } 167 168 func isValidatorKey(tx []byte) bool { 169 return strings.HasPrefix(string(tx), ValidatorKeyPrefix) 170 } 171 172 func MakeValSetChangeTx(pubkey crypto.PubKey, power int64) []byte { 173 pubkeyS := base64.StdEncoding.EncodeToString(pubkey.Bytes()) 174 return []byte(fmt.Sprintf("%s%s!%d", ValidatorUpdatePrefix, pubkeyS, power)) 175 } 176 177 func isValidatorTx(tx []byte) bool { 178 return strings.HasPrefix(string(tx), ValidatorUpdatePrefix) 179 } 180 181 // format is "val:pubkey!power" 182 // pubkey is a base64-encoded 32-byte ed25519 key 183 func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) (res abci.ResponseDeliverTx) { 184 tx = tx[len(ValidatorUpdatePrefix):] 185 186 // get the pubkey and power 187 pubKeyAndPower := strings.Split(string(tx), "!") 188 if len(pubKeyAndPower) != 2 { 189 res.Error = errors.EncodingError{} 190 res.Log = fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower) 191 return 192 } 193 pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1] 194 195 // decode the pubkey 196 bz, err := base64.StdEncoding.DecodeString(pubkeyS) 197 if err != nil { 198 res.Error = errors.EncodingError{} 199 res.Log = fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS) 200 return 201 } 202 var pubkey crypto.PubKey 203 amino.MustUnmarshal(bz, &pubkey) 204 205 // decode the power 206 power, err := strconv.ParseInt(powerS, 10, 64) 207 if err != nil { 208 res.Error = errors.EncodingError{} 209 res.Log = fmt.Sprintf("Power (%s) is not an int", powerS) 210 return 211 } 212 213 // update 214 return app.updateValidator(abci.ValidatorUpdate{pubkey.Address(), pubkey, power}) 215 } 216 217 // add, update, or remove a validator 218 func (app *PersistentKVStoreApplication) updateValidator(val abci.ValidatorUpdate) (res abci.ResponseDeliverTx) { 219 if val.Power == 0 { 220 // remove validator 221 if !app.app.state.db.Has(makeValidatorKey(val)) { 222 res.Error = errors.UnauthorizedError{} 223 res.Log = fmt.Sprintf("Cannot remove non-existent validator %s", val.PubKey.String()) 224 return res 225 } 226 app.app.state.db.Delete(makeValidatorKey(val)) 227 } else { 228 // add or update validator 229 bz := amino.MustMarshal(val) 230 app.app.state.db.Set(makeValidatorKey(val), bz) 231 } 232 233 // we only update the changes array if we successfully updated the tree 234 app.ValSetChanges = append(app.ValSetChanges, val) 235 236 return abci.ResponseDeliverTx{} 237 } 238 239 func (app *PersistentKVStoreApplication) Close() error { 240 return app.app.Close() 241 }