github.com/vipernet-xyz/tm@v0.34.24/state/execution.go (about)

     1  package state
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	abci "github.com/vipernet-xyz/tm/abci/types"
     9  	cryptoenc "github.com/vipernet-xyz/tm/crypto/encoding"
    10  	"github.com/vipernet-xyz/tm/libs/fail"
    11  	"github.com/vipernet-xyz/tm/libs/log"
    12  	mempl "github.com/vipernet-xyz/tm/mempool"
    13  	tmstate "github.com/vipernet-xyz/tm/proto/tendermint/state"
    14  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    15  	"github.com/vipernet-xyz/tm/proxy"
    16  	"github.com/vipernet-xyz/tm/types"
    17  )
    18  
    19  //-----------------------------------------------------------------------------
    20  // BlockExecutor handles block execution and state updates.
    21  // It exposes ApplyBlock(), which validates & executes the block, updates state w/ ABCI responses,
    22  // then commits and updates the mempool atomically, then saves state.
    23  
    24  // BlockExecutor provides the context and accessories for properly executing a block.
    25  type BlockExecutor struct {
    26  	// save state, validators, consensus params, abci responses here
    27  	store Store
    28  
    29  	// execute the app against this
    30  	proxyApp proxy.AppConnConsensus
    31  
    32  	// events
    33  	eventBus types.BlockEventPublisher
    34  
    35  	// manage the mempool lock during commit
    36  	// and update both with block results after commit.
    37  	mempool mempl.Mempool
    38  	evpool  EvidencePool
    39  
    40  	logger log.Logger
    41  
    42  	metrics *Metrics
    43  }
    44  
    45  type BlockExecutorOption func(executor *BlockExecutor)
    46  
    47  func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption {
    48  	return func(blockExec *BlockExecutor) {
    49  		blockExec.metrics = metrics
    50  	}
    51  }
    52  
    53  // NewBlockExecutor returns a new BlockExecutor with a NopEventBus.
    54  // Call SetEventBus to provide one.
    55  func NewBlockExecutor(
    56  	stateStore Store,
    57  	logger log.Logger,
    58  	proxyApp proxy.AppConnConsensus,
    59  	mempool mempl.Mempool,
    60  	evpool EvidencePool,
    61  	options ...BlockExecutorOption,
    62  ) *BlockExecutor {
    63  	res := &BlockExecutor{
    64  		store:    stateStore,
    65  		proxyApp: proxyApp,
    66  		eventBus: types.NopEventBus{},
    67  		mempool:  mempool,
    68  		evpool:   evpool,
    69  		logger:   logger,
    70  		metrics:  NopMetrics(),
    71  	}
    72  
    73  	for _, option := range options {
    74  		option(res)
    75  	}
    76  
    77  	return res
    78  }
    79  
    80  func (blockExec *BlockExecutor) Store() Store {
    81  	return blockExec.store
    82  }
    83  
    84  // SetEventBus - sets the event bus for publishing block related events.
    85  // If not called, it defaults to types.NopEventBus.
    86  func (blockExec *BlockExecutor) SetEventBus(eventBus types.BlockEventPublisher) {
    87  	blockExec.eventBus = eventBus
    88  }
    89  
    90  // CreateProposalBlock calls state.MakeBlock with evidence from the evpool
    91  // and txs from the mempool. The max bytes must be big enough to fit the commit.
    92  // Up to 1/10th of the block space is allcoated for maximum sized evidence.
    93  // The rest is given to txs, up to the max gas.
    94  func (blockExec *BlockExecutor) CreateProposalBlock(
    95  	height int64,
    96  	state State, commit *types.Commit,
    97  	proposerAddr []byte,
    98  ) (*types.Block, *types.PartSet) {
    99  
   100  	maxBytes := state.ConsensusParams.Block.MaxBytes
   101  	maxGas := state.ConsensusParams.Block.MaxGas
   102  
   103  	evidence, evSize := blockExec.evpool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
   104  
   105  	// Fetch a limited amount of valid txs
   106  	maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size())
   107  
   108  	txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas)
   109  
   110  	return state.MakeBlock(height, txs, commit, evidence, proposerAddr)
   111  }
   112  
   113  // ValidateBlock validates the given block against the given state.
   114  // If the block is invalid, it returns an error.
   115  // Validation does not mutate state, but does require historical information from the stateDB,
   116  // ie. to verify evidence from a validator at an old height.
   117  func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error {
   118  	err := validateBlock(state, block)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	return blockExec.evpool.CheckEvidence(block.Evidence.Evidence)
   123  }
   124  
   125  // ApplyBlock validates the block against the state, executes it against the app,
   126  // fires the relevant events, commits the app, and saves the new state and responses.
   127  // It returns the new state and the block height to retain (pruning older blocks).
   128  // It's the only function that needs to be called
   129  // from outside this package to process and commit an entire block.
   130  // It takes a blockID to avoid recomputing the parts hash.
   131  func (blockExec *BlockExecutor) ApplyBlock(
   132  	state State, blockID types.BlockID, block *types.Block,
   133  ) (State, int64, error) {
   134  
   135  	if err := validateBlock(state, block); err != nil {
   136  		return state, 0, ErrInvalidBlock(err)
   137  	}
   138  
   139  	startTime := time.Now().UnixNano()
   140  	abciResponses, err := execBlockOnProxyApp(
   141  		blockExec.logger, blockExec.proxyApp, block, blockExec.store, state.InitialHeight,
   142  	)
   143  	endTime := time.Now().UnixNano()
   144  	blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
   145  	if err != nil {
   146  		return state, 0, ErrProxyAppConn(err)
   147  	}
   148  
   149  	fail.Fail() // XXX
   150  
   151  	// Save the results before we commit.
   152  	if err := blockExec.store.SaveABCIResponses(block.Height, abciResponses); err != nil {
   153  		return state, 0, err
   154  	}
   155  
   156  	fail.Fail() // XXX
   157  
   158  	// validate the validator updates and convert to tendermint types
   159  	abciValUpdates := abciResponses.EndBlock.ValidatorUpdates
   160  	err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator)
   161  	if err != nil {
   162  		return state, 0, fmt.Errorf("error in validator updates: %v", err)
   163  	}
   164  
   165  	validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates)
   166  	if err != nil {
   167  		return state, 0, err
   168  	}
   169  	if len(validatorUpdates) > 0 {
   170  		blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates))
   171  	}
   172  
   173  	// Update the state with the block and responses.
   174  	state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
   175  	if err != nil {
   176  		return state, 0, fmt.Errorf("commit failed for application: %v", err)
   177  	}
   178  
   179  	// Lock mempool, commit app state, update mempoool.
   180  	appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTxs)
   181  	if err != nil {
   182  		return state, 0, fmt.Errorf("commit failed for application: %v", err)
   183  	}
   184  
   185  	// Update evpool with the latest state.
   186  	blockExec.evpool.Update(state, block.Evidence.Evidence)
   187  
   188  	fail.Fail() // XXX
   189  
   190  	// Update the app hash and save the state.
   191  	state.AppHash = appHash
   192  	if err := blockExec.store.Save(state); err != nil {
   193  		return state, 0, err
   194  	}
   195  
   196  	fail.Fail() // XXX
   197  
   198  	// Events are fired after everything else.
   199  	// NOTE: if we crash between Commit and Save, events wont be fired during replay
   200  	fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates)
   201  
   202  	return state, retainHeight, nil
   203  }
   204  
   205  // Commit locks the mempool, runs the ABCI Commit message, and updates the
   206  // mempool.
   207  // It returns the result of calling abci.Commit (the AppHash) and the height to retain (if any).
   208  // The Mempool must be locked during commit and update because state is
   209  // typically reset on Commit and old txs must be replayed against committed
   210  // state before new txs are run in the mempool, lest they be invalid.
   211  func (blockExec *BlockExecutor) Commit(
   212  	state State,
   213  	block *types.Block,
   214  	deliverTxResponses []*abci.ResponseDeliverTx,
   215  ) ([]byte, int64, error) {
   216  	blockExec.mempool.Lock()
   217  	defer blockExec.mempool.Unlock()
   218  
   219  	// while mempool is Locked, flush to ensure all async requests have completed
   220  	// in the ABCI app before Commit.
   221  	err := blockExec.mempool.FlushAppConn()
   222  	if err != nil {
   223  		blockExec.logger.Error("client error during mempool.FlushAppConn", "err", err)
   224  		return nil, 0, err
   225  	}
   226  
   227  	// Commit block, get hash back
   228  	res, err := blockExec.proxyApp.CommitSync()
   229  	if err != nil {
   230  		blockExec.logger.Error("client error during proxyAppConn.CommitSync", "err", err)
   231  		return nil, 0, err
   232  	}
   233  
   234  	// ResponseCommit has no error code - just data
   235  	blockExec.logger.Info(
   236  		"committed state",
   237  		"height", block.Height,
   238  		"num_txs", len(block.Txs),
   239  		"app_hash", fmt.Sprintf("%X", res.Data),
   240  	)
   241  
   242  	// Update mempool.
   243  	err = blockExec.mempool.Update(
   244  		block.Height,
   245  		block.Txs,
   246  		deliverTxResponses,
   247  		TxPreCheck(state),
   248  		TxPostCheck(state),
   249  	)
   250  
   251  	return res.Data, res.RetainHeight, err
   252  }
   253  
   254  //---------------------------------------------------------
   255  // Helper functions for executing blocks and updating state
   256  
   257  // Executes block's transactions on proxyAppConn.
   258  // Returns a list of transaction results and updates to the validator set
   259  func execBlockOnProxyApp(
   260  	logger log.Logger,
   261  	proxyAppConn proxy.AppConnConsensus,
   262  	block *types.Block,
   263  	store Store,
   264  	initialHeight int64,
   265  ) (*tmstate.ABCIResponses, error) {
   266  	var validTxs, invalidTxs = 0, 0
   267  
   268  	txIndex := 0
   269  	abciResponses := new(tmstate.ABCIResponses)
   270  	dtxs := make([]*abci.ResponseDeliverTx, len(block.Txs))
   271  	abciResponses.DeliverTxs = dtxs
   272  
   273  	// Execute transactions and get hash.
   274  	proxyCb := func(req *abci.Request, res *abci.Response) {
   275  		if r, ok := res.Value.(*abci.Response_DeliverTx); ok {
   276  			// TODO: make use of res.Log
   277  			// TODO: make use of this info
   278  			// Blocks may include invalid txs.
   279  			txRes := r.DeliverTx
   280  			if txRes.Code == abci.CodeTypeOK {
   281  				validTxs++
   282  			} else {
   283  				logger.Debug("invalid tx", "code", txRes.Code, "log", txRes.Log)
   284  				invalidTxs++
   285  			}
   286  
   287  			abciResponses.DeliverTxs[txIndex] = txRes
   288  			txIndex++
   289  		}
   290  	}
   291  	proxyAppConn.SetResponseCallback(proxyCb)
   292  
   293  	commitInfo := getBeginBlockValidatorInfo(block, store, initialHeight)
   294  
   295  	byzVals := make([]abci.Evidence, 0)
   296  	for _, evidence := range block.Evidence.Evidence {
   297  		byzVals = append(byzVals, evidence.ABCI()...)
   298  	}
   299  
   300  	// Begin block
   301  	var err error
   302  	pbh := block.Header.ToProto()
   303  	if pbh == nil {
   304  		return nil, errors.New("nil header")
   305  	}
   306  
   307  	abciResponses.BeginBlock, err = proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
   308  		Hash:                block.Hash(),
   309  		Header:              *pbh,
   310  		LastCommitInfo:      commitInfo,
   311  		ByzantineValidators: byzVals,
   312  	})
   313  	if err != nil {
   314  		logger.Error("error in proxyAppConn.BeginBlock", "err", err)
   315  		return nil, err
   316  	}
   317  
   318  	// run txs of block
   319  	for _, tx := range block.Txs {
   320  		proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx})
   321  		if err := proxyAppConn.Error(); err != nil {
   322  			return nil, err
   323  		}
   324  	}
   325  
   326  	// End block.
   327  	abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{Height: block.Height})
   328  	if err != nil {
   329  		logger.Error("error in proxyAppConn.EndBlock", "err", err)
   330  		return nil, err
   331  	}
   332  
   333  	logger.Info("executed block", "height", block.Height, "num_valid_txs", validTxs, "num_invalid_txs", invalidTxs)
   334  	return abciResponses, nil
   335  }
   336  
   337  func getBeginBlockValidatorInfo(block *types.Block, store Store,
   338  	initialHeight int64) abci.LastCommitInfo {
   339  	voteInfos := make([]abci.VoteInfo, block.LastCommit.Size())
   340  	// Initial block -> LastCommitInfo.Votes are empty.
   341  	// Remember that the first LastCommit is intentionally empty, so it makes
   342  	// sense for LastCommitInfo.Votes to also be empty.
   343  	if block.Height > initialHeight {
   344  		lastValSet, err := store.LoadValidators(block.Height - 1)
   345  		if err != nil {
   346  			panic(err)
   347  		}
   348  
   349  		// Sanity check that commit size matches validator set size - only applies
   350  		// after first block.
   351  		var (
   352  			commitSize = block.LastCommit.Size()
   353  			valSetLen  = len(lastValSet.Validators)
   354  		)
   355  		if commitSize != valSetLen {
   356  			panic(fmt.Sprintf(
   357  				"commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v",
   358  				commitSize, valSetLen, block.Height, block.LastCommit.Signatures, lastValSet.Validators,
   359  			))
   360  		}
   361  
   362  		for i, val := range lastValSet.Validators {
   363  			commitSig := block.LastCommit.Signatures[i]
   364  			voteInfos[i] = abci.VoteInfo{
   365  				Validator:       types.TM2PB.Validator(val),
   366  				SignedLastBlock: !commitSig.Absent(),
   367  			}
   368  		}
   369  	}
   370  
   371  	return abci.LastCommitInfo{
   372  		Round: block.LastCommit.Round,
   373  		Votes: voteInfos,
   374  	}
   375  }
   376  
   377  func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate,
   378  	params tmproto.ValidatorParams) error {
   379  	for _, valUpdate := range abciUpdates {
   380  		if valUpdate.GetPower() < 0 {
   381  			return fmt.Errorf("voting power can't be negative %v", valUpdate)
   382  		} else if valUpdate.GetPower() == 0 {
   383  			// continue, since this is deleting the validator, and thus there is no
   384  			// pubkey to check
   385  			continue
   386  		}
   387  
   388  		// Check if validator's pubkey matches an ABCI type in the consensus params
   389  		pk, err := cryptoenc.PubKeyFromProto(valUpdate.PubKey)
   390  		if err != nil {
   391  			return err
   392  		}
   393  
   394  		if !types.IsValidPubkeyType(params, pk.Type()) {
   395  			return fmt.Errorf("validator %v is using pubkey %s, which is unsupported for consensus",
   396  				valUpdate, pk.Type())
   397  		}
   398  	}
   399  	return nil
   400  }
   401  
   402  // updateState returns a new State updated according to the header and responses.
   403  func updateState(
   404  	state State,
   405  	blockID types.BlockID,
   406  	header *types.Header,
   407  	abciResponses *tmstate.ABCIResponses,
   408  	validatorUpdates []*types.Validator,
   409  ) (State, error) {
   410  
   411  	// Copy the valset so we can apply changes from EndBlock
   412  	// and update s.LastValidators and s.Validators.
   413  	nValSet := state.NextValidators.Copy()
   414  
   415  	// Update the validator set with the latest abciResponses.
   416  	lastHeightValsChanged := state.LastHeightValidatorsChanged
   417  	if len(validatorUpdates) > 0 {
   418  		err := nValSet.UpdateWithChangeSet(validatorUpdates)
   419  		if err != nil {
   420  			return state, fmt.Errorf("error changing validator set: %v", err)
   421  		}
   422  		// Change results from this height but only applies to the next next height.
   423  		lastHeightValsChanged = header.Height + 1 + 1
   424  	}
   425  
   426  	// Update validator proposer priority and set state variables.
   427  	nValSet.IncrementProposerPriority(1)
   428  
   429  	// Update the params with the latest abciResponses.
   430  	nextParams := state.ConsensusParams
   431  	lastHeightParamsChanged := state.LastHeightConsensusParamsChanged
   432  	if abciResponses.EndBlock.ConsensusParamUpdates != nil {
   433  		// NOTE: must not mutate s.ConsensusParams
   434  		nextParams = types.UpdateConsensusParams(state.ConsensusParams, abciResponses.EndBlock.ConsensusParamUpdates)
   435  		err := types.ValidateConsensusParams(nextParams)
   436  		if err != nil {
   437  			return state, fmt.Errorf("error updating consensus params: %v", err)
   438  		}
   439  
   440  		state.Version.Consensus.App = nextParams.Version.AppVersion
   441  
   442  		// Change results from this height but only applies to the next height.
   443  		lastHeightParamsChanged = header.Height + 1
   444  	}
   445  
   446  	nextVersion := state.Version
   447  
   448  	// NOTE: the AppHash has not been populated.
   449  	// It will be filled on state.Save.
   450  	return State{
   451  		Version:                          nextVersion,
   452  		ChainID:                          state.ChainID,
   453  		InitialHeight:                    state.InitialHeight,
   454  		LastBlockHeight:                  header.Height,
   455  		LastBlockID:                      blockID,
   456  		LastBlockTime:                    header.Time,
   457  		NextValidators:                   nValSet,
   458  		Validators:                       state.NextValidators.Copy(),
   459  		LastValidators:                   state.Validators.Copy(),
   460  		LastHeightValidatorsChanged:      lastHeightValsChanged,
   461  		ConsensusParams:                  nextParams,
   462  		LastHeightConsensusParamsChanged: lastHeightParamsChanged,
   463  		LastResultsHash:                  ABCIResponsesResultsHash(abciResponses),
   464  		AppHash:                          nil,
   465  	}, nil
   466  }
   467  
   468  // Fire NewBlock, NewBlockHeader.
   469  // Fire TxEvent for every tx.
   470  // NOTE: if Tendermint crashes before commit, some or all of these events may be published again.
   471  func fireEvents(
   472  	logger log.Logger,
   473  	eventBus types.BlockEventPublisher,
   474  	block *types.Block,
   475  	abciResponses *tmstate.ABCIResponses,
   476  	validatorUpdates []*types.Validator,
   477  ) {
   478  	if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{
   479  		Block:            block,
   480  		ResultBeginBlock: *abciResponses.BeginBlock,
   481  		ResultEndBlock:   *abciResponses.EndBlock,
   482  	}); err != nil {
   483  		logger.Error("failed publishing new block", "err", err)
   484  	}
   485  
   486  	if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
   487  		Header:           block.Header,
   488  		NumTxs:           int64(len(block.Txs)),
   489  		ResultBeginBlock: *abciResponses.BeginBlock,
   490  		ResultEndBlock:   *abciResponses.EndBlock,
   491  	}); err != nil {
   492  		logger.Error("failed publishing new block header", "err", err)
   493  	}
   494  
   495  	if len(block.Evidence.Evidence) != 0 {
   496  		for _, ev := range block.Evidence.Evidence {
   497  			if err := eventBus.PublishEventNewEvidence(types.EventDataNewEvidence{
   498  				Evidence: ev,
   499  				Height:   block.Height,
   500  			}); err != nil {
   501  				logger.Error("failed publishing new evidence", "err", err)
   502  			}
   503  		}
   504  	}
   505  
   506  	for i, tx := range block.Data.Txs {
   507  		if err := eventBus.PublishEventTx(types.EventDataTx{TxResult: abci.TxResult{
   508  			Height: block.Height,
   509  			Index:  uint32(i),
   510  			Tx:     tx,
   511  			Result: *(abciResponses.DeliverTxs[i]),
   512  		}}); err != nil {
   513  			logger.Error("failed publishing event TX", "err", err)
   514  		}
   515  	}
   516  
   517  	if len(validatorUpdates) > 0 {
   518  		if err := eventBus.PublishEventValidatorSetUpdates(
   519  			types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates}); err != nil {
   520  			logger.Error("failed publishing event", "err", err)
   521  		}
   522  	}
   523  }
   524  
   525  //----------------------------------------------------------------------------------------------------
   526  // Execute block without state. TODO: eliminate
   527  
   528  // ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state.
   529  // It returns the application root hash (result of abci.Commit).
   530  func ExecCommitBlock(
   531  	appConnConsensus proxy.AppConnConsensus,
   532  	block *types.Block,
   533  	logger log.Logger,
   534  	store Store,
   535  	initialHeight int64,
   536  ) ([]byte, error) {
   537  	_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
   538  	if err != nil {
   539  		logger.Error("failed executing block on proxy app", "height", block.Height, "err", err)
   540  		return nil, err
   541  	}
   542  
   543  	// Commit block, get hash back
   544  	res, err := appConnConsensus.CommitSync()
   545  	if err != nil {
   546  		logger.Error("client error during proxyAppConn.CommitSync", "err", res)
   547  		return nil, err
   548  	}
   549  
   550  	// ResponseCommit has no error or log, just data
   551  	return res.Data, nil
   552  }