github.com/soomindae/tendermint@v0.0.5-0.20210528140126-84a0c70c8162/test/e2e/app/app.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  
    12  	"github.com/soomindae/tendermint/abci/example/code"
    13  	abci "github.com/soomindae/tendermint/abci/types"
    14  	"github.com/soomindae/tendermint/libs/log"
    15  	"github.com/soomindae/tendermint/version"
    16  )
    17  
    18  // Application is an ABCI application for use by end-to-end tests. It is a
    19  // simple key/value store for strings, storing data in memory and persisting
    20  // to disk as JSON, taking state sync snapshots if requested.
    21  type Application struct {
    22  	abci.BaseApplication
    23  	logger          log.Logger
    24  	state           *State
    25  	snapshots       *SnapshotStore
    26  	cfg             *Config
    27  	restoreSnapshot *abci.Snapshot
    28  	restoreChunks   [][]byte
    29  }
    30  
    31  // NewApplication creates the application.
    32  func NewApplication(cfg *Config) (*Application, error) {
    33  	state, err := NewState(filepath.Join(cfg.Dir, "state.json"), cfg.PersistInterval)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	snapshots, err := NewSnapshotStore(filepath.Join(cfg.Dir, "snapshots"))
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	return &Application{
    42  		logger:    log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
    43  		state:     state,
    44  		snapshots: snapshots,
    45  		cfg:       cfg,
    46  	}, nil
    47  }
    48  
    49  // Info implements ABCI.
    50  func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo {
    51  	return abci.ResponseInfo{
    52  		Version:          version.ABCIVersion,
    53  		AppVersion:       1,
    54  		LastBlockHeight:  int64(app.state.Height),
    55  		LastBlockAppHash: app.state.Hash,
    56  	}
    57  }
    58  
    59  // Info implements ABCI.
    60  func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
    61  	var err error
    62  	app.state.initialHeight = uint64(req.InitialHeight)
    63  	if len(req.AppStateBytes) > 0 {
    64  		err = app.state.Import(0, req.AppStateBytes)
    65  		if err != nil {
    66  			panic(err)
    67  		}
    68  	}
    69  	resp := abci.ResponseInitChain{
    70  		AppHash: app.state.Hash,
    71  	}
    72  	if resp.Validators, err = app.validatorUpdates(0); err != nil {
    73  		panic(err)
    74  	}
    75  	return resp
    76  }
    77  
    78  // CheckTx implements ABCI.
    79  func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
    80  	_, _, err := parseTx(req.Tx)
    81  	if err != nil {
    82  		return abci.ResponseCheckTx{
    83  			Code: code.CodeTypeEncodingError,
    84  			Log:  err.Error(),
    85  		}
    86  	}
    87  	return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
    88  }
    89  
    90  // DeliverTx implements ABCI.
    91  func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
    92  	key, value, err := parseTx(req.Tx)
    93  	if err != nil {
    94  		panic(err) // shouldn't happen since we verified it in CheckTx
    95  	}
    96  	app.state.Set(key, value)
    97  	return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
    98  }
    99  
   100  // EndBlock implements ABCI.
   101  func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
   102  	valUpdates, err := app.validatorUpdates(uint64(req.Height))
   103  	if err != nil {
   104  		panic(err)
   105  	}
   106  
   107  	return abci.ResponseEndBlock{
   108  		ValidatorUpdates: valUpdates,
   109  		Events: []abci.Event{
   110  			{
   111  				Type: "val_updates",
   112  				Attributes: []abci.EventAttribute{
   113  					{
   114  						Key:   []byte("size"),
   115  						Value: []byte(strconv.Itoa(valUpdates.Len())),
   116  					},
   117  					{
   118  						Key:   []byte("height"),
   119  						Value: []byte(strconv.Itoa(int(req.Height))),
   120  					},
   121  				},
   122  			},
   123  		},
   124  	}
   125  }
   126  
   127  // Commit implements ABCI.
   128  func (app *Application) Commit() abci.ResponseCommit {
   129  	height, hash, err := app.state.Commit()
   130  	if err != nil {
   131  		panic(err)
   132  	}
   133  	if app.cfg.SnapshotInterval > 0 && height%app.cfg.SnapshotInterval == 0 {
   134  		snapshot, err := app.snapshots.Create(app.state)
   135  		if err != nil {
   136  			panic(err)
   137  		}
   138  		app.logger.Info("Created state sync snapshot", "height", snapshot.Height)
   139  	}
   140  	retainHeight := int64(0)
   141  	if app.cfg.RetainBlocks > 0 {
   142  		retainHeight = int64(height - app.cfg.RetainBlocks + 1)
   143  	}
   144  	return abci.ResponseCommit{
   145  		Data:         hash,
   146  		RetainHeight: retainHeight,
   147  	}
   148  }
   149  
   150  // Query implements ABCI.
   151  func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
   152  	return abci.ResponseQuery{
   153  		Height: int64(app.state.Height),
   154  		Key:    req.Data,
   155  		Value:  []byte(app.state.Get(string(req.Data))),
   156  	}
   157  }
   158  
   159  // ListSnapshots implements ABCI.
   160  func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots {
   161  	snapshots, err := app.snapshots.List()
   162  	if err != nil {
   163  		panic(err)
   164  	}
   165  	return abci.ResponseListSnapshots{Snapshots: snapshots}
   166  }
   167  
   168  // LoadSnapshotChunk implements ABCI.
   169  func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk {
   170  	chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk)
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  	return abci.ResponseLoadSnapshotChunk{Chunk: chunk}
   175  }
   176  
   177  // OfferSnapshot implements ABCI.
   178  func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
   179  	if app.restoreSnapshot != nil {
   180  		panic("A snapshot is already being restored")
   181  	}
   182  	app.restoreSnapshot = req.Snapshot
   183  	app.restoreChunks = [][]byte{}
   184  	return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
   185  }
   186  
   187  // ApplySnapshotChunk implements ABCI.
   188  func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk {
   189  	if app.restoreSnapshot == nil {
   190  		panic("No restore in progress")
   191  	}
   192  	app.restoreChunks = append(app.restoreChunks, req.Chunk)
   193  	if len(app.restoreChunks) == int(app.restoreSnapshot.Chunks) {
   194  		bz := []byte{}
   195  		for _, chunk := range app.restoreChunks {
   196  			bz = append(bz, chunk...)
   197  		}
   198  		err := app.state.Import(app.restoreSnapshot.Height, bz)
   199  		if err != nil {
   200  			panic(err)
   201  		}
   202  		app.restoreSnapshot = nil
   203  		app.restoreChunks = nil
   204  	}
   205  	return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}
   206  }
   207  
   208  // validatorUpdates generates a validator set update.
   209  func (app *Application) validatorUpdates(height uint64) (abci.ValidatorUpdates, error) {
   210  	updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)]
   211  	if len(updates) == 0 {
   212  		return nil, nil
   213  	}
   214  
   215  	valUpdates := abci.ValidatorUpdates{}
   216  	for keyString, power := range updates {
   217  
   218  		keyBytes, err := base64.StdEncoding.DecodeString(keyString)
   219  		if err != nil {
   220  			return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err)
   221  		}
   222  		valUpdates = append(valUpdates, abci.UpdateValidator(keyBytes, int64(power), app.cfg.KeyType))
   223  	}
   224  	return valUpdates, nil
   225  }
   226  
   227  // parseTx parses a tx in 'key=value' format into a key and value.
   228  func parseTx(tx []byte) (string, string, error) {
   229  	parts := bytes.Split(tx, []byte("="))
   230  	if len(parts) != 2 {
   231  		return "", "", fmt.Errorf("invalid tx format: %q", string(tx))
   232  	}
   233  	if len(parts[0]) == 0 {
   234  		return "", "", errors.New("key cannot be empty")
   235  	}
   236  	return string(parts[0]), string(parts[1]), nil
   237  }