github.com/Oyster-zx/tendermint@v0.34.24-fork/test/e2e/app/app.go (about) 1 package app 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strconv" 11 12 "github.com/tendermint/tendermint/abci/example/code" 13 abci "github.com/tendermint/tendermint/abci/types" 14 "github.com/tendermint/tendermint/libs/log" 15 "github.com/tendermint/tendermint/version" 16 ) 17 18 const appVersion = 1 19 20 // Application is an ABCI application for use by end-to-end tests. It is a 21 // simple key/value store for strings, storing data in memory and persisting 22 // to disk as JSON, taking state sync snapshots if requested. 23 24 type Application struct { 25 abci.BaseApplication 26 logger log.Logger 27 state *State 28 snapshots *SnapshotStore 29 cfg *Config 30 restoreSnapshot *abci.Snapshot 31 restoreChunks [][]byte 32 } 33 34 // Config allows for the setting of high level parameters for running the e2e Application 35 // KeyType and ValidatorUpdates must be the same for all nodes running the same application. 36 type Config struct { 37 // The directory with which state.json will be persisted in. Usually $HOME/.tendermint/data 38 Dir string `toml:"dir"` 39 40 // SnapshotInterval specifies the height interval at which the application 41 // will take state sync snapshots. Defaults to 0 (disabled). 42 SnapshotInterval uint64 `toml:"snapshot_interval"` 43 44 // RetainBlocks specifies the number of recent blocks to retain. Defaults to 45 // 0, which retains all blocks. Must be greater that PersistInterval, 46 // SnapshotInterval and EvidenceAgeHeight. 47 RetainBlocks uint64 `toml:"retain_blocks"` 48 49 // KeyType sets the curve that will be used by validators. 50 // Options are ed25519 & secp256k1 51 KeyType string `toml:"key_type"` 52 53 // PersistInterval specifies the height interval at which the application 54 // will persist state to disk. Defaults to 1 (every height), setting this to 55 // 0 disables state persistence. 56 PersistInterval uint64 `toml:"persist_interval"` 57 58 // ValidatorUpdates is a map of heights to validator names and their power, 59 // and will be returned by the ABCI application. For example, the following 60 // changes the power of validator01 and validator02 at height 1000: 61 // 62 // [validator_update.1000] 63 // validator01 = 20 64 // validator02 = 10 65 // 66 // Specifying height 0 returns the validator update during InitChain. The 67 // application returns the validator updates as-is, i.e. removing a 68 // validator must be done by returning it with power 0, and any validators 69 // not specified are not changed. 70 // 71 // height <-> pubkey <-> voting power 72 ValidatorUpdates map[string]map[string]uint8 `toml:"validator_update"` 73 } 74 75 func DefaultConfig(dir string) *Config { 76 return &Config{ 77 PersistInterval: 1, 78 SnapshotInterval: 100, 79 Dir: dir, 80 } 81 } 82 83 // NewApplication creates the application. 84 func NewApplication(cfg *Config) (*Application, error) { 85 state, err := NewState(cfg.Dir, cfg.PersistInterval) 86 if err != nil { 87 return nil, err 88 } 89 snapshots, err := NewSnapshotStore(filepath.Join(cfg.Dir, "snapshots")) 90 if err != nil { 91 return nil, err 92 } 93 return &Application{ 94 logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)), 95 state: state, 96 snapshots: snapshots, 97 cfg: cfg, 98 }, nil 99 } 100 101 // Info implements ABCI. 102 func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo { 103 return abci.ResponseInfo{ 104 Version: version.ABCIVersion, 105 AppVersion: appVersion, 106 LastBlockHeight: int64(app.state.Height), 107 LastBlockAppHash: app.state.Hash, 108 } 109 } 110 111 // Info implements ABCI. 112 func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { 113 var err error 114 app.state.initialHeight = uint64(req.InitialHeight) 115 if len(req.AppStateBytes) > 0 { 116 err = app.state.Import(0, req.AppStateBytes) 117 if err != nil { 118 panic(err) 119 } 120 } 121 resp := abci.ResponseInitChain{ 122 AppHash: app.state.Hash, 123 } 124 if resp.Validators, err = app.validatorUpdates(0); err != nil { 125 panic(err) 126 } 127 return resp 128 } 129 130 // CheckTx implements ABCI. 131 func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 132 _, _, err := parseTx(req.Tx) 133 if err != nil { 134 return abci.ResponseCheckTx{ 135 Code: code.CodeTypeEncodingError, 136 Log: err.Error(), 137 } 138 } 139 return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} 140 } 141 142 // DeliverTx implements ABCI. 143 func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 144 key, value, err := parseTx(req.Tx) 145 if err != nil { 146 panic(err) // shouldn't happen since we verified it in CheckTx 147 } 148 app.state.Set(key, value) 149 return abci.ResponseDeliverTx{Code: code.CodeTypeOK} 150 } 151 152 // EndBlock implements ABCI. 153 func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 154 valUpdates, err := app.validatorUpdates(uint64(req.Height)) 155 if err != nil { 156 panic(err) 157 } 158 159 return abci.ResponseEndBlock{ 160 ValidatorUpdates: valUpdates, 161 Events: []abci.Event{ 162 { 163 Type: "val_updates", 164 Attributes: []abci.EventAttribute{ 165 { 166 Key: []byte("size"), 167 Value: []byte(strconv.Itoa(valUpdates.Len())), 168 }, 169 { 170 Key: []byte("height"), 171 Value: []byte(strconv.Itoa(int(req.Height))), 172 }, 173 }, 174 }, 175 }, 176 } 177 } 178 179 // Commit implements ABCI. 180 func (app *Application) Commit() abci.ResponseCommit { 181 height, hash, err := app.state.Commit() 182 if err != nil { 183 panic(err) 184 } 185 if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 { 186 snapshot, err := app.snapshots.Create(app.state) 187 if err != nil { 188 panic(err) 189 } 190 app.logger.Info("Created state sync snapshot", "height", snapshot.Height) 191 } 192 retainHeight := int64(0) 193 if app.cfg.RetainBlocks > 0 { 194 retainHeight = int64(height - app.cfg.RetainBlocks + 1) 195 } 196 return abci.ResponseCommit{ 197 Data: hash, 198 RetainHeight: retainHeight, 199 } 200 } 201 202 // Query implements ABCI. 203 func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery { 204 return abci.ResponseQuery{ 205 Height: int64(app.state.Height), 206 Key: req.Data, 207 Value: []byte(app.state.Get(string(req.Data))), 208 } 209 } 210 211 // ListSnapshots implements ABCI. 212 func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { 213 snapshots, err := app.snapshots.List() 214 if err != nil { 215 panic(err) 216 } 217 return abci.ResponseListSnapshots{Snapshots: snapshots} 218 } 219 220 // LoadSnapshotChunk implements ABCI. 221 func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { 222 chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk) 223 if err != nil { 224 panic(err) 225 } 226 return abci.ResponseLoadSnapshotChunk{Chunk: chunk} 227 } 228 229 // OfferSnapshot implements ABCI. 230 func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { 231 if app.restoreSnapshot != nil { 232 panic("A snapshot is already being restored") 233 } 234 app.restoreSnapshot = req.Snapshot 235 app.restoreChunks = [][]byte{} 236 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} 237 } 238 239 // ApplySnapshotChunk implements ABCI. 240 func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { 241 if app.restoreSnapshot == nil { 242 panic("No restore in progress") 243 } 244 app.restoreChunks = append(app.restoreChunks, req.Chunk) 245 if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) { 246 bz := []byte{} 247 for _, chunk := range app.restoreChunks { 248 bz = append(bz, chunk...) 249 } 250 err := app.state.Import(app.restoreSnapshot.Height, bz) 251 if err != nil { 252 panic(err) 253 } 254 app.restoreSnapshot = nil 255 app.restoreChunks = nil 256 } 257 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} 258 } 259 260 func (app *Application) Rollback() error { 261 return app.state.Rollback() 262 } 263 264 // validatorUpdates generates a validator set update. 265 func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) { 266 updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)] 267 if len(updates) == 0 { 268 return nil, nil 269 } 270 271 valUpdates := abci.ValidatorUpdates{} 272 for keyString, power := range updates { 273 274 keyBytes, err := base64.StdEncoding.DecodeString(keyString) 275 if err != nil { 276 return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err) 277 } 278 valUpdates = append(valUpdates, abci.UpdateValidator(keyBytes, int64(power), app.cfg.KeyType)) 279 } 280 return valUpdates, nil 281 } 282 283 // parseTx parses a tx in 'key=value' format into a key and value. 284 func parseTx(tx []byte) (string, string, error) { 285 parts := bytes.Split(tx, []byte("=")) 286 if len(parts) != 2 { 287 return "", "", fmt.Errorf("invalid tx format: %q", string(tx)) 288 } 289 if len(parts[0]) == 0 { 290 return "", "", errors.New("key cannot be empty") 291 } 292 return string(parts[0]), string(parts[1]), nil 293 }