bitbucket.org/number571/tendermint@v0.8.14/test/e2e/app/app.go (about)

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