You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
6.5 KiB

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