feat!: Comet v0.38 Integration (#15519)
Co-authored-by: marbar3778 <marbar3778@yahoo.com> Co-authored-by: cool-developer <51834436+cool-develope@users.noreply.github.com> Co-authored-by: Aaron Craelius <aaron@regen.network> Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com> Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
co-authored by
marbar3778
cool-developer
Aaron Craelius
Matt Kocubinski
Julien Robert
parent
166be3766b
commit
6cee22df52
+625
-521
File diff suppressed because it is too large
Load Diff
+237
-552
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,281 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/cockroachdb/errors"
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
cmtcrypto "github.com/cometbft/cometbft/crypto"
|
||||
cryptoenc "github.com/cometbft/cometbft/crypto/encoding"
|
||||
cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
|
||||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
protoio "github.com/cosmos/gogoproto/io"
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
// VoteExtensionThreshold defines the total voting power % that must be
|
||||
// submitted in order for all vote extensions to be considered valid for a
|
||||
// given height.
|
||||
var VoteExtensionThreshold = math.LegacyNewDecWithPrec(667, 3)
|
||||
|
||||
type (
|
||||
// Validator defines the interface contract require for verifying vote extension
|
||||
// signatures. Typically, this will be implemented by the x/staking module,
|
||||
// which has knowledge of the CometBFT public key.
|
||||
Validator interface {
|
||||
CmtConsPublicKey() (cmtprotocrypto.PublicKey, error)
|
||||
BondedTokens() math.Int
|
||||
}
|
||||
|
||||
// ValidatorStore defines the interface contract require for verifying vote
|
||||
// extension signatures. Typically, this will be implemented by the x/staking
|
||||
// module, which has knowledge of the CometBFT public key.
|
||||
ValidatorStore interface {
|
||||
GetValidatorByConsAddr(sdk.Context, cryptotypes.Address) (Validator, error)
|
||||
TotalBondedTokens(ctx sdk.Context) math.Int
|
||||
}
|
||||
)
|
||||
|
||||
// ValidateVoteExtensions defines a helper function for verifying vote extension
|
||||
// signatures that may be passed or manually injected into a block proposal from
|
||||
// a proposer in ProcessProposal. It returns an error if any signature is invalid
|
||||
// or if unexpected vote extensions and/or signatures are found or less than 2/3
|
||||
// power is received.
|
||||
func ValidateVoteExtensions(
|
||||
ctx sdk.Context,
|
||||
valStore ValidatorStore,
|
||||
currentHeight int64,
|
||||
chainID string,
|
||||
extCommit abci.ExtendedCommitInfo,
|
||||
) error {
|
||||
cp := ctx.ConsensusParams()
|
||||
extsEnabled := cp.Abci != nil && cp.Abci.VoteExtensionsEnableHeight > 0
|
||||
|
||||
marshalDelimitedFn := func(msg proto.Message) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := protoio.NewDelimitedWriter(&buf).WriteMsg(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var sumVP math.Int
|
||||
for _, vote := range extCommit.Votes {
|
||||
if !extsEnabled {
|
||||
if len(vote.VoteExtension) > 0 {
|
||||
return fmt.Errorf("vote extensions disabled; received non-empty vote extension at height %d", currentHeight)
|
||||
}
|
||||
if len(vote.ExtensionSignature) > 0 {
|
||||
return fmt.Errorf("vote extensions disabled; received non-empty vote extension signature at height %d", currentHeight)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if len(vote.ExtensionSignature) == 0 {
|
||||
return fmt.Errorf("vote extensions enabled; received empty vote extension signature at height %d", currentHeight)
|
||||
}
|
||||
|
||||
valConsAddr := cmtcrypto.Address(vote.Validator.Address)
|
||||
|
||||
validator, err := valStore.GetValidatorByConsAddr(ctx, valConsAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validator %X: %w", valConsAddr, err)
|
||||
}
|
||||
if validator == nil {
|
||||
return fmt.Errorf("validator %X not found", valConsAddr)
|
||||
}
|
||||
|
||||
cmtPubKeyProto, err := validator.CmtConsPublicKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validator %X public key: %w", valConsAddr, err)
|
||||
}
|
||||
|
||||
cmtPubKey, err := cryptoenc.PubKeyFromProto(cmtPubKeyProto)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert validator %X public key: %w", valConsAddr, err)
|
||||
}
|
||||
|
||||
cve := cmtproto.CanonicalVoteExtension{
|
||||
Extension: vote.VoteExtension,
|
||||
Height: currentHeight - 1, // the vote extension was signed in the previous height
|
||||
Round: int64(extCommit.Round),
|
||||
ChainId: chainID,
|
||||
}
|
||||
|
||||
extSignBytes, err := marshalDelimitedFn(&cve)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode CanonicalVoteExtension: %w", err)
|
||||
}
|
||||
|
||||
if !cmtPubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) {
|
||||
return fmt.Errorf("failed to verify validator %X vote extension signature", valConsAddr)
|
||||
}
|
||||
|
||||
sumVP = sumVP.Add(validator.BondedTokens())
|
||||
}
|
||||
|
||||
// Ensure we have at least 2/3 voting power that submitted valid vote
|
||||
// extensions.
|
||||
totalVP := valStore.TotalBondedTokens(ctx)
|
||||
percentSubmitted := math.LegacyNewDecFromInt(sumVP).Quo(math.LegacyNewDecFromInt(totalVP))
|
||||
if percentSubmitted.LT(VoteExtensionThreshold) {
|
||||
return fmt.Errorf("insufficient cumulative voting power received to verify vote extensions; got: %s, expected: >=%s", percentSubmitted, VoteExtensionThreshold)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
// ProposalTxVerifier defines the interface that is implemented by BaseApp,
|
||||
// that any custom ABCI PrepareProposal and ProcessProposal handler can use
|
||||
// to verify a transaction.
|
||||
ProposalTxVerifier interface {
|
||||
PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error)
|
||||
ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error)
|
||||
}
|
||||
|
||||
// DefaultProposalHandler defines the default ABCI PrepareProposal and
|
||||
// ProcessProposal handlers.
|
||||
DefaultProposalHandler struct {
|
||||
mempool mempool.Mempool
|
||||
txVerifier ProposalTxVerifier
|
||||
}
|
||||
)
|
||||
|
||||
func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier) DefaultProposalHandler {
|
||||
return DefaultProposalHandler{
|
||||
mempool: mp,
|
||||
txVerifier: txVerifier,
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareProposalHandler returns the default implementation for processing an
|
||||
// ABCI proposal. The application's mempool is enumerated and all valid
|
||||
// transactions are added to the proposal. Transactions are valid if they:
|
||||
//
|
||||
// 1) Successfully encode to bytes.
|
||||
// 2) Are valid (i.e. pass runTx, AnteHandler only).
|
||||
//
|
||||
// Enumeration is halted once RequestPrepareProposal.MaxBytes of transactions is
|
||||
// reached or the mempool is exhausted.
|
||||
//
|
||||
// Note:
|
||||
//
|
||||
// - Step (2) is identical to the validation step performed in
|
||||
// DefaultProcessProposal. It is very important that the same validation logic
|
||||
// is used in both steps, and applications must ensure that this is the case in
|
||||
// non-default handlers.
|
||||
//
|
||||
// - If no mempool is set or if the mempool is a no-op mempool, the transactions
|
||||
// requested from CometBFT will simply be returned, which, by default, are in
|
||||
// FIFO order.
|
||||
func (h DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
|
||||
return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
|
||||
// If the mempool is nil or a no-op mempool, we simply return the transactions
|
||||
// requested from CometBFT, which, by default, should be in FIFO order.
|
||||
_, isNoOp := h.mempool.(mempool.NoOpMempool)
|
||||
if h.mempool == nil || isNoOp {
|
||||
return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
selectedTxs [][]byte
|
||||
totalTxBytes int64
|
||||
)
|
||||
|
||||
iterator := h.mempool.Select(ctx, req.Txs)
|
||||
|
||||
for iterator != nil {
|
||||
memTx := iterator.Tx()
|
||||
|
||||
// NOTE: Since transaction verification was already executed in CheckTx,
|
||||
// which calls mempool.Insert, in theory everything in the pool should be
|
||||
// valid. But some mempool implementations may insert invalid txs, so we
|
||||
// check again.
|
||||
bz, err := h.txVerifier.PrepareProposalVerifyTx(memTx)
|
||||
if err != nil {
|
||||
err := h.mempool.Remove(memTx)
|
||||
if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
txSize := int64(len(bz))
|
||||
if totalTxBytes += txSize; totalTxBytes <= req.MaxTxBytes {
|
||||
selectedTxs = append(selectedTxs, bz)
|
||||
} else {
|
||||
// We've reached capacity per req.MaxTxBytes so we cannot select any
|
||||
// more transactions.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
iterator = iterator.Next()
|
||||
}
|
||||
|
||||
return &abci.ResponsePrepareProposal{Txs: selectedTxs}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessProposalHandler returns the default implementation for processing an
|
||||
// ABCI proposal. Every transaction in the proposal must pass 2 conditions:
|
||||
//
|
||||
// 1. The transaction bytes must decode to a valid transaction.
|
||||
// 2. The transaction must be valid (i.e. pass runTx, AnteHandler only)
|
||||
//
|
||||
// If any transaction fails to pass either condition, the proposal is rejected.
|
||||
// Note that step (2) is identical to the validation step performed in
|
||||
// DefaultPrepareProposal. It is very important that the same validation logic
|
||||
// is used in both steps, and applications must ensure that this is the case in
|
||||
// non-default handlers.
|
||||
func (h DefaultProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
|
||||
return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
|
||||
for _, txBytes := range req.Txs {
|
||||
_, err := h.txVerifier.ProcessProposalVerifyTx(txBytes)
|
||||
if err != nil {
|
||||
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpPrepareProposal defines a no-op PrepareProposal handler. It will always
|
||||
// return the transactions sent by the client's request.
|
||||
func NoOpPrepareProposal() sdk.PrepareProposalHandler {
|
||||
return func(_ sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
|
||||
return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpProcessProposal defines a no-op ProcessProposal Handler. It will always
|
||||
// return ACCEPT.
|
||||
func NoOpProcessProposal() sdk.ProcessProposalHandler {
|
||||
return func(_ sdk.Context, _ *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
|
||||
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpExtendVote defines a no-op ExtendVote handler. It will always return an
|
||||
// empty byte slice as the vote extension.
|
||||
func NoOpExtendVote() sdk.ExtendVoteHandler {
|
||||
return func(_ sdk.Context, _ *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
|
||||
return &abci.ResponseExtendVote{VoteExtension: []byte{}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpVerifyVoteExtensionHandler defines a no-op VerifyVoteExtension handler. It
|
||||
// will always return an ACCEPT status with no error.
|
||||
func NoOpVerifyVoteExtensionHandler() sdk.VerifyVoteExtensionHandler {
|
||||
return func(_ sdk.Context, _ *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
|
||||
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
|
||||
}
|
||||
}
|
||||
+234
-221
@@ -1,6 +1,7 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -20,32 +21,35 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
type (
|
||||
// Enum mode for app.runTx
|
||||
runTxMode uint8
|
||||
execMode uint8
|
||||
|
||||
// StoreLoader defines a customizable function to control how we load the CommitMultiStore
|
||||
// from disk. This is useful for state migration, when loading a datastore written with
|
||||
// an older version of the software. In particular, if a module changed the substore key name
|
||||
// (or removed a substore) between two versions of the software.
|
||||
// StoreLoader defines a customizable function to control how we load the
|
||||
// CommitMultiStore from disk. This is useful for state migration, when
|
||||
// loading a datastore written with an older version of the software. In
|
||||
// particular, if a module changed the substore key name (or removed a substore)
|
||||
// between two versions of the software.
|
||||
StoreLoader func(ms storetypes.CommitMultiStore) error
|
||||
)
|
||||
|
||||
const (
|
||||
runTxModeCheck runTxMode = iota // Check a transaction
|
||||
runTxModeReCheck // Recheck a (pending) transaction after a commit
|
||||
runTxModeSimulate // Simulate a transaction
|
||||
runTxModeDeliver // Deliver a transaction
|
||||
runTxPrepareProposal // Prepare a TM block proposal
|
||||
runTxProcessProposal // Process a TM block proposal
|
||||
execModeCheck execMode = iota // Check a transaction
|
||||
execModeReCheck // Recheck a (pending) transaction after a commit
|
||||
execModeSimulate // Simulate a transaction
|
||||
execModePrepareProposal // Prepare a block proposal
|
||||
execModeProcessProposal // Process a block proposal
|
||||
execModeVoteExtension // Extend or verify a pre-commit vote
|
||||
execModeFinalize // Finalize a block proposal
|
||||
)
|
||||
|
||||
var _ abci.Application = (*BaseApp)(nil)
|
||||
var _ servertypes.ABCI = (*BaseApp)(nil)
|
||||
|
||||
// BaseApp reflects the ABCI application implementation.
|
||||
type BaseApp struct {
|
||||
@@ -62,33 +66,59 @@ type BaseApp struct {
|
||||
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
||||
txEncoder sdk.TxEncoder // marshal sdk.Tx into []byte
|
||||
|
||||
mempool mempool.Mempool // application side mempool
|
||||
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
||||
postHandler sdk.PostHandler // post handler, optional, e.g. for tips
|
||||
initChainer sdk.InitChainer // initialize state with validators and state blob
|
||||
beginBlocker sdk.BeginBlocker // logic to run before any txs
|
||||
processProposal sdk.ProcessProposalHandler // the handler which runs on ABCI ProcessProposal
|
||||
prepareProposal sdk.PrepareProposalHandler // the handler which runs on ABCI PrepareProposal
|
||||
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||
prepareCheckStater sdk.PrepareCheckStater // logic to run during commit using the checkState
|
||||
precommiter sdk.Precommiter // logic to run during commit using the deliverState
|
||||
addrPeerFilter sdk.PeerFilter // filter peers by address and port
|
||||
idPeerFilter sdk.PeerFilter // filter peers by node ID
|
||||
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.
|
||||
mempool mempool.Mempool // application side mempool
|
||||
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
||||
postHandler sdk.PostHandler // post handler, optional, e.g. for tips
|
||||
|
||||
initChainer sdk.InitChainer // ABCI InitChain handler
|
||||
beginBlocker sdk.BeginBlocker // (legacy ABCI) BeginBlock handler
|
||||
endBlocker sdk.EndBlocker // (legacy ABCI) EndBlock handler
|
||||
processProposal sdk.ProcessProposalHandler // ABCI ProcessProposal handler
|
||||
prepareProposal sdk.PrepareProposalHandler // ABCI PrepareProposal
|
||||
extendVote sdk.ExtendVoteHandler // ABCI ExtendVote handler
|
||||
verifyVoteExt sdk.VerifyVoteExtensionHandler // ABCI VerifyVoteExtension handler
|
||||
prepareCheckStater sdk.PrepareCheckStater // logic to run during commit using the checkState
|
||||
precommiter sdk.Precommiter // logic to run during commit using the deliverState
|
||||
|
||||
addrPeerFilter sdk.PeerFilter // filter peers by address and port
|
||||
idPeerFilter sdk.PeerFilter // filter peers by node ID
|
||||
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.
|
||||
|
||||
// manages snapshots, i.e. dumps of app state at certain intervals
|
||||
snapshotManager *snapshots.Manager
|
||||
|
||||
// volatile states:
|
||||
//
|
||||
// checkState is set on InitChain and reset on Commit
|
||||
// deliverState is set on InitChain and BeginBlock and set to nil on Commit
|
||||
checkState *state // for CheckTx
|
||||
deliverState *state // for DeliverTx
|
||||
processProposalState *state // for ProcessProposal
|
||||
prepareProposalState *state // for PrepareProposal
|
||||
// - checkState is set on InitChain and reset on Commit
|
||||
// - finalizeBlockState is set on InitChain and FinalizeBlock and set to nil
|
||||
// on Commit.
|
||||
//
|
||||
// - checkState: Used for CheckTx, which is set based on the previous block's
|
||||
// state. This state is never committed.
|
||||
//
|
||||
// - prepareProposalState: Used for PrepareProposal, which is set based on the
|
||||
// previous block's state. This state is never committed. In case of multiple
|
||||
// consensus rounds, the state is always reset to the previous block's state.
|
||||
//
|
||||
// - voteExtensionState: Used for ExtendVote and VerifyVoteExtension, which is
|
||||
// set based on the previous block's state. This state is never committed. In
|
||||
// case of multiple rounds, the state is always reset to the previous block's
|
||||
// state.
|
||||
//
|
||||
// - processProposalState: Used for ProcessProposal, which is set based on the
|
||||
// the previous block's state. This state is never committed. In case of
|
||||
// multiple rounds, the state is always reset to the previous block's state.
|
||||
//
|
||||
// - finalizeBlockState: Used for FinalizeBlock, which is set based on the
|
||||
// previous block's state. This state is committed.
|
||||
checkState *state
|
||||
prepareProposalState *state
|
||||
processProposalState *state
|
||||
voteExtensionState *state
|
||||
finalizeBlockState *state
|
||||
|
||||
// an inter-block write-through cache provided to the context during deliverState
|
||||
// An inter-block write-through cache provided to the context during the ABCI
|
||||
// FinalizeBlock call.
|
||||
interBlockCache storetypes.MultiStorePersistentCache
|
||||
|
||||
// paramStore is used to query for ABCI consensus parameters from an
|
||||
@@ -99,7 +129,7 @@ type BaseApp struct {
|
||||
// transaction. This is mainly used for DoS and spam prevention.
|
||||
minGasPrices sdk.DecCoins
|
||||
|
||||
// initialHeight is the initial height at which we start the baseapp
|
||||
// initialHeight is the initial height at which we start the BaseApp
|
||||
initialHeight int64
|
||||
|
||||
// flag for sealing options and parameters to a BaseApp
|
||||
@@ -149,8 +179,6 @@ type BaseApp struct {
|
||||
// NewBaseApp returns a reference to an initialized BaseApp. It accepts a
|
||||
// variadic number of option functions, which act on the BaseApp to set
|
||||
// configuration choices.
|
||||
//
|
||||
// NOTE: The db is used to store the version number for now.
|
||||
func NewBaseApp(
|
||||
name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp),
|
||||
) *BaseApp {
|
||||
@@ -179,11 +207,15 @@ func NewBaseApp(
|
||||
if app.prepareProposal == nil {
|
||||
app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
|
||||
}
|
||||
|
||||
if app.processProposal == nil {
|
||||
app.SetProcessProposal(abciProposalHandler.ProcessProposalHandler())
|
||||
}
|
||||
|
||||
if app.extendVote == nil {
|
||||
app.SetExtendVoteHandler(NoOpExtendVote())
|
||||
}
|
||||
if app.verifyVoteExt == nil {
|
||||
app.SetVerifyVoteExtensionHandler(NoOpVerifyVoteExtensionHandler())
|
||||
}
|
||||
if app.interBlockCache != nil {
|
||||
app.cms.SetInterBlockCache(app.interBlockCache)
|
||||
}
|
||||
@@ -342,6 +374,16 @@ func (app *BaseApp) LastBlockHeight() int64 {
|
||||
return app.cms.LastCommitID().Version
|
||||
}
|
||||
|
||||
// ChainID returns the chainID of the app.
|
||||
func (app *BaseApp) ChainID() string {
|
||||
return app.chainID
|
||||
}
|
||||
|
||||
// AnteHandler returns the AnteHandler of the app.
|
||||
func (app *BaseApp) AnteHandler() sdk.AnteHandler {
|
||||
return app.anteHandler
|
||||
}
|
||||
|
||||
// Init initializes the app. It seals the app, preventing any
|
||||
// further modifications. In addition, it validates the app against
|
||||
// the earlier provided settings. Returns an error if validation fails.
|
||||
@@ -354,7 +396,7 @@ func (app *BaseApp) Init() error {
|
||||
emptyHeader := cmtproto.Header{ChainID: app.chainID}
|
||||
|
||||
// needed for the export command which inits from store but never calls initchain
|
||||
app.setState(runTxModeCheck, emptyHeader)
|
||||
app.setState(execModeCheck, emptyHeader)
|
||||
app.Seal()
|
||||
|
||||
if app.cms == nil {
|
||||
@@ -405,7 +447,7 @@ func (app *BaseApp) IsSealed() bool { return app.sealed }
|
||||
// setState sets the BaseApp's state for the corresponding mode with a branched
|
||||
// multi-store (i.e. a CacheMultiStore) and a new Context with the same
|
||||
// multi-store branch, and provided header.
|
||||
func (app *BaseApp) setState(mode runTxMode, header cmtproto.Header) {
|
||||
func (app *BaseApp) setState(mode execMode, header cmtproto.Header) {
|
||||
ms := app.cms.CacheMultiStore()
|
||||
baseState := &state{
|
||||
ms: ms,
|
||||
@@ -413,24 +455,39 @@ func (app *BaseApp) setState(mode runTxMode, header cmtproto.Header) {
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case runTxModeCheck:
|
||||
// Minimum gas prices are also set. It is set on InitChain and reset on Commit.
|
||||
case execModeCheck:
|
||||
baseState.ctx = baseState.ctx.WithIsCheckTx(true).WithMinGasPrices(app.minGasPrices)
|
||||
app.checkState = baseState
|
||||
case runTxModeDeliver:
|
||||
// It is set on InitChain and BeginBlock and set to nil on Commit.
|
||||
app.deliverState = baseState
|
||||
case runTxPrepareProposal:
|
||||
// It is set on InitChain and Commit.
|
||||
|
||||
case execModePrepareProposal:
|
||||
app.prepareProposalState = baseState
|
||||
case runTxProcessProposal:
|
||||
// It is set on InitChain and Commit.
|
||||
|
||||
case execModeProcessProposal:
|
||||
app.processProposalState = baseState
|
||||
|
||||
case execModeVoteExtension:
|
||||
app.voteExtensionState = baseState
|
||||
|
||||
case execModeFinalize:
|
||||
app.finalizeBlockState = baseState
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid runTxMode for setState: %d", mode))
|
||||
}
|
||||
}
|
||||
|
||||
// GetFinalizeBlockStateCtx returns the Context associated with the FinalizeBlock
|
||||
// state. This Context can be used to write data derived from processing vote
|
||||
// extensions to application state during ProcessProposal.
|
||||
//
|
||||
// NOTE:
|
||||
// - Do NOT use or write to state using this Context unless you intend for
|
||||
// that state to be committed.
|
||||
// - Do NOT use or write to state using this Context on the first block.
|
||||
func (app *BaseApp) GetFinalizeBlockStateCtx() sdk.Context {
|
||||
return app.finalizeBlockState.ctx
|
||||
}
|
||||
|
||||
// SetCircuitBreaker sets the circuit breaker for the BaseApp.
|
||||
// The circuit breaker is checked on every message execution to verify if a transaction should be executed or not.
|
||||
func (app *BaseApp) SetCircuitBreaker(cb CircuitBreaker) {
|
||||
@@ -452,15 +509,17 @@ func (app *BaseApp) GetConsensusParams(ctx sdk.Context) cmtproto.ConsensusParams
|
||||
return cp
|
||||
}
|
||||
|
||||
// StoreConsensusParams sets the consensus parameters to the baseapp's param store.
|
||||
// StoreConsensusParams sets the consensus parameters to the BaseApp's param
|
||||
// store.
|
||||
//
|
||||
// NOTE: We're explicitly not storing the CometBFT app_version in the param store.
|
||||
// It's stored instead in the x/upgrade store, with its own bump logic.
|
||||
func (app *BaseApp) StoreConsensusParams(ctx sdk.Context, cp cmtproto.ConsensusParams) error {
|
||||
if app.paramStore == nil {
|
||||
panic("cannot store consensus params with no params store set")
|
||||
}
|
||||
|
||||
return app.paramStore.Set(ctx, cp)
|
||||
// We're explicitly not storing the CometBFT app_version in the param store. It's
|
||||
// stored instead in the x/upgrade store, with its own bump logic.
|
||||
}
|
||||
|
||||
// AddRunTxRecoveryHandler adds custom app.runTx method panic handlers.
|
||||
@@ -493,9 +552,9 @@ func (app *BaseApp) GetMaximumBlockGas(ctx sdk.Context) uint64 {
|
||||
}
|
||||
}
|
||||
|
||||
func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
|
||||
if req.Header.Height < 1 {
|
||||
return fmt.Errorf("invalid height: %d", req.Header.Height)
|
||||
func (app *BaseApp) validateFinalizeBlockHeight(req *abci.RequestFinalizeBlock) error {
|
||||
if req.Height < 1 {
|
||||
return fmt.Errorf("invalid height: %d", req.Height)
|
||||
}
|
||||
|
||||
lastBlockHeight := app.LastBlockHeight()
|
||||
@@ -515,8 +574,8 @@ func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
|
||||
expectedHeight = lastBlockHeight + 1
|
||||
}
|
||||
|
||||
if req.Header.Height != expectedHeight {
|
||||
return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, expectedHeight)
|
||||
if req.Height != expectedHeight {
|
||||
return fmt.Errorf("invalid height: %d; expected: %d", req.Height, expectedHeight)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -542,18 +601,15 @@ func validateBasicTxMsgs(msgs []sdk.Msg) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the application's deliverState if app is in runTxModeDeliver,
|
||||
// prepareProposalState if app is in runTxPrepareProposal, processProposalState
|
||||
// if app is in runTxProcessProposal, and checkState otherwise.
|
||||
func (app *BaseApp) getState(mode runTxMode) *state {
|
||||
func (app *BaseApp) getState(mode execMode) *state {
|
||||
switch mode {
|
||||
case runTxModeDeliver:
|
||||
return app.deliverState
|
||||
case execModeFinalize:
|
||||
return app.finalizeBlockState
|
||||
|
||||
case runTxPrepareProposal:
|
||||
case execModePrepareProposal:
|
||||
return app.prepareProposalState
|
||||
|
||||
case runTxProcessProposal:
|
||||
case execModeProcessProposal:
|
||||
return app.processProposalState
|
||||
|
||||
default:
|
||||
@@ -570,7 +626,7 @@ func (app *BaseApp) getBlockGasMeter(ctx sdk.Context) storetypes.GasMeter {
|
||||
}
|
||||
|
||||
// retrieve the context for the tx w/ txBytes and other memoized values.
|
||||
func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context {
|
||||
func (app *BaseApp) getContextForTx(mode execMode, txBytes []byte) sdk.Context {
|
||||
modeState := app.getState(mode)
|
||||
if modeState == nil {
|
||||
panic(fmt.Sprintf("state is nil for mode %v", mode))
|
||||
@@ -581,11 +637,11 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context
|
||||
|
||||
ctx = ctx.WithConsensusParams(app.GetConsensusParams(ctx))
|
||||
|
||||
if mode == runTxModeReCheck {
|
||||
if mode == execModeReCheck {
|
||||
ctx = ctx.WithIsReCheckTx(true)
|
||||
}
|
||||
|
||||
if mode == runTxModeSimulate {
|
||||
if mode == execModeSimulate {
|
||||
ctx, _ = ctx.CacheContext()
|
||||
}
|
||||
|
||||
@@ -611,6 +667,95 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context
|
||||
return ctx.WithMultiStore(msCache), msCache
|
||||
}
|
||||
|
||||
func (app *BaseApp) beginBlock(req *abci.RequestFinalizeBlock) sdk.BeginBlock {
|
||||
var (
|
||||
resp sdk.BeginBlock
|
||||
err error
|
||||
)
|
||||
|
||||
if app.beginBlocker != nil {
|
||||
resp, err = app.beginBlocker(app.finalizeBlockState.ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// append BeginBlock attributes to all events in the EndBlock response
|
||||
for i, event := range resp.Events {
|
||||
resp.Events[i].Attributes = append(
|
||||
event.Attributes,
|
||||
abci.EventAttribute{Key: "mode", Value: "BeginBlock"},
|
||||
)
|
||||
}
|
||||
|
||||
resp.Events = sdk.MarkEventsToIndex(resp.Events, app.indexEvents)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (app *BaseApp) deliverTx(tx []byte) *abci.ExecTxResult {
|
||||
gInfo := sdk.GasInfo{}
|
||||
resultStr := "successful"
|
||||
|
||||
var resp *abci.ExecTxResult
|
||||
|
||||
defer func() {
|
||||
telemetry.IncrCounter(1, "tx", "count")
|
||||
telemetry.IncrCounter(1, "tx", resultStr)
|
||||
telemetry.SetGauge(float32(gInfo.GasUsed), "tx", "gas", "used")
|
||||
telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted")
|
||||
}()
|
||||
|
||||
gInfo, result, anteEvents, err := app.runTx(execModeFinalize, tx)
|
||||
if err != nil {
|
||||
resultStr = "failed"
|
||||
resp = sdkerrors.ResponseExecTxResultWithEvents(
|
||||
err,
|
||||
gInfo.GasWanted,
|
||||
gInfo.GasUsed,
|
||||
sdk.MarkEventsToIndex(anteEvents, app.indexEvents),
|
||||
app.trace,
|
||||
)
|
||||
return resp
|
||||
}
|
||||
|
||||
resp = &abci.ExecTxResult{
|
||||
GasWanted: int64(gInfo.GasWanted),
|
||||
GasUsed: int64(gInfo.GasUsed),
|
||||
Log: result.Log,
|
||||
Data: result.Data,
|
||||
Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents),
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// endBlock is an application-defined function that is called after transactions
|
||||
// have been processed in FinalizeBlock.
|
||||
func (app *BaseApp) endBlock(ctx context.Context) (sdk.EndBlock, error) {
|
||||
var endblock sdk.EndBlock
|
||||
|
||||
if app.endBlocker != nil {
|
||||
eb, err := app.endBlocker(app.finalizeBlockState.ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// append EndBlock attributes to all events in the EndBlock response
|
||||
for i, event := range eb.Events {
|
||||
eb.Events[i].Attributes = append(
|
||||
event.Attributes,
|
||||
abci.EventAttribute{Key: "mode", Value: "EndBlock"},
|
||||
)
|
||||
}
|
||||
|
||||
eb.Events = sdk.MarkEventsToIndex(eb.Events, app.indexEvents)
|
||||
endblock = eb
|
||||
}
|
||||
|
||||
return endblock, nil
|
||||
}
|
||||
|
||||
// runTx processes a transaction within a given execution mode, encoded transaction
|
||||
// bytes, and the decoded transaction itself. All state transitions occur through
|
||||
// a cached Context depending on the mode provided. State only gets persisted
|
||||
@@ -618,7 +763,7 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context
|
||||
// Note, gas execution info is always returned. A reference to a Result is
|
||||
// returned if the tx does not run out of gas and if all the messages are valid
|
||||
// and execute successfully. An error is returned otherwise.
|
||||
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, result *sdk.Result, anteEvents []abci.Event, priority int64, err error) {
|
||||
func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, result *sdk.Result, anteEvents []abci.Event, err error) {
|
||||
// NOTE: GasWanted should be returned by the AnteHandler. GasUsed is
|
||||
// determined by the GasMeter. We need access to the context to get the gas
|
||||
// meter, so we initialize upfront.
|
||||
@@ -628,8 +773,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
ms := ctx.MultiStore()
|
||||
|
||||
// only run the tx if there is block gas remaining
|
||||
if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() {
|
||||
return gInfo, nil, nil, 0, errorsmod.Wrap(sdkerrors.ErrOutOfGas, "no block gas left to run tx")
|
||||
if mode == execModeFinalize && ctx.BlockGasMeter().IsOutOfGas() {
|
||||
return gInfo, nil, nil, errorsmod.Wrap(sdkerrors.ErrOutOfGas, "no block gas left to run tx")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -661,18 +806,18 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
// NOTE: consumeBlockGas must exist in a separate defer function from the
|
||||
// general deferred recovery function to recover from consumeBlockGas as it'll
|
||||
// be executed first (deferred statements are executed as stack).
|
||||
if mode == runTxModeDeliver {
|
||||
if mode == execModeFinalize {
|
||||
defer consumeBlockGas()
|
||||
}
|
||||
|
||||
tx, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
return sdk.GasInfo{}, nil, nil, 0, err
|
||||
return sdk.GasInfo{}, nil, nil, err
|
||||
}
|
||||
|
||||
msgs := tx.GetMsgs()
|
||||
if err := validateBasicTxMsgs(msgs); err != nil {
|
||||
return sdk.GasInfo{}, nil, nil, 0, err
|
||||
return sdk.GasInfo{}, nil, nil, err
|
||||
}
|
||||
|
||||
if app.anteHandler != nil {
|
||||
@@ -690,7 +835,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
// performance benefits, but it'll be more difficult to get right.
|
||||
anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
|
||||
anteCtx = anteCtx.WithEventManager(sdk.NewEventManager())
|
||||
newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate)
|
||||
newCtx, err := app.anteHandler(anteCtx, tx, mode == execModeSimulate)
|
||||
|
||||
if !newCtx.IsZero() {
|
||||
// At this point, newCtx.MultiStore() is a store branch, or something else
|
||||
@@ -708,23 +853,22 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
gasWanted = ctx.GasMeter().Limit()
|
||||
|
||||
if err != nil {
|
||||
return gInfo, nil, nil, 0, err
|
||||
return gInfo, nil, nil, err
|
||||
}
|
||||
|
||||
priority = ctx.Priority()
|
||||
msCache.Write()
|
||||
anteEvents = events.ToABCIEvents()
|
||||
}
|
||||
|
||||
if mode == runTxModeCheck {
|
||||
if mode == execModeCheck {
|
||||
err = app.mempool.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return gInfo, nil, anteEvents, priority, err
|
||||
return gInfo, nil, anteEvents, err
|
||||
}
|
||||
} else if mode == runTxModeDeliver {
|
||||
} else if mode == execModeFinalize {
|
||||
err = app.mempool.Remove(tx)
|
||||
if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {
|
||||
return gInfo, nil, anteEvents, priority,
|
||||
return gInfo, nil, anteEvents,
|
||||
fmt.Errorf("failed to remove tx from mempool: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -748,28 +892,28 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
// Note that the state is still preserved.
|
||||
postCtx := runMsgCtx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
newCtx, err := app.postHandler(postCtx, tx, mode == runTxModeSimulate, err == nil)
|
||||
newCtx, err := app.postHandler(postCtx, tx, mode == execModeSimulate, err == nil)
|
||||
if err != nil {
|
||||
return gInfo, nil, anteEvents, priority, err
|
||||
return gInfo, nil, anteEvents, err
|
||||
}
|
||||
|
||||
result.Events = append(result.Events, newCtx.EventManager().ABCIEvents()...)
|
||||
}
|
||||
|
||||
if mode == runTxModeDeliver {
|
||||
if mode == execModeFinalize {
|
||||
// When block gas exceeds, it'll panic and won't commit the cached store.
|
||||
consumeBlockGas()
|
||||
|
||||
msCache.Write()
|
||||
}
|
||||
|
||||
if len(anteEvents) > 0 && (mode == runTxModeDeliver || mode == runTxModeSimulate) {
|
||||
if len(anteEvents) > 0 && (mode == execModeFinalize || mode == execModeSimulate) {
|
||||
// append the events in the order of occurrence
|
||||
result.Events = append(anteEvents, result.Events...)
|
||||
}
|
||||
}
|
||||
|
||||
return gInfo, result, anteEvents, priority, err
|
||||
return gInfo, result, anteEvents, err
|
||||
}
|
||||
|
||||
// runMsgs iterates through a list of messages and executes them with the provided
|
||||
@@ -777,13 +921,13 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
// and DeliverTx. An error is returned if any single message fails or if a
|
||||
// Handler does not exist for a given message route. Otherwise, a reference to a
|
||||
// Result is returned. The caller must not commit state if an error is returned.
|
||||
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*sdk.Result, error) {
|
||||
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode execMode) (*sdk.Result, error) {
|
||||
events := sdk.EmptyEvents()
|
||||
var msgResponses []*codectypes.Any
|
||||
|
||||
// NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter.
|
||||
for i, msg := range msgs {
|
||||
if mode != runTxModeDeliver && mode != runTxModeSimulate {
|
||||
if mode != execModeFinalize && mode != execModeSimulate {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -874,7 +1018,7 @@ func (app *BaseApp) PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, _, _, _, err = app.runTx(runTxPrepareProposal, bz)
|
||||
_, _, _, err = app.runTx(execModePrepareProposal, bz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -893,7 +1037,7 @@ func (app *BaseApp) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, _, _, _, err = app.runTx(runTxProcessProposal, txBz)
|
||||
_, _, _, err = app.runTx(execModeProcessProposal, txBz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -901,137 +1045,6 @@ func (app *BaseApp) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error) {
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
type (
|
||||
// ProposalTxVerifier defines the interface that is implemented by BaseApp,
|
||||
// that any custom ABCI PrepareProposal and ProcessProposal handler can use
|
||||
// to verify a transaction.
|
||||
ProposalTxVerifier interface {
|
||||
PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error)
|
||||
ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error)
|
||||
}
|
||||
|
||||
// DefaultProposalHandler defines the default ABCI PrepareProposal and
|
||||
// ProcessProposal handlers.
|
||||
DefaultProposalHandler struct {
|
||||
mempool mempool.Mempool
|
||||
txVerifier ProposalTxVerifier
|
||||
}
|
||||
)
|
||||
|
||||
func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier) DefaultProposalHandler {
|
||||
return DefaultProposalHandler{
|
||||
mempool: mp,
|
||||
txVerifier: txVerifier,
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareProposalHandler returns the default implementation for processing an
|
||||
// ABCI proposal. The application's mempool is enumerated and all valid
|
||||
// transactions are added to the proposal. Transactions are valid if they:
|
||||
//
|
||||
// 1) Successfully encode to bytes.
|
||||
// 2) Are valid (i.e. pass runTx, AnteHandler only).
|
||||
//
|
||||
// Enumeration is halted once RequestPrepareProposal.MaxBytes of transactions is
|
||||
// reached or the mempool is exhausted.
|
||||
//
|
||||
// Note:
|
||||
//
|
||||
// - Step (2) is identical to the validation step performed in
|
||||
// DefaultProcessProposal. It is very important that the same validation logic
|
||||
// is used in both steps, and applications must ensure that this is the case in
|
||||
// non-default handlers.
|
||||
//
|
||||
// - If no mempool is set or if the mempool is a no-op mempool, the transactions
|
||||
// requested from CometBFT will simply be returned, which, by default, are in
|
||||
// FIFO order.
|
||||
func (h DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
|
||||
return func(ctx sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
|
||||
// If the mempool is nil or a no-op mempool, we simply return the transactions
|
||||
// requested from CometBFT, which, by default, should be in FIFO order.
|
||||
_, isNoOp := h.mempool.(mempool.NoOpMempool)
|
||||
if h.mempool == nil || isNoOp {
|
||||
return abci.ResponsePrepareProposal{Txs: req.Txs}
|
||||
}
|
||||
|
||||
var (
|
||||
selectedTxs [][]byte
|
||||
totalTxBytes int64
|
||||
)
|
||||
|
||||
iterator := h.mempool.Select(ctx, req.Txs)
|
||||
|
||||
for iterator != nil {
|
||||
memTx := iterator.Tx()
|
||||
|
||||
// NOTE: Since transaction verification was already executed in CheckTx,
|
||||
// which calls mempool.Insert, in theory everything in the pool should be
|
||||
// valid. But some mempool implementations may insert invalid txs, so we
|
||||
// check again.
|
||||
bz, err := h.txVerifier.PrepareProposalVerifyTx(memTx)
|
||||
if err != nil {
|
||||
err := h.mempool.Remove(memTx)
|
||||
if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
txSize := int64(len(bz))
|
||||
if totalTxBytes += txSize; totalTxBytes <= req.MaxTxBytes {
|
||||
selectedTxs = append(selectedTxs, bz)
|
||||
} else {
|
||||
// We've reached capacity per req.MaxTxBytes so we cannot select any
|
||||
// more transactions.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
iterator = iterator.Next()
|
||||
}
|
||||
|
||||
return abci.ResponsePrepareProposal{Txs: selectedTxs}
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessProposalHandler returns the default implementation for processing an
|
||||
// ABCI proposal. Every transaction in the proposal must pass 2 conditions:
|
||||
//
|
||||
// 1. The transaction bytes must decode to a valid transaction.
|
||||
// 2. The transaction must be valid (i.e. pass runTx, AnteHandler only)
|
||||
//
|
||||
// If any transaction fails to pass either condition, the proposal is rejected.
|
||||
// Note that step (2) is identical to the validation step performed in
|
||||
// DefaultPrepareProposal. It is very important that the same validation logic
|
||||
// is used in both steps, and applications must ensure that this is the case in
|
||||
// non-default handlers.
|
||||
func (h DefaultProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
|
||||
return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
for _, txBytes := range req.Txs {
|
||||
_, err := h.txVerifier.ProcessProposalVerifyTx(txBytes)
|
||||
if err != nil {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
}
|
||||
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpPrepareProposal defines a no-op PrepareProposal handler. It will always
|
||||
// return the transactions sent by the client's request.
|
||||
func NoOpPrepareProposal() sdk.PrepareProposalHandler {
|
||||
return func(_ sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
|
||||
return abci.ResponsePrepareProposal{Txs: req.Txs}
|
||||
}
|
||||
}
|
||||
|
||||
// NoOpProcessProposal defines a no-op ProcessProposal Handler. It will always
|
||||
// return ACCEPT.
|
||||
func NoOpProcessProposal() sdk.ProcessProposalHandler {
|
||||
return func(_ sdk.Context, _ abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
}
|
||||
}
|
||||
|
||||
// Close is called in start cmd to gracefully cleanup resources.
|
||||
func (app *BaseApp) Close() error {
|
||||
return nil
|
||||
|
||||
+68
-53
@@ -1,6 +1,7 @@
|
||||
package baseapp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
@@ -97,7 +98,7 @@ func NewBaseAppSuiteWithSnapshots(t *testing.T, cfg SnapshotsConfig, opts ...fun
|
||||
|
||||
baseapptestutil.RegisterKeyValueServer(suite.baseApp.MsgServiceRouter(), MsgKeyValueImpl{})
|
||||
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{
|
||||
suite.baseApp.InitChain(&abci.RequestInitChain{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
|
||||
@@ -105,8 +106,8 @@ func NewBaseAppSuiteWithSnapshots(t *testing.T, cfg SnapshotsConfig, opts ...fun
|
||||
keyCounter := 0
|
||||
|
||||
for height := int64(1); height <= int64(cfg.blocks); height++ {
|
||||
suite.baseApp.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: height}})
|
||||
|
||||
txs := [][]byte{}
|
||||
for txNum := 0; txNum < cfg.blockTxs; txNum++ {
|
||||
msgs := []sdk.Msg{}
|
||||
for msgNum := 0; msgNum < 100; msgNum++ {
|
||||
@@ -127,12 +128,17 @@ func NewBaseAppSuiteWithSnapshots(t *testing.T, cfg SnapshotsConfig, opts ...fun
|
||||
txBytes, err := suite.txConfig.TxEncoder()(builder.GetTx())
|
||||
require.NoError(t, err)
|
||||
|
||||
resp := suite.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.True(t, resp.IsOK(), "%v", resp.String())
|
||||
txs = append(txs, txBytes)
|
||||
}
|
||||
|
||||
suite.baseApp.EndBlock(abci.RequestEndBlock{Height: height})
|
||||
suite.baseApp.Commit()
|
||||
_, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{
|
||||
Height: height,
|
||||
Txs: txs,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = suite.baseApp.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
// wait for snapshot to be taken, since it happens asynchronously
|
||||
if cfg.snapshotInterval > 0 && uint64(height)%cfg.snapshotInterval == 0 {
|
||||
@@ -177,16 +183,18 @@ func TestLoadVersion(t *testing.T) {
|
||||
require.Equal(t, emptyCommitID, lastID)
|
||||
|
||||
// execute a block, collect commit ID
|
||||
header := cmtproto.Header{Height: 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res := app.Commit()
|
||||
commitID1 := storetypes.CommitID{Version: 1, Hash: res.Data}
|
||||
res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1})
|
||||
require.NoError(t, err)
|
||||
commitID1 := storetypes.CommitID{Version: 1, Hash: res.AppHash}
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
// execute a block, collect commit ID
|
||||
header = cmtproto.Header{Height: 2}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res = app.Commit()
|
||||
commitID2 := storetypes.CommitID{Version: 2, Hash: res.Data}
|
||||
res, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 2})
|
||||
require.NoError(t, err)
|
||||
commitID2 := storetypes.CommitID{Version: 2, Hash: res.AppHash}
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
// reload with LoadLatestVersion
|
||||
app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt)
|
||||
@@ -205,8 +213,10 @@ func TestLoadVersion(t *testing.T) {
|
||||
|
||||
testLoadVersionHelper(t, app, int64(1), commitID1)
|
||||
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
app.Commit()
|
||||
_, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 2})
|
||||
require.NoError(t, err)
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
testLoadVersionHelper(t, app, int64(2), commitID2)
|
||||
}
|
||||
@@ -289,9 +299,11 @@ func TestSetLoader(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
|
||||
// "execute" one block
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: 2}})
|
||||
res := app.Commit()
|
||||
require.NotNil(t, res.Data)
|
||||
res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 2})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.AppHash)
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
// check db is properly updated
|
||||
checkStore(t, db, 2, tc.loadStoreKey, k, v)
|
||||
@@ -307,7 +319,8 @@ func TestVersionSetterGetter(t *testing.T) {
|
||||
app := baseapp.NewBaseApp(name, log.NewTestLogger(t), db, nil, pruningOpt)
|
||||
|
||||
require.Equal(t, "", app.Version())
|
||||
res := app.Query(abci.RequestQuery{Path: "app/version"})
|
||||
res, err := app.Query(context.TODO(), &abci.RequestQuery{Path: "app/version"})
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsOK())
|
||||
require.Equal(t, "", string(res.Value))
|
||||
|
||||
@@ -315,7 +328,8 @@ func TestVersionSetterGetter(t *testing.T) {
|
||||
app.SetVersion(versionString)
|
||||
require.Equal(t, versionString, app.Version())
|
||||
|
||||
res = app.Query(abci.RequestQuery{Path: "app/version"})
|
||||
res, err = app.Query(context.TODO(), &abci.RequestQuery{Path: "app/version"})
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsOK())
|
||||
require.Equal(t, versionString, string(res.Value))
|
||||
}
|
||||
@@ -334,10 +348,11 @@ func TestLoadVersionInvalid(t *testing.T) {
|
||||
err = app.LoadVersion(-1)
|
||||
require.Error(t, err)
|
||||
|
||||
header := cmtproto.Header{Height: 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res := app.Commit()
|
||||
commitID1 := storetypes.CommitID{Version: 1, Hash: res.Data}
|
||||
res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1})
|
||||
require.NoError(t, err)
|
||||
commitID1 := storetypes.CommitID{Version: 1, Hash: res.AppHash}
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a new app with the stores mounted under the same cap key
|
||||
app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt)
|
||||
@@ -437,13 +452,10 @@ func TestCustomRunTxPanicHandler(t *testing.T) {
|
||||
}
|
||||
suite := NewBaseAppSuite(t, anteOpt)
|
||||
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{
|
||||
suite.baseApp.InitChain(&abci.RequestInitChain{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
|
||||
header := cmtproto.Header{Height: 1}
|
||||
suite.baseApp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
suite.baseApp.AddRunTxRecoveryHandler(func(recoveryObj interface{}) error {
|
||||
err, ok := recoveryObj.(error)
|
||||
if !ok {
|
||||
@@ -462,7 +474,9 @@ func TestCustomRunTxPanicHandler(t *testing.T) {
|
||||
tx := newTxCounter(t, suite.txConfig, 0, 0)
|
||||
|
||||
require.PanicsWithValue(t, customPanicMsg, func() {
|
||||
suite.baseApp.SimDeliver(suite.txConfig.TxEncoder(), tx)
|
||||
bz, err := suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{bz}})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -477,13 +491,10 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
deliverKey := []byte("deliver-key")
|
||||
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, deliverKey})
|
||||
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{
|
||||
suite.baseApp.InitChain(&abci.RequestInitChain{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
|
||||
header := cmtproto.Header{Height: suite.baseApp.LastBlockHeight() + 1}
|
||||
suite.baseApp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
// execute a tx that will fail ante handler execution
|
||||
//
|
||||
// NOTE: State should not be mutated here. This will be implicitly checked by
|
||||
@@ -494,11 +505,12 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
txBytes, err := suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := suite.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res.Events)
|
||||
require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
require.False(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
ctx := getDeliverStateCtx(suite.baseApp)
|
||||
ctx := getFinalizeBlockStateCtx(suite.baseApp)
|
||||
store := ctx.KVStore(capKey1)
|
||||
require.Equal(t, int64(0), getIntFromStore(t, store, anteKey))
|
||||
|
||||
@@ -510,11 +522,12 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
txBytes, err = suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res = suite.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.NotEmpty(t, res.Events)
|
||||
require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
res, err = suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res.Events)
|
||||
require.False(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
ctx = getDeliverStateCtx(suite.baseApp)
|
||||
ctx = getFinalizeBlockStateCtx(suite.baseApp)
|
||||
store = ctx.KVStore(capKey1)
|
||||
require.Equal(t, int64(1), getIntFromStore(t, store, anteKey))
|
||||
require.Equal(t, int64(0), getIntFromStore(t, store, deliverKey))
|
||||
@@ -526,16 +539,16 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
txBytes, err = suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res = suite.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.NotEmpty(t, res.Events)
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
res, err = suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, res.TxResults[0].Events)
|
||||
require.True(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
ctx = getDeliverStateCtx(suite.baseApp)
|
||||
ctx = getFinalizeBlockStateCtx(suite.baseApp)
|
||||
store = ctx.KVStore(capKey1)
|
||||
require.Equal(t, int64(2), getIntFromStore(t, store, anteKey))
|
||||
require.Equal(t, int64(1), getIntFromStore(t, store, deliverKey))
|
||||
|
||||
suite.baseApp.EndBlock(abci.RequestEndBlock{})
|
||||
suite.baseApp.Commit()
|
||||
}
|
||||
|
||||
@@ -550,10 +563,10 @@ func TestABCI_CreateQueryContext(t *testing.T) {
|
||||
name := t.Name()
|
||||
app := baseapp.NewBaseApp(name, log.NewTestLogger(t), db, nil)
|
||||
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: 1}})
|
||||
app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1})
|
||||
app.Commit()
|
||||
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: 2}})
|
||||
app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 2})
|
||||
app.Commit()
|
||||
|
||||
testCases := []struct {
|
||||
@@ -590,8 +603,8 @@ func TestSetMinGasPrices(t *testing.T) {
|
||||
|
||||
func TestGetMaximumBlockGas(t *testing.T) {
|
||||
suite := NewBaseAppSuite(t)
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{})
|
||||
ctx := suite.baseApp.NewContext(true, cmtproto.Header{})
|
||||
suite.baseApp.InitChain(&abci.RequestInitChain{})
|
||||
ctx := suite.baseApp.NewContext(true, cmtproto.Header{}) // TODO remove header here
|
||||
|
||||
suite.baseApp.StoreConsensusParams(ctx, cmtproto.ConsensusParams{Block: &cmtproto.BlockParams{MaxGas: 0}})
|
||||
require.Equal(t, uint64(0), suite.baseApp.GetMaximumBlockGas(ctx))
|
||||
@@ -634,9 +647,11 @@ func TestLoadVersionPruning(t *testing.T) {
|
||||
// Commit seven blocks, of which 7 (latest) is kept in addition to 6, 5
|
||||
// (keep recent) and 3 (keep every).
|
||||
for i := int64(1); i <= 7; i++ {
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: i}})
|
||||
res := app.Commit()
|
||||
lastCommitID = storetypes.CommitID{Version: i, Hash: res.Data}
|
||||
res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: i})
|
||||
require.NoError(t, err)
|
||||
_, err = app.Commit()
|
||||
require.NoError(t, err)
|
||||
lastCommitID = storetypes.CommitID{Version: i, Hash: res.AppHash}
|
||||
}
|
||||
|
||||
for _, v := range []int64{1, 2, 4} {
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestBaseApp_BlockGas(t *testing.T) {
|
||||
genState := GenesisStateWithSingleValidator(t, cdc, appBuilder)
|
||||
stateBytes, err := cmtjson.MarshalIndent(genState, "", " ")
|
||||
require.NoError(t, err)
|
||||
bapp.InitChain(abci.RequestInitChain{
|
||||
bapp.InitChain(&abci.RequestInitChain{
|
||||
Validators: []abci.ValidatorUpdate{},
|
||||
ConsensusParams: simtestutil.DefaultConsensusParams,
|
||||
AppStateBytes: stateBytes,
|
||||
@@ -158,22 +158,22 @@ func TestBaseApp_BlockGas(t *testing.T) {
|
||||
_, txBytes, err := createTestTx(txConfig, txBuilder, privs, accNums, accSeqs, ctx.ChainID())
|
||||
require.NoError(t, err)
|
||||
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: 1}})
|
||||
rsp := bapp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
rsp, err := bapp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// check result
|
||||
ctx = bapp.GetContextForDeliverTx(txBytes)
|
||||
ctx = bapp.GetContextForFinalizeBlock(txBytes)
|
||||
okValue := ctx.KVStore(bapp.UnsafeFindStoreKey(banktypes.ModuleName)).Get([]byte("ok"))
|
||||
|
||||
if tc.expErr {
|
||||
if tc.panicTx {
|
||||
require.Equal(t, sdkerrors.ErrPanic.ABCICode(), rsp.Code)
|
||||
require.Equal(t, sdkerrors.ErrPanic.ABCICode(), rsp.TxResults[0].Code)
|
||||
} else {
|
||||
require.Equal(t, sdkerrors.ErrOutOfGas.ABCICode(), rsp.Code)
|
||||
require.Equal(t, sdkerrors.ErrOutOfGas.ABCICode(), rsp.TxResults[0].Code)
|
||||
}
|
||||
require.Empty(t, okValue)
|
||||
} else {
|
||||
require.Equal(t, uint32(0), rsp.Code)
|
||||
require.Equal(t, uint32(0), rsp.TxResults[0].Code)
|
||||
require.Equal(t, []byte("ok"), okValue)
|
||||
}
|
||||
// check block gas is always consumed
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ var _ genesis.TxHandler = (*BaseApp)(nil)
|
||||
// ExecuteGenesisTx implements genesis.GenesisState from
|
||||
// cosmossdk.io/core/genesis to set initial state in genesis
|
||||
func (ba BaseApp) ExecuteGenesisTx(tx []byte) error {
|
||||
res := ba.DeliverTx(types.RequestDeliverTx{Tx: tx})
|
||||
res := ba.deliverTx(tx)
|
||||
|
||||
if res.Code != types.CodeTypeOK {
|
||||
return errors.New(res.Log)
|
||||
|
||||
@@ -38,7 +38,7 @@ func NewGRPCQueryRouter() *GRPCQueryRouter {
|
||||
|
||||
// GRPCQueryHandler defines a function type which handles ABCI Query requests
|
||||
// using gRPC
|
||||
type GRPCQueryHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error)
|
||||
type GRPCQueryHandler = func(ctx sdk.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error)
|
||||
|
||||
// Route returns the GRPCQueryHandler for a given query route path or nil
|
||||
// if not found
|
||||
@@ -76,25 +76,25 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf
|
||||
)
|
||||
}
|
||||
|
||||
qrt.routes[fqName] = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) {
|
||||
qrt.routes[fqName] = func(ctx sdk.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) {
|
||||
// call the method handler from the service description with the handler object,
|
||||
// a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data
|
||||
res, err := methodHandler(handler, ctx, func(i interface{}) error {
|
||||
return qrt.cdc.Unmarshal(req.Data, i)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return abci.ResponseQuery{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// proto marshal the result bytes
|
||||
var resBytes []byte
|
||||
resBytes, err = qrt.cdc.Marshal(res)
|
||||
if err != nil {
|
||||
return abci.ResponseQuery{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return the result bytes as the response value
|
||||
return abci.ResponseQuery{
|
||||
return &abci.ResponseQuery{
|
||||
Height: req.Height,
|
||||
Value: resBytes,
|
||||
}, nil
|
||||
|
||||
@@ -45,7 +45,7 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := querier(q.Ctx, abci.RequestQuery{Data: reqBz})
|
||||
res, err := querier(q.Ctx, &abci.RequestQuery{Data: reqBz})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+5
-5
@@ -83,8 +83,8 @@ type voteInfoWrapper struct {
|
||||
|
||||
var _ comet.VoteInfo = (*voteInfoWrapper)(nil)
|
||||
|
||||
func (v voteInfoWrapper) SignedLastBlock() bool {
|
||||
return v.VoteInfo.SignedLastBlock
|
||||
func (v voteInfoWrapper) GetBlockIDFlag() comet.BlockIDFlag {
|
||||
return comet.BlockIDFlag(v.VoteInfo.BlockIdFlag)
|
||||
}
|
||||
|
||||
func (v voteInfoWrapper) Validator() comet.Validator {
|
||||
@@ -131,7 +131,7 @@ func (m misbehaviorWrapper) TotalVotingPower() int64 {
|
||||
}
|
||||
|
||||
type prepareProposalInfo struct {
|
||||
abci.RequestPrepareProposal
|
||||
*abci.RequestPrepareProposal
|
||||
}
|
||||
|
||||
var _ comet.BlockInfo = (*prepareProposalInfo)(nil)
|
||||
@@ -188,8 +188,8 @@ type extendedVoteInfoWrapper struct {
|
||||
|
||||
var _ comet.VoteInfo = (*extendedVoteInfoWrapper)(nil)
|
||||
|
||||
func (e extendedVoteInfoWrapper) SignedLastBlock() bool {
|
||||
return e.ExtendedVoteInfo.SignedLastBlock
|
||||
func (e extendedVoteInfoWrapper) GetBlockIDFlag() comet.BlockIDFlag {
|
||||
return comet.BlockIDFlag(e.ExtendedVoteInfo.BlockIdFlag)
|
||||
}
|
||||
|
||||
func (e extendedVoteInfoWrapper) Validator() comet.Validator {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -116,7 +115,7 @@ func TestMsgService(t *testing.T) {
|
||||
app.MsgServiceRouter(),
|
||||
testdata.MsgServerImpl{},
|
||||
)
|
||||
_ = app.BeginBlock(abci.RequestBeginBlock{Header: cmtproto.Header{Height: 1}})
|
||||
app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1})
|
||||
|
||||
msg := testdata.MsgCreateDog{Dog: &testdata.Dog{Name: "Spot"}}
|
||||
|
||||
@@ -157,6 +156,7 @@ func TestMsgService(t *testing.T) {
|
||||
// Send the tx to the app
|
||||
txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx())
|
||||
require.NoError(t, err)
|
||||
res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.Equal(t, abci.CodeTypeOK, res.Code, "res=%+v", res)
|
||||
res, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abci.CodeTypeOK, res.TxResults[0].Code, "res=%+v", res)
|
||||
}
|
||||
|
||||
@@ -300,6 +300,22 @@ func (app *BaseApp) SetPrepareProposal(handler sdk.PrepareProposalHandler) {
|
||||
app.prepareProposal = handler
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetExtendVoteHandler(handler sdk.ExtendVoteHandler) {
|
||||
if app.sealed {
|
||||
panic("SetExtendVoteHandler() on sealed BaseApp")
|
||||
}
|
||||
|
||||
app.extendVote = handler
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetVerifyVoteExtensionHandler(handler sdk.VerifyVoteExtensionHandler) {
|
||||
if app.sealed {
|
||||
panic("SetVerifyVoteExtensionHandler() on sealed BaseApp")
|
||||
}
|
||||
|
||||
app.verifyVoteExt = handler
|
||||
}
|
||||
|
||||
// SetStoreMetrics sets the prepare proposal function for the BaseApp.
|
||||
func (app *BaseApp) SetStoreMetrics(gatherer metrics.StoreMetrics) {
|
||||
if app.sealed {
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
package baseapp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
pruningtypes "cosmossdk.io/store/pruning/types"
|
||||
snapshottypes "cosmossdk.io/store/snapshots/types"
|
||||
)
|
||||
|
||||
func TestABCI_ListSnapshots(t *testing.T) {
|
||||
ssCfg := SnapshotsConfig{
|
||||
blocks: 5,
|
||||
blockTxs: 4,
|
||||
snapshotInterval: 2,
|
||||
snapshotKeepRecent: 2,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
}
|
||||
|
||||
suite := NewBaseAppSuiteWithSnapshots(t, ssCfg)
|
||||
|
||||
resp, err := suite.baseApp.ListSnapshots(&abci.RequestListSnapshots{})
|
||||
require.NoError(t, err)
|
||||
for _, s := range resp.Snapshots {
|
||||
require.NotEmpty(t, s.Hash)
|
||||
require.NotEmpty(t, s.Metadata)
|
||||
|
||||
s.Hash = nil
|
||||
s.Metadata = nil
|
||||
}
|
||||
|
||||
require.Equal(t, &abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{
|
||||
{Height: 4, Format: snapshottypes.CurrentFormat, Chunks: 2},
|
||||
{Height: 2, Format: snapshottypes.CurrentFormat, Chunks: 1},
|
||||
}}, resp)
|
||||
}
|
||||
|
||||
func TestABCI_SnapshotWithPruning(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
ssCfg SnapshotsConfig
|
||||
expectedSnapshots []*abci.Snapshot
|
||||
}{
|
||||
"prune nothing with snapshot": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 20,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 5,
|
||||
snapshotKeepRecent: 1,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{
|
||||
{Height: 20, Format: snapshottypes.CurrentFormat, Chunks: 5},
|
||||
},
|
||||
},
|
||||
"prune everything with snapshot": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 20,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 5,
|
||||
snapshotKeepRecent: 1,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningEverything),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{
|
||||
{Height: 20, Format: snapshottypes.CurrentFormat, Chunks: 5},
|
||||
},
|
||||
},
|
||||
"default pruning with snapshot": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 20,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 5,
|
||||
snapshotKeepRecent: 1,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningDefault),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{
|
||||
{Height: 20, Format: snapshottypes.CurrentFormat, Chunks: 5},
|
||||
},
|
||||
},
|
||||
"custom": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 25,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 5,
|
||||
snapshotKeepRecent: 2,
|
||||
pruningOpts: pruningtypes.NewCustomPruningOptions(12, 12),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{
|
||||
{Height: 25, Format: snapshottypes.CurrentFormat, Chunks: 6},
|
||||
{Height: 20, Format: snapshottypes.CurrentFormat, Chunks: 5},
|
||||
},
|
||||
},
|
||||
"no snapshots": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 10,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 0, // 0 implies disable snapshots
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{},
|
||||
},
|
||||
"keep all snapshots": {
|
||||
ssCfg: SnapshotsConfig{
|
||||
blocks: 10,
|
||||
blockTxs: 2,
|
||||
snapshotInterval: 3,
|
||||
snapshotKeepRecent: 0, // 0 implies keep all snapshots
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
},
|
||||
expectedSnapshots: []*abci.Snapshot{
|
||||
{Height: 9, Format: snapshottypes.CurrentFormat, Chunks: 2},
|
||||
{Height: 6, Format: snapshottypes.CurrentFormat, Chunks: 2},
|
||||
{Height: 3, Format: snapshottypes.CurrentFormat, Chunks: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
suite := NewBaseAppSuiteWithSnapshots(t, tc.ssCfg)
|
||||
|
||||
resp, err := suite.baseApp.ListSnapshots(&abci.RequestListSnapshots{})
|
||||
require.NoError(t, err)
|
||||
for _, s := range resp.Snapshots {
|
||||
require.NotEmpty(t, s.Hash)
|
||||
require.NotEmpty(t, s.Metadata)
|
||||
|
||||
s.Hash = nil
|
||||
s.Metadata = nil
|
||||
}
|
||||
|
||||
require.Equal(t, &abci.ResponseListSnapshots{Snapshots: tc.expectedSnapshots}, resp)
|
||||
|
||||
// Validate that heights were pruned correctly by querying the state at the last height that should be present relative to latest
|
||||
// and the first height that should be pruned.
|
||||
//
|
||||
// Exceptions:
|
||||
// * Prune nothing: should be able to query all heights (we only test first and latest)
|
||||
// * Prune default: should be able to query all heights (we only test first and latest)
|
||||
// * The reason for default behaving this way is that we only commit 20 heights but default has 100_000 keep-recent
|
||||
var lastExistingHeight int64
|
||||
if tc.ssCfg.pruningOpts.GetPruningStrategy() == pruningtypes.PruningNothing || tc.ssCfg.pruningOpts.GetPruningStrategy() == pruningtypes.PruningDefault {
|
||||
lastExistingHeight = 1
|
||||
} else {
|
||||
// Integer division rounds down so by multiplying back we get the last height at which we pruned
|
||||
lastExistingHeight = int64((tc.ssCfg.blocks/tc.ssCfg.pruningOpts.Interval)*tc.ssCfg.pruningOpts.Interval - tc.ssCfg.pruningOpts.KeepRecent)
|
||||
}
|
||||
|
||||
// Query 1
|
||||
res, err := suite.baseApp.Query(context.TODO(), &abci.RequestQuery{Path: fmt.Sprintf("/store/%s/key", capKey2.Name()), Data: []byte("0"), Height: lastExistingHeight})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res, "height: %d", lastExistingHeight)
|
||||
require.NotNil(t, res.Value, "height: %d", lastExistingHeight)
|
||||
|
||||
// Query 2
|
||||
res, err = suite.baseApp.Query(context.TODO(), &abci.RequestQuery{Path: fmt.Sprintf("/store/%s/key", capKey2.Name()), Data: []byte("0"), Height: lastExistingHeight - 1})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res, "height: %d", lastExistingHeight-1)
|
||||
|
||||
if tc.ssCfg.pruningOpts.GetPruningStrategy() == pruningtypes.PruningNothing || tc.ssCfg.pruningOpts.GetPruningStrategy() == pruningtypes.PruningDefault {
|
||||
// With prune nothing or default, we query height 0 which translates to the latest height.
|
||||
require.NotNil(t, res.Value, "height: %d", lastExistingHeight-1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCI_LoadSnapshotChunk(t *testing.T) {
|
||||
ssCfg := SnapshotsConfig{
|
||||
blocks: 2,
|
||||
blockTxs: 5,
|
||||
snapshotInterval: 2,
|
||||
snapshotKeepRecent: snapshottypes.CurrentFormat,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
}
|
||||
suite := NewBaseAppSuiteWithSnapshots(t, ssCfg)
|
||||
|
||||
testCases := map[string]struct {
|
||||
height uint64
|
||||
format uint32
|
||||
chunk uint32
|
||||
expectEmpty bool
|
||||
}{
|
||||
"Existing snapshot": {2, snapshottypes.CurrentFormat, 1, false},
|
||||
"Missing height": {100, snapshottypes.CurrentFormat, 1, true},
|
||||
"Missing format": {2, snapshottypes.CurrentFormat + 1, 1, true},
|
||||
"Missing chunk": {2, snapshottypes.CurrentFormat, 9, true},
|
||||
"Zero height": {0, snapshottypes.CurrentFormat, 1, true},
|
||||
"Zero format": {2, 0, 1, true},
|
||||
"Zero chunk": {2, snapshottypes.CurrentFormat, 0, false},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
resp, _ := suite.baseApp.LoadSnapshotChunk(&abci.RequestLoadSnapshotChunk{
|
||||
Height: tc.height,
|
||||
Format: tc.format,
|
||||
Chunk: tc.chunk,
|
||||
})
|
||||
if tc.expectEmpty {
|
||||
require.Equal(t, &abci.ResponseLoadSnapshotChunk{}, resp)
|
||||
return
|
||||
}
|
||||
|
||||
require.NotEmpty(t, resp.Chunk)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCI_OfferSnapshot_Errors(t *testing.T) {
|
||||
ssCfg := SnapshotsConfig{
|
||||
blocks: 0,
|
||||
blockTxs: 0,
|
||||
snapshotInterval: 2,
|
||||
snapshotKeepRecent: 2,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
}
|
||||
suite := NewBaseAppSuiteWithSnapshots(t, ssCfg)
|
||||
|
||||
m := snapshottypes.Metadata{ChunkHashes: [][]byte{{1}, {2}, {3}}}
|
||||
metadata, err := m.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
hash := []byte{1, 2, 3}
|
||||
|
||||
testCases := map[string]struct {
|
||||
snapshot *abci.Snapshot
|
||||
result abci.ResponseOfferSnapshot_Result
|
||||
}{
|
||||
"nil snapshot": {nil, abci.ResponseOfferSnapshot_REJECT},
|
||||
"invalid format": {&abci.Snapshot{
|
||||
Height: 1, Format: 9, Chunks: 3, Hash: hash, Metadata: metadata,
|
||||
}, abci.ResponseOfferSnapshot_REJECT_FORMAT},
|
||||
"incorrect chunk count": {&abci.Snapshot{
|
||||
Height: 1, Format: snapshottypes.CurrentFormat, Chunks: 2, Hash: hash, Metadata: metadata,
|
||||
}, abci.ResponseOfferSnapshot_REJECT},
|
||||
"no chunks": {&abci.Snapshot{
|
||||
Height: 1, Format: snapshottypes.CurrentFormat, Chunks: 0, Hash: hash, Metadata: metadata,
|
||||
}, abci.ResponseOfferSnapshot_REJECT},
|
||||
"invalid metadata serialization": {&abci.Snapshot{
|
||||
Height: 1, Format: snapshottypes.CurrentFormat, Chunks: 0, Hash: hash, Metadata: []byte{3, 1, 4},
|
||||
}, abci.ResponseOfferSnapshot_REJECT},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
resp, err := suite.baseApp.OfferSnapshot(&abci.RequestOfferSnapshot{Snapshot: tc.snapshot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.result, resp.Result)
|
||||
})
|
||||
}
|
||||
|
||||
// Offering a snapshot after one has been accepted should error
|
||||
resp, err := suite.baseApp.OfferSnapshot(&abci.RequestOfferSnapshot{Snapshot: &abci.Snapshot{
|
||||
Height: 1,
|
||||
Format: snapshottypes.CurrentFormat,
|
||||
Chunks: 3,
|
||||
Hash: []byte{1, 2, 3},
|
||||
Metadata: metadata,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, resp)
|
||||
|
||||
resp, err = suite.baseApp.OfferSnapshot(&abci.RequestOfferSnapshot{Snapshot: &abci.Snapshot{
|
||||
Height: 2,
|
||||
Format: snapshottypes.CurrentFormat,
|
||||
Chunks: 3,
|
||||
Hash: []byte{1, 2, 3},
|
||||
Metadata: metadata,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, resp)
|
||||
}
|
||||
|
||||
func TestABCI_ApplySnapshotChunk(t *testing.T) {
|
||||
srcCfg := SnapshotsConfig{
|
||||
blocks: 4,
|
||||
blockTxs: 10,
|
||||
snapshotInterval: 2,
|
||||
snapshotKeepRecent: 2,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
}
|
||||
srcSuite := NewBaseAppSuiteWithSnapshots(t, srcCfg)
|
||||
|
||||
targetCfg := SnapshotsConfig{
|
||||
blocks: 0,
|
||||
blockTxs: 0,
|
||||
snapshotInterval: 2,
|
||||
snapshotKeepRecent: 2,
|
||||
pruningOpts: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing),
|
||||
}
|
||||
targetSuite := NewBaseAppSuiteWithSnapshots(t, targetCfg)
|
||||
|
||||
// fetch latest snapshot to restore
|
||||
respList, err := srcSuite.baseApp.ListSnapshots(&abci.RequestListSnapshots{})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, respList.Snapshots)
|
||||
snapshot := respList.Snapshots[0]
|
||||
|
||||
// make sure the snapshot has at least 3 chunks
|
||||
require.GreaterOrEqual(t, snapshot.Chunks, uint32(3), "Not enough snapshot chunks")
|
||||
|
||||
// begin a snapshot restoration in the target
|
||||
respOffer, err := targetSuite.baseApp.OfferSnapshot(&abci.RequestOfferSnapshot{Snapshot: snapshot})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, respOffer)
|
||||
|
||||
// We should be able to pass an invalid chunk and get a verify failure, before
|
||||
// reapplying it.
|
||||
respApply, err := targetSuite.baseApp.ApplySnapshotChunk(&abci.RequestApplySnapshotChunk{
|
||||
Index: 0,
|
||||
Chunk: []byte{9},
|
||||
Sender: "sender",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &abci.ResponseApplySnapshotChunk{
|
||||
Result: abci.ResponseApplySnapshotChunk_RETRY,
|
||||
RefetchChunks: []uint32{0},
|
||||
RejectSenders: []string{"sender"},
|
||||
}, respApply)
|
||||
|
||||
// fetch each chunk from the source and apply it to the target
|
||||
for index := uint32(0); index < snapshot.Chunks; index++ {
|
||||
respChunk, err := srcSuite.baseApp.LoadSnapshotChunk(&abci.RequestLoadSnapshotChunk{
|
||||
Height: snapshot.Height,
|
||||
Format: snapshot.Format,
|
||||
Chunk: index,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, respChunk.Chunk)
|
||||
|
||||
respApply, err := targetSuite.baseApp.ApplySnapshotChunk(&abci.RequestApplySnapshotChunk{
|
||||
Index: index,
|
||||
Chunk: respChunk.Chunk,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &abci.ResponseApplySnapshotChunk{
|
||||
Result: abci.ResponseApplySnapshotChunk_ACCEPT,
|
||||
}, respApply)
|
||||
}
|
||||
|
||||
// the target should now have the same hash as the source
|
||||
require.Equal(t, srcSuite.baseApp.LastCommitID(), targetSuite.baseApp.LastCommitID())
|
||||
}
|
||||
+29
-31
@@ -5,14 +5,13 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var _ storetypes.ABCIListener = (*MockABCIListener)(nil)
|
||||
@@ -29,20 +28,12 @@ func NewMockABCIListener(name string) MockABCIListener {
|
||||
}
|
||||
}
|
||||
|
||||
func (m MockABCIListener) ListenBeginBlock(ctx context.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) error {
|
||||
func (m MockABCIListener) ListenFinalizeBlock(_ context.Context, _ abci.RequestFinalizeBlock, _ abci.ResponseFinalizeBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MockABCIListener) ListenEndBlock(ctx context.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MockABCIListener) ListenDeliverTx(ctx context.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockABCIListener) ListenCommit(ctx context.Context, res abci.ResponseCommit, changeSet []*storetypes.StoreKVPair) error {
|
||||
m.ChangeSet = changeSet
|
||||
func (m *MockABCIListener) ListenCommit(_ context.Context, _ abci.ResponseCommit, cs []*storetypes.StoreKVPair) error {
|
||||
m.ChangeSet = cs
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -59,9 +50,11 @@ func TestABCI_MultiListener_StateChanges(t *testing.T) {
|
||||
addListenerOpt := func(bapp *baseapp.BaseApp) { bapp.CommitMultiStore().AddListeners([]storetypes.StoreKey{distKey1}) }
|
||||
suite := NewBaseAppSuite(t, anteOpt, distOpt, streamingManagerOpt, addListenerOpt)
|
||||
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{
|
||||
ConsensusParams: &tmproto.ConsensusParams{},
|
||||
})
|
||||
suite.baseApp.InitChain(
|
||||
&abci.RequestInitChain{
|
||||
ConsensusParams: &tmproto.ConsensusParams{},
|
||||
},
|
||||
)
|
||||
|
||||
deliverKey := []byte("deliver-key")
|
||||
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, deliverKey})
|
||||
@@ -70,10 +63,14 @@ func TestABCI_MultiListener_StateChanges(t *testing.T) {
|
||||
txPerHeight := 5
|
||||
|
||||
for blockN := 0; blockN < nBlocks; blockN++ {
|
||||
header := tmproto.Header{Height: int64(blockN) + 1}
|
||||
suite.baseApp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
txs := [][]byte{}
|
||||
|
||||
var expectedChangeSet []*storetypes.StoreKVPair
|
||||
|
||||
// create final block context state
|
||||
_, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: int64(blockN) + 1, Txs: txs})
|
||||
require.NoError(t, err)
|
||||
|
||||
for i := 0; i < txPerHeight; i++ {
|
||||
counter := int64(blockN*txPerHeight + i)
|
||||
tx := newTxCounter(t, suite.txConfig, counter, counter)
|
||||
@@ -83,7 +80,7 @@ func TestABCI_MultiListener_StateChanges(t *testing.T) {
|
||||
|
||||
sKey := []byte(fmt.Sprintf("distKey%d", i))
|
||||
sVal := []byte(fmt.Sprintf("distVal%d", i))
|
||||
store := getDeliverStateCtx(suite.baseApp).KVStore(distKey1)
|
||||
store := getFinalizeBlockStateCtx(suite.baseApp).KVStore(distKey1)
|
||||
store.Set(sKey, sVal)
|
||||
|
||||
expectedChangeSet = append(expectedChangeSet, &storetypes.StoreKVPair{
|
||||
@@ -93,16 +90,18 @@ func TestABCI_MultiListener_StateChanges(t *testing.T) {
|
||||
Value: sVal,
|
||||
})
|
||||
|
||||
res := suite.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
txs = append(txs, txBytes)
|
||||
}
|
||||
|
||||
events := res.GetEvents()
|
||||
res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: int64(blockN) + 1, Txs: txs})
|
||||
require.NoError(t, err)
|
||||
for _, tx := range res.TxResults {
|
||||
events := tx.GetEvents()
|
||||
require.Len(t, events, 3, "should contain ante handler, message type and counter events respectively")
|
||||
require.Equal(t, sdk.MarkEventsToIndex(counterEvent("ante_handler", counter).ToABCIEvents(), map[string]struct{}{})[0], events[0], "ante handler event")
|
||||
require.Equal(t, sdk.MarkEventsToIndex(counterEvent(sdk.EventTypeMessage, counter).ToABCIEvents(), map[string]struct{}{})[0].Attributes[0], events[2].Attributes[0], "msg handler update counter event")
|
||||
// require.Equal(t, sdk.MarkEventsToIndex(counterEvent("ante_handler", counter).ToABCIEvents(), map[string]struct{}{})[0], events[0], "ante handler event")
|
||||
// require.Equal(t, sdk.MarkEventsToIndex(counterEvent(sdk.EventTypeMessage, counter).ToABCIEvents(), map[string]struct{}{})[0], events[2], "msg handler update counter event")
|
||||
}
|
||||
|
||||
suite.baseApp.EndBlock(abci.RequestEndBlock{})
|
||||
suite.baseApp.Commit()
|
||||
|
||||
require.Equal(t, expectedChangeSet, mockListener1.ChangeSet, "should contain the same changeSet")
|
||||
@@ -119,11 +118,11 @@ func Test_Ctx_with_StreamingManager(t *testing.T) {
|
||||
addListenerOpt := func(bapp *baseapp.BaseApp) { bapp.CommitMultiStore().AddListeners([]storetypes.StoreKey{distKey1}) }
|
||||
suite := NewBaseAppSuite(t, streamingManagerOpt, addListenerOpt)
|
||||
|
||||
suite.baseApp.InitChain(abci.RequestInitChain{
|
||||
suite.baseApp.InitChain(&abci.RequestInitChain{
|
||||
ConsensusParams: &tmproto.ConsensusParams{},
|
||||
})
|
||||
|
||||
ctx := getDeliverStateCtx(suite.baseApp)
|
||||
ctx := getFinalizeBlockStateCtx(suite.baseApp)
|
||||
sm := ctx.StreamingManager()
|
||||
require.NotNil(t, sm, fmt.Sprintf("nil StreamingManager: %v", sm))
|
||||
require.Equal(t, listeners, sm.ABCIListeners, fmt.Sprintf("should contain same listeners: %v", listeners))
|
||||
@@ -132,16 +131,15 @@ func Test_Ctx_with_StreamingManager(t *testing.T) {
|
||||
nBlocks := 2
|
||||
|
||||
for blockN := 0; blockN < nBlocks; blockN++ {
|
||||
header := tmproto.Header{Height: int64(blockN) + 1}
|
||||
suite.baseApp.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
ctx := getDeliverStateCtx(suite.baseApp)
|
||||
suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: int64(blockN) + 1})
|
||||
|
||||
ctx := getFinalizeBlockStateCtx(suite.baseApp)
|
||||
sm := ctx.StreamingManager()
|
||||
require.NotNil(t, sm, fmt.Sprintf("nil StreamingManager: %v", sm))
|
||||
require.Equal(t, listeners, sm.ABCIListeners, fmt.Sprintf("should contain same listeners: %v", listeners))
|
||||
require.Equal(t, true, sm.StopNodeOnErr, "should contain StopNodeOnErr = true")
|
||||
|
||||
suite.baseApp.EndBlock(abci.RequestEndBlock{})
|
||||
suite.baseApp.Commit()
|
||||
}
|
||||
}
|
||||
|
||||
+20
-9
@@ -18,13 +18,14 @@ func (app *BaseApp) SimCheck(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *
|
||||
if err != nil {
|
||||
return sdk.GasInfo{}, nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err)
|
||||
}
|
||||
gasInfo, result, _, _, err := app.runTx(runTxModeCheck, bz)
|
||||
|
||||
gasInfo, result, _, err := app.runTx(execModeCheck, bz)
|
||||
return gasInfo, result, err
|
||||
}
|
||||
|
||||
// Simulate executes a tx in simulate mode to get result and gas info.
|
||||
func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) {
|
||||
gasInfo, result, _, _, err := app.runTx(runTxModeSimulate, txBytes)
|
||||
gasInfo, result, _, err := app.runTx(execModeSimulate, txBytes)
|
||||
return gasInfo, result, err
|
||||
}
|
||||
|
||||
@@ -34,28 +35,38 @@ func (app *BaseApp) SimDeliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo,
|
||||
if err != nil {
|
||||
return sdk.GasInfo{}, nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err)
|
||||
}
|
||||
gasInfo, result, _, _, err := app.runTx(runTxModeDeliver, bz)
|
||||
gasInfo, result, _, err := app.runTx(execModeFinalize, bz)
|
||||
return gasInfo, result, err
|
||||
}
|
||||
|
||||
// Context with current {check, deliver}State of the app used by tests.
|
||||
func (app *BaseApp) NewContext(isCheckTx bool, header cmtproto.Header) sdk.Context {
|
||||
func (app *BaseApp) SimTxFinalizeBlock(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) {
|
||||
// See comment for Check().
|
||||
bz, err := txEncoder(tx)
|
||||
if err != nil {
|
||||
return sdk.GasInfo{}, nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err)
|
||||
}
|
||||
|
||||
gasInfo, result, _, err := app.runTx(execModeFinalize, bz)
|
||||
return gasInfo, result, err
|
||||
}
|
||||
|
||||
func (app *BaseApp) NewContext(isCheckTx bool, header cmtproto.Header) sdk.Context { // todo discuss how to remove header, wrapper or no
|
||||
if isCheckTx {
|
||||
return sdk.NewContext(app.checkState.ms, header, true, app.logger).
|
||||
WithMinGasPrices(app.minGasPrices)
|
||||
}
|
||||
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, app.logger)
|
||||
return sdk.NewContext(app.finalizeBlockState.ms, header, false, app.logger)
|
||||
}
|
||||
|
||||
func (app *BaseApp) NewUncachedContext(isCheckTx bool, header cmtproto.Header) sdk.Context {
|
||||
return sdk.NewContext(app.cms, header, isCheckTx, app.logger)
|
||||
}
|
||||
|
||||
func (app *BaseApp) GetContextForDeliverTx(txBytes []byte) sdk.Context {
|
||||
return app.getContextForTx(runTxModeDeliver, txBytes)
|
||||
func (app *BaseApp) GetContextForFinalizeBlock(txBytes []byte) sdk.Context {
|
||||
return app.getContextForTx(execModeFinalize, txBytes)
|
||||
}
|
||||
|
||||
func (app *BaseApp) GetContextForCheckTx(txBytes []byte) sdk.Context {
|
||||
return app.getContextForTx(runTxModeCheck, txBytes)
|
||||
return app.getContextForTx(execModeCheck, txBytes)
|
||||
}
|
||||
|
||||
@@ -299,9 +299,9 @@ func getCheckStateCtx(app *baseapp.BaseApp) sdk.Context {
|
||||
return rf.MethodByName("Context").Call(nil)[0].Interface().(sdk.Context)
|
||||
}
|
||||
|
||||
func getDeliverStateCtx(app *baseapp.BaseApp) sdk.Context {
|
||||
func getFinalizeBlockStateCtx(app *baseapp.BaseApp) sdk.Context {
|
||||
v := reflect.ValueOf(app).Elem()
|
||||
f := v.FieldByName("deliverState")
|
||||
f := v.FieldByName("finalizeBlockState")
|
||||
rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
|
||||
return rf.MethodByName("Context").Call(nil)[0].Interface().(sdk.Context)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user