feat: ABCI 1.0 baseapp integration (#13453)
This commit is contained in:
parent
90fd3e9f42
commit
61effe8260
@ -21,6 +21,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// Supported ABCI Query prefixes
|
||||
@ -38,6 +39,8 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
||||
// req.InitialHeight is 1 by default.
|
||||
initHeader := tmproto.Header{ChainID: req.ChainId, Time: req.Time}
|
||||
|
||||
app.logger.Info("InitChain", "initialHeight", req.InitialHeight, "chainID", req.ChainId)
|
||||
|
||||
// If req.InitialHeight is > 1, then we set the initial version in the
|
||||
// stores.
|
||||
if req.InitialHeight > 1 {
|
||||
@ -49,9 +52,11 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the deliver state and check state with a correct header
|
||||
// initialize states with a correct header
|
||||
app.setDeliverState(initHeader)
|
||||
app.setCheckState(initHeader)
|
||||
app.setPrepareProposalState(initHeader)
|
||||
app.setProcessProposalState(initHeader)
|
||||
|
||||
// Store the consensus params in the BaseApp's paramstore. Note, this must be
|
||||
// done after the deliver state and context have been set as it's persisted
|
||||
@ -182,8 +187,6 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
|
||||
WithHeaderHash(req.Hash).
|
||||
WithConsensusParams(app.GetConsensusParams(app.deliverState.ctx))
|
||||
|
||||
// we also set block gas meter to checkState in case the application needs to
|
||||
// verify gas consumption during (Re)CheckTx
|
||||
if app.checkState != nil {
|
||||
app.checkState.ctx = app.checkState.ctx.
|
||||
WithBlockGasMeter(gasMeter).
|
||||
@ -238,19 +241,52 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
|
||||
// work in a block before proposing it.
|
||||
//
|
||||
// Transactions can be modified, removed, or added by the application. Since the
|
||||
// application maintains it's own local mempool, it will ignore the transactions
|
||||
// application maintains its own local mempool, it will ignore the transactions
|
||||
// provided to it in RequestPrepareProposal. Instead, it will determine which
|
||||
// transactions to return based on the mempool's semantics and the MaxTxBytes
|
||||
// provided by the client's request.
|
||||
//
|
||||
// Note, there is no need to execute the transactions for validity as they have
|
||||
// already passed CheckTx.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-060-abci-1.0.md
|
||||
// Ref: https://github.com/tendermint/tendermint/blob/main/spec/abci/abci%2B%2B_basic_concepts.md
|
||||
func (app *BaseApp) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
|
||||
// TODO: Implement.
|
||||
return abci.ResponsePrepareProposal{Txs: req.Txs}
|
||||
var (
|
||||
txsBytes [][]byte
|
||||
byteCount int64
|
||||
)
|
||||
|
||||
ctx := app.getContextForTx(runTxPrepareProposal, []byte{})
|
||||
iterator := app.mempool.Select(ctx, req.Txs)
|
||||
|
||||
for iterator != nil {
|
||||
memTx := iterator.Tx()
|
||||
|
||||
bz, err := app.txEncoder(memTx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
txSize := int64(len(bz))
|
||||
|
||||
// NOTE: runTx was already run in CheckTx which calls mempool.Insert so ideally everything in the pool
|
||||
// should be valid. But some mempool implementations may insert invalid txs, so we check again.
|
||||
_, _, _, _, err = app.runTx(runTxPrepareProposal, bz)
|
||||
if err != nil {
|
||||
err := app.mempool.Remove(memTx)
|
||||
if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
iterator = iterator.Next()
|
||||
continue
|
||||
} else if byteCount += txSize; byteCount <= req.MaxTxBytes {
|
||||
txsBytes = append(txsBytes, bz)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
iterator = iterator.Next()
|
||||
}
|
||||
|
||||
return abci.ResponsePrepareProposal{Txs: txsBytes}
|
||||
}
|
||||
|
||||
// ProcessProposal implements the ProcessProposal ABCI method and returns a
|
||||
@ -266,8 +302,19 @@ func (app *BaseApp) PrepareProposal(req abci.RequestPrepareProposal) abci.Respon
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-060-abci-1.0.md
|
||||
// Ref: https://github.com/tendermint/tendermint/blob/main/spec/abci/abci%2B%2B_basic_concepts.md
|
||||
func (app *BaseApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
// TODO: Implement.
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
if app.processProposal == nil {
|
||||
panic("app.ProcessProposal is not set")
|
||||
}
|
||||
|
||||
ctx := app.processProposalState.ctx.
|
||||
WithVoteInfos(app.voteInfos).
|
||||
WithBlockHeight(req.Height).
|
||||
WithBlockTime(req.Time).
|
||||
WithHeaderHash(req.Hash).
|
||||
WithProposer(req.ProposerAddress).
|
||||
WithConsensusParams(app.GetConsensusParams(app.processProposalState.ctx))
|
||||
|
||||
return app.processProposal(ctx, req)
|
||||
}
|
||||
|
||||
// CheckTx implements the ABCI interface and executes a tx in CheckTx mode. In
|
||||
@ -367,6 +414,8 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
||||
// NOTE: This is safe because Tendermint holds a lock on the mempool for
|
||||
// Commit. Use the header from this latest block.
|
||||
app.setCheckState(header)
|
||||
app.setPrepareProposalState(header)
|
||||
app.setProcessProposalState(header)
|
||||
|
||||
// empty/reset the deliver state
|
||||
app.deliverState = nil
|
||||
|
||||
196
baseapp/abci_v1_test.go
Normal file
196
baseapp/abci_v1_test.go
Normal file
@ -0,0 +1,196 @@
|
||||
package baseapp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
"cosmossdk.io/depinject"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
)
|
||||
|
||||
type NoopCounterServerImpl struct{}
|
||||
|
||||
func (m NoopCounterServerImpl) IncrementCounter(
|
||||
_ context.Context,
|
||||
_ *baseapptestutil.MsgCounter,
|
||||
) (*baseapptestutil.MsgCreateCounterResponse, error) {
|
||||
return &baseapptestutil.MsgCreateCounterResponse{}, nil
|
||||
}
|
||||
|
||||
type ABCIv1TestSuite struct {
|
||||
suite.Suite
|
||||
baseApp *baseapp.BaseApp
|
||||
mempool mempool.Mempool
|
||||
txConfig client.TxConfig
|
||||
cdc codec.ProtoCodecMarshaler
|
||||
}
|
||||
|
||||
func TestABCIv1TestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ABCIv1TestSuite))
|
||||
}
|
||||
|
||||
func (s *ABCIv1TestSuite) SetupTest() {
|
||||
t := s.T()
|
||||
anteKey := []byte("ante-key")
|
||||
pool := mempool.NewNonceMempool()
|
||||
anteOpt := func(bapp *baseapp.BaseApp) {
|
||||
bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey))
|
||||
}
|
||||
|
||||
var (
|
||||
appBuilder *runtime.AppBuilder
|
||||
cdc codec.ProtoCodecMarshaler
|
||||
registry codectypes.InterfaceRegistry
|
||||
)
|
||||
err := depinject.Inject(makeMinimalConfig(), &appBuilder, &cdc, ®istry)
|
||||
require.NoError(t, err)
|
||||
|
||||
app := setupBaseApp(t, anteOpt, baseapp.SetMempool(pool))
|
||||
baseapptestutil.RegisterInterfaces(registry)
|
||||
app.SetMsgServiceRouter(baseapp.NewMsgServiceRouter())
|
||||
app.SetInterfaceRegistry(registry)
|
||||
|
||||
baseapptestutil.RegisterKeyValueServer(app.MsgServiceRouter(), MsgKeyValueImpl{})
|
||||
baseapptestutil.RegisterCounterServer(app.MsgServiceRouter(), NoopCounterServerImpl{})
|
||||
header := tmproto.Header{Height: app.LastBlockHeight() + 1}
|
||||
|
||||
app.InitChain(abci.RequestInitChain{
|
||||
ConsensusParams: &tmproto.ConsensusParams{},
|
||||
})
|
||||
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
// patch in TxConfig insted of using an output from x/auth/tx
|
||||
txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
|
||||
|
||||
app.SetTxDecoder(txConfig.TxDecoder())
|
||||
app.SetTxEncoder(txConfig.TxEncoder())
|
||||
|
||||
s.baseApp = app
|
||||
s.mempool = pool
|
||||
s.txConfig = txConfig
|
||||
s.cdc = cdc
|
||||
}
|
||||
|
||||
func (s *ABCIv1TestSuite) TestABCIv1_HappyPath() {
|
||||
txConfig := s.txConfig
|
||||
t := s.T()
|
||||
|
||||
tx := newTxCounter(txConfig, 0, 1)
|
||||
txBytes, err := txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
reqCheckTx := abci.RequestCheckTx{
|
||||
Tx: txBytes,
|
||||
Type: abci.CheckTxType_New,
|
||||
}
|
||||
s.baseApp.CheckTx(reqCheckTx)
|
||||
|
||||
tx2 := newTxCounter(txConfig, 1, 1)
|
||||
|
||||
tx2Bytes, err := txConfig.TxEncoder()(tx2)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.mempool.Insert(sdk.Context{}, tx2)
|
||||
require.NoError(t, err)
|
||||
reqPreparePropossal := abci.RequestPrepareProposal{
|
||||
MaxTxBytes: 1000,
|
||||
}
|
||||
resPreparePropossal := s.baseApp.PrepareProposal(reqPreparePropossal)
|
||||
|
||||
require.Equal(t, 2, len(resPreparePropossal.Txs))
|
||||
|
||||
var reqProposalTxBytes [2][]byte
|
||||
reqProposalTxBytes[0] = txBytes
|
||||
reqProposalTxBytes[1] = tx2Bytes
|
||||
reqProcessProposal := abci.RequestProcessProposal{
|
||||
Txs: reqProposalTxBytes[:],
|
||||
}
|
||||
|
||||
s.baseApp.SetProcessProposal(nil)
|
||||
require.Panics(t, func() { s.baseApp.ProcessProposal(reqProcessProposal) })
|
||||
s.baseApp.SetProcessProposal(s.baseApp.DefaultProcessProposal())
|
||||
|
||||
resProcessProposal := s.baseApp.ProcessProposal(reqProcessProposal)
|
||||
require.Equal(t, abci.ResponseProcessProposal_ACCEPT, resProcessProposal.Status)
|
||||
|
||||
res := s.baseApp.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.Equal(t, 1, s.mempool.CountTx())
|
||||
|
||||
require.NotEmpty(t, res.Events)
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
}
|
||||
|
||||
func (s *ABCIv1TestSuite) TestABCIv1_PrepareProposal_ReachedMaxBytes() {
|
||||
txConfig := s.txConfig
|
||||
t := s.T()
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
tx2 := newTxCounter(txConfig, int64(i), int64(i))
|
||||
err := s.mempool.Insert(sdk.Context{}, tx2)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
reqPreparePropossal := abci.RequestPrepareProposal{
|
||||
MaxTxBytes: 1500,
|
||||
}
|
||||
resPreparePropossal := s.baseApp.PrepareProposal(reqPreparePropossal)
|
||||
|
||||
require.Equal(t, 10, len(resPreparePropossal.Txs))
|
||||
}
|
||||
|
||||
func (s *ABCIv1TestSuite) TestABCIv1_PrepareProposal_BadEncoding() {
|
||||
txConfig := authtx.NewTxConfig(s.cdc, authtx.DefaultSignModes)
|
||||
|
||||
t := s.T()
|
||||
|
||||
tx := newTxCounter(txConfig, 0, 0)
|
||||
err := s.mempool.Insert(sdk.Context{}, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
reqPrepareProposal := abci.RequestPrepareProposal{
|
||||
MaxTxBytes: 1000,
|
||||
}
|
||||
resPrepareProposal := s.baseApp.PrepareProposal(reqPrepareProposal)
|
||||
|
||||
require.Equal(t, 1, len(resPrepareProposal.Txs))
|
||||
}
|
||||
|
||||
func (s *ABCIv1TestSuite) TestABCIv1_PrepareProposal_Failures() {
|
||||
tx := newTxCounter(s.txConfig, 0, 0)
|
||||
txBytes, err := s.txConfig.TxEncoder()(tx)
|
||||
s.NoError(err)
|
||||
|
||||
reqCheckTx := abci.RequestCheckTx{
|
||||
Tx: txBytes,
|
||||
Type: abci.CheckTxType_New,
|
||||
}
|
||||
checkTxRes := s.baseApp.CheckTx(reqCheckTx)
|
||||
s.True(checkTxRes.IsOK())
|
||||
|
||||
failTx := newTxCounter(s.txConfig, 1, 1)
|
||||
failTx = setFailOnAnte(s.txConfig, failTx, true)
|
||||
err = s.mempool.Insert(sdk.Context{}, failTx)
|
||||
s.NoError(err)
|
||||
s.Equal(2, s.mempool.CountTx())
|
||||
|
||||
req := abci.RequestPrepareProposal{
|
||||
MaxTxBytes: 1000,
|
||||
}
|
||||
res := s.baseApp.PrepareProposal(req)
|
||||
s.Equal(1, len(res.Txs))
|
||||
}
|
||||
@ -1,10 +1,12 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@ -20,7 +22,6 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,6 +29,8 @@ const (
|
||||
runTxModeReCheck // Recheck a (pending) transaction after a commit
|
||||
runTxModeSimulate // Simulate a transaction
|
||||
runTxModeDeliver // Deliver a transaction
|
||||
runTxPrepareProposal
|
||||
runTxProcessProposal
|
||||
)
|
||||
|
||||
var _ abci.Application = (*BaseApp)(nil)
|
||||
@ -56,16 +59,18 @@ type BaseApp struct { //nolint: maligned
|
||||
msgServiceRouter *MsgServiceRouter // router for redirecting Msg service messages
|
||||
interfaceRegistry codectypes.InterfaceRegistry
|
||||
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.AnteHandler // 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
|
||||
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||
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.AnteHandler // 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
|
||||
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||
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
|
||||
@ -74,8 +79,10 @@ type BaseApp struct { //nolint: maligned
|
||||
//
|
||||
// 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
|
||||
checkState *state // for CheckTx
|
||||
deliverState *state // for DeliverTx
|
||||
processProposalState *state // for ProcessProposal
|
||||
prepareProposalState *state // for PrepareProposal
|
||||
|
||||
// an inter-block write-through cache provided to the context during deliverState
|
||||
interBlockCache sdk.MultiStorePersistentCache
|
||||
@ -161,6 +168,14 @@ func NewBaseApp(
|
||||
option(app)
|
||||
}
|
||||
|
||||
if app.mempool == nil {
|
||||
app.SetMempool(mempool.NewNonceMempool())
|
||||
}
|
||||
|
||||
if app.processProposal == nil {
|
||||
app.SetProcessProposal(app.DefaultProcessProposal())
|
||||
}
|
||||
|
||||
if app.interBlockCache != nil {
|
||||
app.cms.SetInterBlockCache(app.interBlockCache)
|
||||
}
|
||||
@ -328,8 +343,14 @@ func (app *BaseApp) Init() error {
|
||||
panic("cannot call initFromMainStore: baseapp already sealed")
|
||||
}
|
||||
|
||||
emptyHeader := tmproto.Header{}
|
||||
|
||||
// needed for the export command which inits from store but never calls initchain
|
||||
app.setCheckState(tmproto.Header{})
|
||||
app.setCheckState(emptyHeader)
|
||||
|
||||
// needed for ABCI Replay Blocks mode which calls Prepare/Process proposal (InitChain is not called)
|
||||
app.setPrepareProposalState(emptyHeader)
|
||||
app.setProcessProposalState(emptyHeader)
|
||||
app.Seal()
|
||||
|
||||
rms, ok := app.cms.(*rootmulti.Store)
|
||||
@ -401,6 +422,28 @@ func (app *BaseApp) setDeliverState(header tmproto.Header) {
|
||||
}
|
||||
}
|
||||
|
||||
// setPrepareProposalState sets the BaseApp's prepareProposalState with a
|
||||
// branched multi-store (i.e. a CacheMultiStore) and a new Context with the
|
||||
// same multi-store branch, and provided header. It is set on InitChain and Commit.
|
||||
func (app *BaseApp) setPrepareProposalState(header tmproto.Header) {
|
||||
ms := app.cms.CacheMultiStore()
|
||||
app.prepareProposalState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, false, app.logger),
|
||||
}
|
||||
}
|
||||
|
||||
// setProcessProposalState sets the BaseApp's processProposalState with a
|
||||
// branched multi-store (i.e. a CacheMultiStore) and a new Context with the
|
||||
// same multi-store branch, and provided header. It is set on InitChain and Commit.
|
||||
func (app *BaseApp) setProcessProposalState(header tmproto.Header) {
|
||||
ms := app.cms.CacheMultiStore()
|
||||
app.processProposalState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, false, app.logger),
|
||||
}
|
||||
}
|
||||
|
||||
// GetConsensusParams returns the current consensus parameters from the BaseApp's
|
||||
// ParamStore. If the BaseApp has no ParamStore defined, nil is returned.
|
||||
func (app *BaseApp) GetConsensusParams(ctx sdk.Context) *tmproto.ConsensusParams {
|
||||
@ -507,16 +550,25 @@ func validateBasicTxMsgs(msgs []sdk.Msg) error {
|
||||
// Returns the application's deliverState if app is in runTxModeDeliver,
|
||||
// otherwise it returns the application's checkstate.
|
||||
func (app *BaseApp) getState(mode runTxMode) *state {
|
||||
if mode == runTxModeDeliver {
|
||||
switch mode {
|
||||
case runTxModeDeliver:
|
||||
return app.deliverState
|
||||
case runTxPrepareProposal:
|
||||
return app.prepareProposalState
|
||||
case runTxProcessProposal:
|
||||
return app.processProposalState
|
||||
default:
|
||||
return app.checkState
|
||||
}
|
||||
|
||||
return app.checkState
|
||||
}
|
||||
|
||||
// retrieve the context for the tx w/ txBytes and other memoized values.
|
||||
func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context {
|
||||
ctx := app.getState(mode).ctx.
|
||||
modeState := app.getState(mode)
|
||||
if modeState == nil {
|
||||
panic(fmt.Sprintf("state is nil for mode %v", mode))
|
||||
}
|
||||
ctx := modeState.ctx.
|
||||
WithTxBytes(txBytes).
|
||||
WithVoteInfos(app.voteInfos)
|
||||
|
||||
@ -654,12 +706,17 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
|
||||
anteEvents = events.ToABCIEvents()
|
||||
}
|
||||
|
||||
// TODO remove nil check when implemented
|
||||
if mode == runTxModeCheck && app.mempool != nil {
|
||||
err = app.mempool.Insert(ctx, tx.(mempool.Tx))
|
||||
if mode == runTxModeCheck {
|
||||
err = app.mempool.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return gInfo, nil, anteEvents, priority, err
|
||||
}
|
||||
} else if mode == runTxModeDeliver {
|
||||
err = app.mempool.Remove(tx)
|
||||
if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {
|
||||
return gInfo, nil, anteEvents, priority,
|
||||
fmt.Errorf("failed to remove tx from mempool: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new Context based off of the existing Context with a MultiStore branch
|
||||
@ -713,8 +770,8 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s
|
||||
|
||||
// NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter.
|
||||
for i, msg := range msgs {
|
||||
// skip actual execution for (Re)CheckTx mode
|
||||
if mode == runTxModeCheck || mode == runTxModeReCheck {
|
||||
|
||||
if mode != runTxModeDeliver && mode != runTxModeSimulate {
|
||||
break
|
||||
}
|
||||
|
||||
@ -790,3 +847,27 @@ func createEvents(msg sdk.Msg) sdk.Events {
|
||||
|
||||
return sdk.Events{msgEvent}
|
||||
}
|
||||
|
||||
// DefaultProcessProposal 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.
|
||||
func (app *BaseApp) DefaultProcessProposal() sdk.ProcessProposalHandler {
|
||||
return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
for _, txBytes := range req.Txs {
|
||||
_, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
|
||||
_, _, _, _, err = app.runTx(runTxProcessProposal, txBytes)
|
||||
if err != nil {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
}
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,8 +17,9 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"cosmossdk.io/depinject"
|
||||
"github.com/cosmos/gogoproto/jsonpb"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -26,6 +27,10 @@ import (
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/gogoproto/jsonpb"
|
||||
|
||||
"cosmossdk.io/depinject"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@ -70,6 +75,7 @@ func defaultLogger() log.Logger {
|
||||
func setupBaseApp(t *testing.T, options ...func(*baseapp.BaseApp)) *baseapp.BaseApp {
|
||||
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
|
||||
baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
|
||||
|
||||
txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
|
||||
|
||||
logger := defaultLogger()
|
||||
@ -129,6 +135,7 @@ func setupBaseAppWithSnapshots(t *testing.T, config *setupConfig) (*baseapp.Base
|
||||
|
||||
builder := txConfig.NewTxBuilder()
|
||||
builder.SetMsgs(msgs...)
|
||||
setTxSignature(builder, 0)
|
||||
|
||||
txBytes, err := txConfig.TxEncoder()(builder.GetTx())
|
||||
require.NoError(t, err)
|
||||
@ -1061,9 +1068,9 @@ func TestCheckTx(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
r := app.CheckTx(abci.RequestCheckTx{Tx: txBytes})
|
||||
require.True(t, r.IsOK(), fmt.Sprintf("%v", r))
|
||||
require.Equal(t, testTxPriority, r.Priority)
|
||||
require.Empty(t, r.GetEvents())
|
||||
require.True(t, r.IsOK(), fmt.Sprintf("%v", r))
|
||||
}
|
||||
|
||||
checkStateStore := getCheckStateCtx(app.BaseApp).KVStore(capKey1)
|
||||
@ -1208,6 +1215,7 @@ func TestMultiMsgDeliverTx(t *testing.T) {
|
||||
|
||||
builder.SetMsgs(msgs...)
|
||||
builder.SetMemo(tx.GetMemo())
|
||||
setTxSignature(builder, 0)
|
||||
|
||||
txBytes, err = txConfig.TxEncoder()(builder.GetTx())
|
||||
require.NoError(t, err)
|
||||
@ -1389,6 +1397,7 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
{
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
txBuilder.SetMsgs(&baseapptestutil.MsgCounter2{})
|
||||
setTxSignature(txBuilder, 0)
|
||||
unknownRouteTx := txBuilder.GetTx()
|
||||
|
||||
_, result, err := app.SimDeliver(txConfig.TxEncoder(), unknownRouteTx)
|
||||
@ -1401,6 +1410,7 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
|
||||
txBuilder = txConfig.NewTxBuilder()
|
||||
txBuilder.SetMsgs(&baseapptestutil.MsgCounter{}, &baseapptestutil.MsgCounter2{})
|
||||
setTxSignature(txBuilder, 0)
|
||||
unknownRouteTx = txBuilder.GetTx()
|
||||
_, result, err = app.SimDeliver(txConfig.TxEncoder(), unknownRouteTx)
|
||||
require.Error(t, err)
|
||||
@ -1989,6 +1999,7 @@ func newTxCounter(cfg client.TxConfig, counter int64, msgCounters ...int64) sign
|
||||
builder := cfg.NewTxBuilder()
|
||||
builder.SetMsgs(msgs...)
|
||||
builder.SetMemo("counter=" + strconv.FormatInt(counter, 10) + "&failOnAnte=false")
|
||||
setTxSignature(builder, uint64(counter))
|
||||
|
||||
return builder.GetTx()
|
||||
}
|
||||
@ -2006,6 +2017,7 @@ func setFailOnAnte(cfg client.TxConfig, tx signing.Tx, failOnAnte bool) signing.
|
||||
vals.Set("failOnAnte", strconv.FormatBool(failOnAnte))
|
||||
memo = vals.Encode()
|
||||
builder.SetMemo(memo)
|
||||
setTxSignature(builder, 1)
|
||||
|
||||
return builder.GetTx()
|
||||
}
|
||||
@ -2201,3 +2213,15 @@ func (ps paramStore) Get(ctx sdk.Context) (*tmproto.ConsensusParams, error) {
|
||||
|
||||
return ¶ms, nil
|
||||
}
|
||||
|
||||
func setTxSignature(builder client.TxBuilder, nonce uint64) {
|
||||
privKey := secp256k1.GenPrivKeyFromSecret([]byte("test"))
|
||||
pubKey := privKey.PubKey()
|
||||
err := builder.SetSignatures(
|
||||
signingtypes.SignatureV2{
|
||||
PubKey: pubKey, Sequence: nonce, Data: &signingtypes.SingleSignatureData{},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
// File for storing in-package BaseApp optional functions,
|
||||
@ -80,6 +81,16 @@ func SetSnapshot(snapshotStore *snapshots.Store, opts snapshottypes.SnapshotOpti
|
||||
return func(app *BaseApp) { app.SetSnapshot(snapshotStore, opts) }
|
||||
}
|
||||
|
||||
// SetMempool sets the mempool on BaseApp.
|
||||
func SetMempool(mempool mempool.Mempool) func(*BaseApp) {
|
||||
return func(app *BaseApp) { app.SetMempool(mempool) }
|
||||
}
|
||||
|
||||
// SetProcessProposal sets the ProcessProposal handler.
|
||||
func SetProcessProposal(proposalHandler sdk.ProcessProposalHandler) func(*BaseApp) {
|
||||
return func(app *BaseApp) { app.SetProcessProposal(proposalHandler) }
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetName(name string) {
|
||||
if app.sealed {
|
||||
panic("SetName() on sealed BaseApp")
|
||||
@ -241,9 +252,23 @@ func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
|
||||
app.txDecoder = txDecoder
|
||||
}
|
||||
|
||||
// SetTxEncoder sets the TxEncoder if it wasn't provided in the BaseApp constructor.
|
||||
func (app *BaseApp) SetTxEncoder(txEncoder sdk.TxEncoder) {
|
||||
app.txEncoder = txEncoder
|
||||
}
|
||||
|
||||
// SetQueryMultiStore set a alternative MultiStore implementation to support grpc query service.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/13317
|
||||
func (app *BaseApp) SetQueryMultiStore(ms sdk.MultiStore) {
|
||||
app.qms = ms
|
||||
}
|
||||
|
||||
// SetMempool sets the mempool for the BaseApp and is required for the app to start up.
|
||||
func (app *BaseApp) SetMempool(mempool mempool.Mempool) {
|
||||
app.mempool = mempool
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetProcessProposal(handler sdk.ProcessProposalHandler) {
|
||||
app.processProposal = handler
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package testutil
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/msgservice"
|
||||
@ -17,6 +18,8 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Counter_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Counter2_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_KeyValue_serviceDesc)
|
||||
|
||||
codec.RegisterInterfaces(registry)
|
||||
}
|
||||
|
||||
var _ sdk.Msg = &MsgCounter{}
|
||||
|
||||
@ -18,12 +18,14 @@ import (
|
||||
txmodulev1 "cosmossdk.io/api/cosmos/tx/module/v1"
|
||||
"cosmossdk.io/core/appconfig"
|
||||
"cosmossdk.io/depinject"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
_ "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/module"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
@ -148,14 +150,17 @@ func makeTestConfig() depinject.Config {
|
||||
}
|
||||
|
||||
func makeMinimalConfig() depinject.Config {
|
||||
return appconfig.Compose(&appv1alpha1.Config{
|
||||
Modules: []*appv1alpha1.ModuleConfig{
|
||||
{
|
||||
Name: "runtime",
|
||||
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
|
||||
AppName: "BaseAppApp",
|
||||
}),
|
||||
var mempoolOpt runtime.BaseAppOption = baseapp.SetMempool(mempool.NewNonceMempool())
|
||||
return depinject.Configs(
|
||||
depinject.Supply(mempoolOpt),
|
||||
appconfig.Compose(&appv1alpha1.Config{
|
||||
Modules: []*appv1alpha1.ModuleConfig{
|
||||
{
|
||||
Name: "runtime",
|
||||
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
|
||||
AppName: "BaseAppApp",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -53,7 +57,11 @@ func TestDeliverTx(t *testing.T) {
|
||||
|
||||
key := "my-special-key"
|
||||
value := "top-secret-data!!"
|
||||
tx := NewTx(key, value)
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomAccounts := simtypes.RandomAccounts(r, 1)
|
||||
|
||||
tx := NewTx(key, value, randomAccounts[0].Address)
|
||||
txBytes := tx.GetSignBytes()
|
||||
|
||||
header := tmproto.Header{
|
||||
|
||||
@ -4,15 +4,66 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// An sdk.Tx which is its own sdk.Msg.
|
||||
type kvstoreTx struct {
|
||||
key []byte
|
||||
value []byte
|
||||
bytes []byte
|
||||
key []byte
|
||||
value []byte
|
||||
bytes []byte
|
||||
address sdk.AccAddress
|
||||
}
|
||||
type testPubKey struct {
|
||||
address sdk.AccAddress
|
||||
}
|
||||
|
||||
func (t testPubKey) Reset() { panic("implement me") }
|
||||
|
||||
func (t testPubKey) String() string { panic("implement me") }
|
||||
|
||||
func (t testPubKey) ProtoMessage() { panic("implement me") }
|
||||
|
||||
func (t testPubKey) Address() cryptotypes.Address { return t.address.Bytes() }
|
||||
|
||||
func (t testPubKey) Bytes() []byte { panic("implement me") }
|
||||
|
||||
func (t testPubKey) VerifySignature(msg []byte, sig []byte) bool { panic("implement me") }
|
||||
|
||||
func (t testPubKey) Equals(key cryptotypes.PubKey) bool { panic("implement me") }
|
||||
|
||||
func (t testPubKey) Type() string { panic("implement me") }
|
||||
|
||||
func (msg *kvstoreTx) GetSignaturesV2() (res []txsigning.SignatureV2, err error) {
|
||||
res = append(res, txsigning.SignatureV2{
|
||||
PubKey: testPubKey{address: msg.address},
|
||||
Data: nil,
|
||||
Sequence: 1,
|
||||
})
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (msg *kvstoreTx) VerifySignature(msgByte []byte, sig []byte) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (msg *kvstoreTx) Address() cryptotypes.Address {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (msg *kvstoreTx) Bytes() []byte {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (msg *kvstoreTx) Equals(key cryptotypes.PubKey) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// dummy implementation of proto.Message
|
||||
@ -21,16 +72,19 @@ func (msg *kvstoreTx) String() string { return "TODO" }
|
||||
func (msg *kvstoreTx) ProtoMessage() {}
|
||||
|
||||
var (
|
||||
_ sdk.Tx = &kvstoreTx{}
|
||||
_ sdk.Msg = &kvstoreTx{}
|
||||
_ sdk.Tx = &kvstoreTx{}
|
||||
_ sdk.Msg = &kvstoreTx{}
|
||||
_ signing.SigVerifiableTx = &kvstoreTx{}
|
||||
_ cryptotypes.PubKey = &kvstoreTx{}
|
||||
)
|
||||
|
||||
func NewTx(key, value string) *kvstoreTx {
|
||||
func NewTx(key, value string, accAddress sdk.AccAddress) *kvstoreTx {
|
||||
bytes := fmt.Sprintf("%s=%s", key, value)
|
||||
return &kvstoreTx{
|
||||
key: []byte(key),
|
||||
value: []byte(value),
|
||||
bytes: []byte(bytes),
|
||||
key: []byte(key),
|
||||
value: []byte(value),
|
||||
bytes: []byte(bytes),
|
||||
address: accAddress,
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +109,8 @@ func (tx *kvstoreTx) GetSigners() []sdk.AccAddress {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *kvstoreTx) GetPubKeys() ([]cryptotypes.PubKey, error) { panic("GetPubKeys not implemented") }
|
||||
|
||||
// takes raw transaction bytes and decodes them into an sdk.Tx. An sdk.Tx has
|
||||
// all the signatures and can be used to authenticate.
|
||||
func decodeTx(txBytes []byte) (sdk.Tx, error) {
|
||||
@ -63,10 +119,10 @@ func decodeTx(txBytes []byte) (sdk.Tx, error) {
|
||||
split := bytes.Split(txBytes, []byte("="))
|
||||
if len(split) == 1 { //nolint:gocritic
|
||||
k := split[0]
|
||||
tx = &kvstoreTx{k, k, txBytes}
|
||||
tx = &kvstoreTx{k, k, txBytes, nil}
|
||||
} else if len(split) == 2 {
|
||||
k, v := split[0], split[1]
|
||||
tx = &kvstoreTx{k, v, txBytes}
|
||||
tx = &kvstoreTx{k, v, txBytes, nil}
|
||||
} else {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "too many '='")
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ type (
|
||||
// RegisterNodeService registers the node gRPC Query service.
|
||||
RegisterNodeService(client.Context)
|
||||
|
||||
// Return the multistore instance
|
||||
// CommitMultiStore return the multistore instance
|
||||
CommitMultiStore() sdk.CommitMultiStore
|
||||
}
|
||||
|
||||
|
||||
@ -231,6 +231,7 @@ func NewSimApp(
|
||||
bApp.SetCommitMultiStoreTracer(traceStore)
|
||||
bApp.SetVersion(version.Version)
|
||||
bApp.SetInterfaceRegistry(interfaceRegistry)
|
||||
bApp.SetTxEncoder(txConfig.TxEncoder())
|
||||
|
||||
keys := sdk.NewKVStoreKeys(
|
||||
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey,
|
||||
|
||||
@ -19,3 +19,6 @@ type EndBlocker func(ctx Context, req abci.RequestEndBlock) abci.ResponseEndBloc
|
||||
|
||||
// PeerFilter responds to p2p filtering queries from Tendermint
|
||||
type PeerFilter func(info string) abci.ResponseQuery
|
||||
|
||||
// ProcessProposalHandler defines a function type alias for processing a proposer
|
||||
type ProcessProposalHandler func(ctx Context, proposal abci.RequestProcessProposal) abci.ResponseProcessProposal
|
||||
|
||||
@ -3,39 +3,34 @@ package mempool
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Tx defines an app-side mempool transaction interface that is as
|
||||
// minimal as possible, only requiring applications to define the size of the
|
||||
// transaction to be used when inserting, selecting, and deleting the transaction.
|
||||
// Interface type casting can be used in the actual app-side mempool implementation.
|
||||
type Tx interface {
|
||||
types.Tx
|
||||
|
||||
// Size returns the size of the transaction in bytes.
|
||||
Size() int64
|
||||
}
|
||||
|
||||
type Mempool interface {
|
||||
// Insert attempts to insert a Tx into the app-side mempool returning
|
||||
// an error upon failure.
|
||||
Insert(types.Context, Tx) error
|
||||
Insert(sdk.Context, sdk.Tx) error
|
||||
|
||||
// Select returns the next set of available transactions from the app-side
|
||||
// mempool, up to maxBytes or until the mempool is empty. The application can
|
||||
// decide to return transactions from its own mempool, from the incoming
|
||||
// txs, or some combination of both.
|
||||
Select(txs [][]byte, maxBytes int64) ([]Tx, error)
|
||||
// Select returns an Iterator over the app-side mempool. If txs are specified, then they shall be incorporated
|
||||
// into the Iterator. The Iterator must be closed by the caller.
|
||||
Select(sdk.Context, [][]byte) Iterator
|
||||
|
||||
// CountTx returns the number of transactions currently in the mempool.
|
||||
CountTx() int
|
||||
|
||||
// Remove attempts to remove a transaction from the mempool, returning an error
|
||||
// upon failure.
|
||||
Remove(Tx) error
|
||||
Remove(sdk.Tx) error
|
||||
}
|
||||
|
||||
// Iterator defines an app-side mempool iterator interface that is as minimal as possible. The order of iteration
|
||||
// is determined by the app-side mempool implementation.
|
||||
type Iterator interface {
|
||||
// Next returns the next transaction from the mempool. If there are no more transactions, it returns nil.
|
||||
Next() Iterator
|
||||
|
||||
// Tx returns the transaction at the current position of the iterator.
|
||||
Tx() sdk.Tx
|
||||
}
|
||||
|
||||
var ErrTxNotFound = errors.New("tx not found in mempool")
|
||||
|
||||
type Factory func() Mempool
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -9,6 +9,11 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Mempool = (*nonceMempool)(nil)
|
||||
_ Iterator = (*nonceMempoolIterator)(nil)
|
||||
)
|
||||
|
||||
// nonceMempool is a mempool that keeps transactions sorted by nonce. Transactions with the lowest nonce globally
|
||||
// are prioritized. Transactions with the same nonce are prioritized by sender address. Fee/gas based
|
||||
// prioritization is not supported.
|
||||
@ -16,6 +21,24 @@ type nonceMempool struct {
|
||||
txQueue *huandu.SkipList
|
||||
}
|
||||
|
||||
type nonceMempoolIterator struct {
|
||||
currentTx *huandu.Element
|
||||
}
|
||||
|
||||
func (i nonceMempoolIterator) Next() Iterator {
|
||||
if i.currentTx == nil {
|
||||
return nil
|
||||
} else if n := i.currentTx.Next(); n != nil {
|
||||
return nonceMempoolIterator{currentTx: n}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i nonceMempoolIterator) Tx() sdk.Tx {
|
||||
return i.currentTx.Value.(sdk.Tx)
|
||||
}
|
||||
|
||||
type txKey struct {
|
||||
nonce uint64
|
||||
sender string
|
||||
@ -34,6 +57,7 @@ func txKeyLessNonce(a, b any) int {
|
||||
return huandu.String.Compare(keyB.sender, keyA.sender)
|
||||
}
|
||||
|
||||
// NewNonceMempool creates a new mempool that prioritizes transactions by nonce, the lowest first.
|
||||
func NewNonceMempool() Mempool {
|
||||
sp := &nonceMempool{
|
||||
txQueue: huandu.New(huandu.LessThanFunc(txKeyLessNonce)),
|
||||
@ -44,7 +68,7 @@ func NewNonceMempool() Mempool {
|
||||
|
||||
// Insert adds a tx to the mempool. It returns an error if the tx does not have at least one signer.
|
||||
// priority is ignored.
|
||||
func (sp nonceMempool) Insert(_ sdk.Context, tx Tx) error {
|
||||
func (sp nonceMempool) Insert(_ sdk.Context, tx sdk.Tx) error {
|
||||
sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -61,26 +85,15 @@ func (sp nonceMempool) Insert(_ sdk.Context, tx Tx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Select returns txs from the mempool with the lowest nonce globally first. A sender's txs will always be returned
|
||||
// in nonce order.
|
||||
func (sp nonceMempool) Select(_ [][]byte, maxBytes int64) ([]Tx, error) {
|
||||
var (
|
||||
txBytes int64
|
||||
selectedTxs []Tx
|
||||
)
|
||||
|
||||
// Select returns an iterator ordering transactions the mempool with the lowest nonce globally first. A sender's txs
|
||||
// will always be returned in nonce order.
|
||||
func (sp nonceMempool) Select(_ sdk.Context, _ [][]byte) Iterator {
|
||||
currentTx := sp.txQueue.Front()
|
||||
for currentTx != nil {
|
||||
mempoolTx := currentTx.Value.(Tx)
|
||||
|
||||
if txBytes += mempoolTx.Size(); txBytes <= maxBytes {
|
||||
selectedTxs = append(selectedTxs, mempoolTx)
|
||||
} else {
|
||||
return selectedTxs, nil
|
||||
}
|
||||
currentTx = currentTx.Next()
|
||||
if currentTx == nil {
|
||||
return nil
|
||||
}
|
||||
return selectedTxs, nil
|
||||
|
||||
return &nonceMempoolIterator{currentTx: currentTx}
|
||||
}
|
||||
|
||||
// CountTx returns the number of txs in the mempool.
|
||||
@ -90,7 +103,7 @@ func (sp nonceMempool) CountTx() int {
|
||||
|
||||
// Remove removes a tx from the mempool. It returns an error if the tx does not have at least one signer or the tx
|
||||
// was not found in the pool.
|
||||
func (sp nonceMempool) Remove(tx Tx) error {
|
||||
func (sp nonceMempool) Remove(tx sdk.Tx) error {
|
||||
sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -193,8 +193,8 @@ func (s *MempoolTestSuite) TestTxOrder() {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
orderedTxs, err := pool.Select(nil, 1000)
|
||||
require.NoError(t, err)
|
||||
itr := pool.Select(ctx, nil)
|
||||
orderedTxs := fetchTxs(itr, 1000)
|
||||
var txOrder []int
|
||||
for _, tx := range orderedTxs {
|
||||
txOrder = append(txOrder, tx.(testTx).id)
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
@ -32,8 +31,6 @@ type wrapper struct {
|
||||
authInfoBz []byte
|
||||
|
||||
txBodyHasUnknownNonCriticals bool
|
||||
|
||||
txSize int64
|
||||
}
|
||||
|
||||
var (
|
||||
@ -43,7 +40,6 @@ var (
|
||||
_ ante.HasExtensionOptionsTx = &wrapper{}
|
||||
_ ExtensionOptionsTxBuilder = &wrapper{}
|
||||
_ tx.TipTx = &wrapper{}
|
||||
_ mempool.Tx = &wrapper{}
|
||||
)
|
||||
|
||||
// ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions.
|
||||
@ -66,12 +62,6 @@ func newBuilder(cdc codec.Codec) *wrapper {
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the size of the transaction, but is only correct immediately after decoding a proto-marshal transaction.
|
||||
// It should not be used in any other cases.
|
||||
func (w *wrapper) Size() int64 {
|
||||
return w.txSize
|
||||
}
|
||||
|
||||
func (w *wrapper) GetMsgs() []sdk.Msg {
|
||||
return w.tx.GetMsgs()
|
||||
}
|
||||
@ -199,10 +189,12 @@ func (w *wrapper) GetSignaturesV2() ([]signing.SignatureV2, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// sequence number is functionally a transaction nonce and referred to as such in the SDK
|
||||
nonce := si.GetSequence()
|
||||
res[i] = signing.SignatureV2{
|
||||
PubKey: pubKeys[i],
|
||||
Data: sigData,
|
||||
Sequence: si.GetSequence(),
|
||||
Sequence: nonce,
|
||||
}
|
||||
|
||||
}
|
||||
@ -221,8 +213,6 @@ func (w *wrapper) SetMsgs(msgs ...sdk.Msg) error {
|
||||
|
||||
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
|
||||
w.bodyBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -233,8 +223,6 @@ func (w *wrapper) SetTimeoutHeight(height uint64) {
|
||||
|
||||
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
|
||||
w.bodyBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetMemo(memo string) {
|
||||
@ -242,8 +230,6 @@ func (w *wrapper) SetMemo(memo string) {
|
||||
|
||||
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
|
||||
w.bodyBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetGasLimit(limit uint64) {
|
||||
@ -255,8 +241,6 @@ func (w *wrapper) SetGasLimit(limit uint64) {
|
||||
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetFeeAmount(coins sdk.Coins) {
|
||||
@ -268,8 +252,6 @@ func (w *wrapper) SetFeeAmount(coins sdk.Coins) {
|
||||
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetTip(tip *tx.Tip) {
|
||||
@ -277,8 +259,6 @@ func (w *wrapper) SetTip(tip *tx.Tip) {
|
||||
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetFeePayer(feePayer sdk.AccAddress) {
|
||||
@ -290,8 +270,6 @@ func (w *wrapper) SetFeePayer(feePayer sdk.AccAddress) {
|
||||
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetFeeGranter(feeGranter sdk.AccAddress) {
|
||||
@ -303,8 +281,6 @@ func (w *wrapper) SetFeeGranter(feeGranter sdk.AccAddress) {
|
||||
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetSignatures(signatures ...signing.SignatureV2) error {
|
||||
@ -336,8 +312,6 @@ func (w *wrapper) setSignerInfos(infos []*tx.SignerInfo) {
|
||||
w.tx.AuthInfo.SignerInfos = infos
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) setSignerInfoAtIndex(index int, info *tx.SignerInfo) {
|
||||
@ -348,8 +322,6 @@ func (w *wrapper) setSignerInfoAtIndex(index int, info *tx.SignerInfo) {
|
||||
w.tx.AuthInfo.SignerInfos[index] = info
|
||||
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
|
||||
w.authInfoBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) setSignatures(sigs [][]byte) {
|
||||
@ -396,15 +368,11 @@ func (w *wrapper) GetNonCriticalExtensionOptions() []*codectypes.Any {
|
||||
func (w *wrapper) SetExtensionOptions(extOpts ...*codectypes.Any) {
|
||||
w.tx.Body.ExtensionOptions = extOpts
|
||||
w.bodyBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) {
|
||||
w.tx.Body.NonCriticalExtensionOptions = extOpts
|
||||
w.bodyBz = nil
|
||||
// set txSize to 0 because it is no longer correct
|
||||
w.txSize = 0
|
||||
}
|
||||
|
||||
func (w *wrapper) AddAuxSignerData(data tx.AuxSignerData) error {
|
||||
|
||||
@ -79,8 +79,9 @@ func ProvideModule(in TxInputs) TxOutputs {
|
||||
app.SetPostHandler(postHandler)
|
||||
}
|
||||
|
||||
// TxDecoder
|
||||
// TxDecoder/TxEncoder
|
||||
app.SetTxDecoder(txConfig.TxDecoder())
|
||||
app.SetTxEncoder(txConfig.TxEncoder())
|
||||
}
|
||||
|
||||
return TxOutputs{TxConfig: txConfig, BaseAppOption: baseAppOption}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/types/msgservice"
|
||||
authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec"
|
||||
govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
groupcodec "github.com/cosmos/cosmos-sdk/x/group/codec"
|
||||
)
|
||||
|
||||
@ -37,6 +38,10 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
|
||||
&MsgCommunityPoolSpend{},
|
||||
)
|
||||
|
||||
registry.RegisterImplementations(
|
||||
(*govtypes.Content)(nil),
|
||||
&CommunityPoolSpendProposal{})
|
||||
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
|
||||
}
|
||||
|
||||
|
||||
@ -3,8 +3,38 @@ package types
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
)
|
||||
|
||||
// GetTitle returns the title of a community pool spend proposal.
|
||||
func (csp *CommunityPoolSpendProposal) GetTitle() string { return csp.Title }
|
||||
|
||||
// GetDescription returns the description of a community pool spend proposal.
|
||||
func (csp *CommunityPoolSpendProposal) GetDescription() string { return csp.Description }
|
||||
|
||||
// GetDescription returns the routing key of a community pool spend proposal.
|
||||
func (csp *CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey }
|
||||
|
||||
// ProposalType returns the type of a community pool spend proposal.
|
||||
func (csp *CommunityPoolSpendProposal) ProposalType() string { return "CommunityPoolSpend" }
|
||||
|
||||
// ValidateBasic runs basic stateless validity checks
|
||||
func (csp *CommunityPoolSpendProposal) ValidateBasic() error {
|
||||
err := govtypes.ValidateAbstract(csp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !csp.Amount.IsValid() {
|
||||
return ErrInvalidProposalAmount
|
||||
}
|
||||
if csp.Recipient == "" {
|
||||
return ErrEmptyProposalRecipient
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (csp CommunityPoolSpendProposal) String() string {
|
||||
var b strings.Builder
|
||||
|
||||
@ -107,7 +107,7 @@ func DeliverGenTxs(
|
||||
|
||||
res := deliverTx(abci.RequestDeliverTx{Tx: bz})
|
||||
if !res.IsOK() {
|
||||
return nil, fmt.Errorf("failed to execute DelverTx for '%s': %s", genTx, res.Log)
|
||||
return nil, fmt.Errorf("failed to execute DeliverTx for '%s': %s", genTx, res.Log)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user