github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/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 11 "github.com/lazyledger/lazyledger-core/abci/example/code" 12 abci "github.com/lazyledger/lazyledger-core/abci/types" 13 "github.com/lazyledger/lazyledger-core/libs/log" 14 "github.com/lazyledger/lazyledger-core/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.NewTMLogger(log.NewSyncWriter(os.Stdout)), 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 // InitChain 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 var err error 102 resp := abci.ResponseEndBlock{} 103 if resp.ValidatorUpdates, err = app.validatorUpdates(uint64(req.Height)); err != nil { 104 panic(err) 105 } 106 return resp 107 } 108 109 // Commit implements ABCI. 110 func (app *Application) Commit() abci.ResponseCommit { 111 height, hash, err := app.state.Commit() 112 if err != nil { 113 panic(err) 114 } 115 if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 { 116 snapshot, err := app.snapshots.Create(app.state) 117 if err != nil { 118 panic(err) 119 } 120 logger.Info("Created state sync snapshot", "height", snapshot.Height) 121 } 122 retainHeight := int64(0) 123 if app.cfg.RetainBlocks > 0 { 124 retainHeight = int64(height - app.cfg.RetainBlocks + 1) 125 } 126 return abci.ResponseCommit{ 127 Data: hash, 128 RetainHeight: retainHeight, 129 } 130 } 131 132 // Query implements ABCI. 133 func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery { 134 return abci.ResponseQuery{ 135 Height: int64(app.state.Height), 136 Key: req.Data, 137 Value: []byte(app.state.Get(string(req.Data))), 138 } 139 } 140 141 // ListSnapshots implements ABCI. 142 func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { 143 snapshots, err := app.snapshots.List() 144 if err != nil { 145 panic(err) 146 } 147 return abci.ResponseListSnapshots{Snapshots: snapshots} 148 } 149 150 // LoadSnapshotChunk implements ABCI. 151 func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { 152 chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk) 153 if err != nil { 154 panic(err) 155 } 156 return abci.ResponseLoadSnapshotChunk{Chunk: chunk} 157 } 158 159 // OfferSnapshot implements ABCI. 160 func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { 161 if app.restoreSnapshot != nil { 162 panic("A snapshot is already being restored") 163 } 164 app.restoreSnapshot = req.Snapshot 165 app.restoreChunks = [][]byte{} 166 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} 167 } 168 169 // ApplySnapshotChunk implements ABCI. 170 func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { 171 if app.restoreSnapshot == nil { 172 panic("No restore in progress") 173 } 174 app.restoreChunks = append(app.restoreChunks, req.Chunk) 175 if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) { 176 bz := []byte{} 177 for _, chunk := range app.restoreChunks { 178 bz = append(bz, chunk...) 179 } 180 err := app.state.Import(app.restoreSnapshot.Height, bz) 181 if err != nil { 182 panic(err) 183 } 184 app.restoreSnapshot = nil 185 app.restoreChunks = nil 186 } 187 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} 188 } 189 190 // PreprocessTxs implements ABCI 191 func (app *Application) PreprocessTxs( 192 req abci.RequestPreprocessTxs) abci.ResponsePreprocessTxs { 193 return abci.ResponsePreprocessTxs{Txs: req.Txs} 194 } 195 196 // validatorUpdates generates a validator set update. 197 func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) { 198 updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)] 199 if len(updates) == 0 { 200 return nil, nil 201 } 202 203 valUpdates := abci.ValidatorUpdates{} 204 for keyString, power := range updates { 205 206 keyBytes, err := base64.StdEncoding.DecodeString(keyString) 207 if err != nil { 208 return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err) 209 } 210 valUpdates = append(valUpdates, abci.UpdateValidator(keyBytes, int64(power), app.cfg.KeyType)) 211 } 212 return valUpdates, nil 213 } 214 215 // parseTx parses a tx in 'key=value' format into a key and value. 216 func parseTx(tx []byte) (string, string, error) { 217 parts := bytes.Split(tx, []byte("=")) 218 if len(parts) != 2 { 219 return "", "", fmt.Errorf("invalid tx format: %q", string(tx)) 220 } 221 if len(parts[0]) == 0 { 222 return "", "", errors.New("key cannot be empty") 223 } 224 return string(parts[0]), string(parts[1]), nil 225 }