bitbucket.org/number571/tendermint@v0.8.14/test/e2e/app/app.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "path/filepath" 9 "strconv" 10 11 "bitbucket.org/number571/tendermint/abci/example/code" 12 abci "bitbucket.org/number571/tendermint/abci/types" 13 "bitbucket.org/number571/tendermint/libs/log" 14 "bitbucket.org/number571/tendermint/version" 15 ) 16 17 // Application is an ABCI application for use by end-to-end tests. It is a 18 // simple key/value store for strings, storing data in memory and persisting 19 // to disk as JSON, taking state sync snapshots if requested. 20 type Application struct { 21 abci.BaseApplication 22 logger log.Logger 23 state *State 24 snapshots *SnapshotStore 25 cfg *Config 26 restoreSnapshot *abci.Snapshot 27 restoreChunks [][]byte 28 } 29 30 // NewApplication creates the application. 31 func NewApplication(cfg *Config) (*Application, error) { 32 state, err := NewState(filepath.Join(cfg.Dir, "state.json"), cfg.PersistInterval) 33 if err != nil { 34 return nil, err 35 } 36 snapshots, err := NewSnapshotStore(filepath.Join(cfg.Dir, "snapshots")) 37 if err != nil { 38 return nil, err 39 } 40 return &Application{ 41 logger: log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false), 42 state: state, 43 snapshots: snapshots, 44 cfg: cfg, 45 }, nil 46 } 47 48 // Info implements ABCI. 49 func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo { 50 return abci.ResponseInfo{ 51 Version: version.ABCIVersion, 52 AppVersion: 1, 53 LastBlockHeight: int64(app.state.Height), 54 LastBlockAppHash: app.state.Hash, 55 } 56 } 57 58 // Info implements ABCI. 59 func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { 60 var err error 61 app.state.initialHeight = uint64(req.InitialHeight) 62 if len(req.AppStateBytes) > 0 { 63 err = app.state.Import(0, req.AppStateBytes) 64 if err != nil { 65 panic(err) 66 } 67 } 68 resp := abci.ResponseInitChain{ 69 AppHash: app.state.Hash, 70 } 71 if resp.Validators, err = app.validatorUpdates(0); err != nil { 72 panic(err) 73 } 74 return resp 75 } 76 77 // CheckTx implements ABCI. 78 func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { 79 _, _, err := parseTx(req.Tx) 80 if err != nil { 81 return abci.ResponseCheckTx{ 82 Code: code.CodeTypeEncodingError, 83 Log: err.Error(), 84 } 85 } 86 return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} 87 } 88 89 // DeliverTx implements ABCI. 90 func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { 91 key, value, err := parseTx(req.Tx) 92 if err != nil { 93 panic(err) // shouldn't happen since we verified it in CheckTx 94 } 95 app.state.Set(key, value) 96 return abci.ResponseDeliverTx{Code: code.CodeTypeOK} 97 } 98 99 // EndBlock implements ABCI. 100 func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { 101 valUpdates, err := app.validatorUpdates(uint64(req.Height)) 102 if err != nil { 103 panic(err) 104 } 105 106 return abci.ResponseEndBlock{ 107 ValidatorUpdates: valUpdates, 108 Events: []abci.Event{ 109 { 110 Type: "val_updates", 111 Attributes: []abci.EventAttribute{ 112 { 113 Key: "size", 114 Value: strconv.Itoa(valUpdates.Len()), 115 }, 116 { 117 Key: "height", 118 Value: strconv.Itoa(int(req.Height)), 119 }, 120 }, 121 }, 122 }, 123 } 124 } 125 126 // Commit implements ABCI. 127 func (app *Application) Commit() abci.ResponseCommit { 128 height, hash, err := app.state.Commit() 129 if err != nil { 130 panic(err) 131 } 132 if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 { 133 snapshot, err := app.snapshots.Create(app.state) 134 if err != nil { 135 panic(err) 136 } 137 logger.Info("Created state sync snapshot", "height", snapshot.Height) 138 } 139 retainHeight := int64(0) 140 if app.cfg.RetainBlocks > 0 { 141 retainHeight = int64(height - app.cfg.RetainBlocks + 1) 142 } 143 return abci.ResponseCommit{ 144 Data: hash, 145 RetainHeight: retainHeight, 146 } 147 } 148 149 // Query implements ABCI. 150 func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery { 151 return abci.ResponseQuery{ 152 Height: int64(app.state.Height), 153 Key: req.Data, 154 Value: []byte(app.state.Get(string(req.Data))), 155 } 156 } 157 158 // ListSnapshots implements ABCI. 159 func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { 160 snapshots, err := app.snapshots.List() 161 if err != nil { 162 panic(err) 163 } 164 return abci.ResponseListSnapshots{Snapshots: snapshots} 165 } 166 167 // LoadSnapshotChunk implements ABCI. 168 func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { 169 chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk) 170 if err != nil { 171 panic(err) 172 } 173 return abci.ResponseLoadSnapshotChunk{Chunk: chunk} 174 } 175 176 // OfferSnapshot implements ABCI. 177 func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { 178 if app.restoreSnapshot != nil { 179 panic("A snapshot is already being restored") 180 } 181 app.restoreSnapshot = req.Snapshot 182 app.restoreChunks = [][]byte{} 183 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} 184 } 185 186 // ApplySnapshotChunk implements ABCI. 187 func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { 188 if app.restoreSnapshot == nil { 189 panic("No restore in progress") 190 } 191 app.restoreChunks = append(app.restoreChunks, req.Chunk) 192 if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) { 193 bz := []byte{} 194 for _, chunk := range app.restoreChunks { 195 bz = append(bz, chunk...) 196 } 197 err := app.state.Import(app.restoreSnapshot.Height, bz) 198 if err != nil { 199 panic(err) 200 } 201 app.restoreSnapshot = nil 202 app.restoreChunks = nil 203 } 204 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} 205 } 206 207 // validatorUpdates generates a validator set update. 208 func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) { 209 updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)] 210 if len(updates) == 0 { 211 return nil, nil 212 } 213 214 valUpdates := abci.ValidatorUpdates{} 215 for keyString, power := range updates { 216 217 keyBytes, err := base64.StdEncoding.DecodeString(keyString) 218 if err != nil { 219 return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err) 220 } 221 valUpdates = append(valUpdates, abci.UpdateValidator(keyBytes, int64(power), app.cfg.KeyType)) 222 } 223 return valUpdates, nil 224 } 225 226 // parseTx parses a tx in 'key=value' format into a key and value. 227 func parseTx(tx []byte) (string, string, error) { 228 parts := bytes.Split(tx, []byte("=")) 229 if len(parts) != 2 { 230 return "", "", fmt.Errorf("invalid tx format: %q", string(tx)) 231 } 232 if len(parts[0]) == 0 { 233 return "", "", errors.New("key cannot be empty") 234 } 235 return string(parts[0]), string(parts[1]), nil 236 }