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.

285 lines
8.4 KiB

  1. package app
  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. // Config allows for the setting of high level parameters for running the e2e Application
  27. // KeyType and ValidatorUpdates must be the same for all nodes running the same application.
  28. type Config struct {
  29. // The directory with which state.json will be persisted in. Usually $HOME/.tendermint/data
  30. Dir string `toml:"dir"`
  31. // SnapshotInterval specifies the height interval at which the application
  32. // will take state sync snapshots. Defaults to 0 (disabled).
  33. SnapshotInterval uint64 `toml:"snapshot_interval"`
  34. // RetainBlocks specifies the number of recent blocks to retain. Defaults to
  35. // 0, which retains all blocks. Must be greater that PersistInterval,
  36. // SnapshotInterval and EvidenceAgeHeight.
  37. RetainBlocks uint64 `toml:"retain_blocks"`
  38. // KeyType sets the curve that will be used by validators.
  39. // Options are ed25519 & secp256k1
  40. KeyType string `toml:"key_type"`
  41. // PersistInterval specifies the height interval at which the application
  42. // will persist state to disk. Defaults to 1 (every height), setting this to
  43. // 0 disables state persistence.
  44. PersistInterval uint64 `toml:"persist_interval"`
  45. // ValidatorUpdates is a map of heights to validator names and their power,
  46. // and will be returned by the ABCI application. For example, the following
  47. // changes the power of validator01 and validator02 at height 1000:
  48. //
  49. // [validator_update.1000]
  50. // validator01 = 20
  51. // validator02 = 10
  52. //
  53. // Specifying height 0 returns the validator update during InitChain. The
  54. // application returns the validator updates as-is, i.e. removing a
  55. // validator must be done by returning it with power 0, and any validators
  56. // not specified are not changed.
  57. //
  58. // height <-> pubkey <-> voting power
  59. ValidatorUpdates map[string]map[string]uint8 `toml:"validator_update"`
  60. }
  61. func DefaultConfig(dir string) *Config {
  62. return &Config{
  63. PersistInterval: 1,
  64. SnapshotInterval: 100,
  65. Dir: dir,
  66. }
  67. }
  68. // NewApplication creates the application.
  69. func NewApplication(cfg *Config) (*Application, error) {
  70. state, err := NewState(filepath.Join(cfg.Dir, "state.json"), cfg.PersistInterval)
  71. if err != nil {
  72. return nil, err
  73. }
  74. snapshots, err := NewSnapshotStore(filepath.Join(cfg.Dir, "snapshots"))
  75. if err != nil {
  76. return nil, err
  77. }
  78. return &Application{
  79. logger: log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false),
  80. state: state,
  81. snapshots: snapshots,
  82. cfg: cfg,
  83. }, nil
  84. }
  85. // Info implements ABCI.
  86. func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo {
  87. return abci.ResponseInfo{
  88. Version: version.ABCIVersion,
  89. AppVersion: 1,
  90. LastBlockHeight: int64(app.state.Height),
  91. LastBlockAppHash: app.state.Hash,
  92. }
  93. }
  94. // Info implements ABCI.
  95. func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
  96. var err error
  97. app.state.initialHeight = uint64(req.InitialHeight)
  98. if len(req.AppStateBytes) > 0 {
  99. err = app.state.Import(0, req.AppStateBytes)
  100. if err != nil {
  101. panic(err)
  102. }
  103. }
  104. resp := abci.ResponseInitChain{
  105. AppHash: app.state.Hash,
  106. }
  107. if resp.Validators, err = app.validatorUpdates(0); err != nil {
  108. panic(err)
  109. }
  110. return resp
  111. }
  112. // CheckTx implements ABCI.
  113. func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
  114. _, _, err := parseTx(req.Tx)
  115. if err != nil {
  116. return abci.ResponseCheckTx{
  117. Code: code.CodeTypeEncodingError,
  118. Log: err.Error(),
  119. }
  120. }
  121. return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
  122. }
  123. // DeliverTx implements ABCI.
  124. func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
  125. key, value, err := parseTx(req.Tx)
  126. if err != nil {
  127. panic(err) // shouldn't happen since we verified it in CheckTx
  128. }
  129. app.state.Set(key, value)
  130. return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
  131. }
  132. // EndBlock implements ABCI.
  133. func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
  134. valUpdates, err := app.validatorUpdates(uint64(req.Height))
  135. if err != nil {
  136. panic(err)
  137. }
  138. return abci.ResponseEndBlock{
  139. ValidatorUpdates: valUpdates,
  140. Events: []abci.Event{
  141. {
  142. Type: "val_updates",
  143. Attributes: []abci.EventAttribute{
  144. {
  145. Key: "size",
  146. Value: strconv.Itoa(valUpdates.Len()),
  147. },
  148. {
  149. Key: "height",
  150. Value: strconv.Itoa(int(req.Height)),
  151. },
  152. },
  153. },
  154. },
  155. }
  156. }
  157. // Commit implements ABCI.
  158. func (app *Application) Commit() abci.ResponseCommit {
  159. height, hash, err := app.state.Commit()
  160. if err != nil {
  161. panic(err)
  162. }
  163. if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 {
  164. snapshot, err := app.snapshots.Create(app.state)
  165. if err != nil {
  166. panic(err)
  167. }
  168. app.logger.Info("Created state sync snapshot", "height", snapshot.Height)
  169. }
  170. retainHeight := int64(0)
  171. if app.cfg.RetainBlocks > 0 {
  172. retainHeight = int64(height - app.cfg.RetainBlocks + 1)
  173. }
  174. return abci.ResponseCommit{
  175. Data: hash,
  176. RetainHeight: retainHeight,
  177. }
  178. }
  179. // Query implements ABCI.
  180. func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
  181. return abci.ResponseQuery{
  182. Height: int64(app.state.Height),
  183. Key: req.Data,
  184. Value: []byte(app.state.Get(string(req.Data))),
  185. }
  186. }
  187. // ListSnapshots implements ABCI.
  188. func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots {
  189. snapshots, err := app.snapshots.List()
  190. if err != nil {
  191. panic(err)
  192. }
  193. return abci.ResponseListSnapshots{Snapshots: snapshots}
  194. }
  195. // LoadSnapshotChunk implements ABCI.
  196. func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk {
  197. chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk)
  198. if err != nil {
  199. panic(err)
  200. }
  201. return abci.ResponseLoadSnapshotChunk{Chunk: chunk}
  202. }
  203. // OfferSnapshot implements ABCI.
  204. func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
  205. if app.restoreSnapshot != nil {
  206. panic("A snapshot is already being restored")
  207. }
  208. app.restoreSnapshot = req.Snapshot
  209. app.restoreChunks = [][]byte{}
  210. return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
  211. }
  212. // ApplySnapshotChunk implements ABCI.
  213. func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk {
  214. if app.restoreSnapshot == nil {
  215. panic("No restore in progress")
  216. }
  217. app.restoreChunks = append(app.restoreChunks, req.Chunk)
  218. if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) {
  219. bz := []byte{}
  220. for _, chunk := range app.restoreChunks {
  221. bz = append(bz, chunk...)
  222. }
  223. err := app.state.Import(app.restoreSnapshot.Height, bz)
  224. if err != nil {
  225. panic(err)
  226. }
  227. app.restoreSnapshot = nil
  228. app.restoreChunks = nil
  229. }
  230. return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}
  231. }
  232. // validatorUpdates generates a validator set update.
  233. func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) {
  234. updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)]
  235. if len(updates) == 0 {
  236. return nil, nil
  237. }
  238. valUpdates := abci.ValidatorUpdates{}
  239. for keyString, power := range updates {
  240. keyBytes, err := base64.StdEncoding.DecodeString(keyString)
  241. if err != nil {
  242. return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err)
  243. }
  244. valUpdates = append(valUpdates, abci.UpdateValidator(keyBytes, int64(power), app.cfg.KeyType))
  245. }
  246. return valUpdates, nil
  247. }
  248. // parseTx parses a tx in 'key=value' format into a key and value.
  249. func parseTx(tx []byte) (string, string, error) {
  250. parts := bytes.Split(tx, []byte("="))
  251. if len(parts) != 2 {
  252. return "", "", fmt.Errorf("invalid tx format: %q", string(tx))
  253. }
  254. if len(parts[0]) == 0 {
  255. return "", "", errors.New("key cannot be empty")
  256. }
  257. return string(parts[0]), string(parts[1]), nil
  258. }