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