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.

217 lines
6.2 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "path/filepath"
  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.NewTMLogger(log.NewSyncWriter(os.Stdout)),
  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. var err error
  93. resp := abci.ResponseEndBlock{}
  94. if resp.ValidatorUpdates, err = app.validatorUpdates(uint64(req.Height)); err != nil {
  95. panic(err)
  96. }
  97. return resp
  98. }
  99. // Commit implements ABCI.
  100. func (app *Application) Commit() abci.ResponseCommit {
  101. height, hash, err := app.state.Commit()
  102. if err != nil {
  103. panic(err)
  104. }
  105. if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 {
  106. snapshot, err := app.snapshots.Create(app.state)
  107. if err != nil {
  108. panic(err)
  109. }
  110. logger.Info("Created state sync snapshot", "height", snapshot.Height)
  111. }
  112. retainHeight := int64(0)
  113. if app.cfg.RetainBlocks > 0 {
  114. retainHeight = int64(height - app.cfg.RetainBlocks + 1)
  115. }
  116. return abci.ResponseCommit{
  117. Data: hash,
  118. RetainHeight: retainHeight,
  119. }
  120. }
  121. // Query implements ABCI.
  122. func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
  123. return abci.ResponseQuery{
  124. Height: int64(app.state.Height),
  125. Key: req.Data,
  126. Value: []byte(app.state.Get(string(req.Data))),
  127. }
  128. }
  129. // ListSnapshots implements ABCI.
  130. func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots {
  131. snapshots, err := app.snapshots.List()
  132. if err != nil {
  133. panic(err)
  134. }
  135. return abci.ResponseListSnapshots{Snapshots: snapshots}
  136. }
  137. // LoadSnapshotChunk implements ABCI.
  138. func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk {
  139. chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk)
  140. if err != nil {
  141. panic(err)
  142. }
  143. return abci.ResponseLoadSnapshotChunk{Chunk: chunk}
  144. }
  145. // OfferSnapshot implements ABCI.
  146. func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
  147. if app.restoreSnapshot != nil {
  148. panic("A snapshot is already being restored")
  149. }
  150. app.restoreSnapshot = req.Snapshot
  151. app.restoreChunks = [][]byte{}
  152. return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
  153. }
  154. // ApplySnapshotChunk implements ABCI.
  155. func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk {
  156. if app.restoreSnapshot == nil {
  157. panic("No restore in progress")
  158. }
  159. app.restoreChunks = append(app.restoreChunks, req.Chunk)
  160. if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) {
  161. bz := []byte{}
  162. for _, chunk := range app.restoreChunks {
  163. bz = append(bz, chunk...)
  164. }
  165. err := app.state.Import(app.restoreSnapshot.Height, bz)
  166. if err != nil {
  167. panic(err)
  168. }
  169. app.restoreSnapshot = nil
  170. app.restoreChunks = nil
  171. }
  172. return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}
  173. }
  174. // validatorUpdates generates a validator set update.
  175. func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) {
  176. updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)]
  177. if len(updates) == 0 {
  178. return nil, nil
  179. }
  180. valUpdates := abci.ValidatorUpdates{}
  181. for keyString, power := range updates {
  182. keyBytes, err := base64.StdEncoding.DecodeString(keyString)
  183. if err != nil {
  184. return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err)
  185. }
  186. valUpdates = append(valUpdates, abci.Ed25519ValidatorUpdate(keyBytes, int64(power)))
  187. }
  188. return valUpdates, nil
  189. }
  190. // parseTx parses a tx in 'key=value' format into a key and value.
  191. func parseTx(tx []byte) (string, string, error) {
  192. parts := bytes.Split(tx, []byte("="))
  193. if len(parts) != 2 {
  194. return "", "", fmt.Errorf("invalid tx format: %q", string(tx))
  195. }
  196. if len(parts[0]) == 0 {
  197. return "", "", errors.New("key cannot be empty")
  198. }
  199. return string(parts[0]), string(parts[1]), nil
  200. }