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.

236 lines
6.5 KiB

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