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.

319 lines
9.2 KiB

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