github.com/MagHErmit/tendermint@v0.282.1/test/e2e/app/app.go (about)

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