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.

286 lines
8.4 KiB

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