diff --git a/app/app.go b/app/app.go index 53db42419f..430eacb94a 100644 --- a/app/app.go +++ b/app/app.go @@ -1004,9 +1004,6 @@ func New( if benchmarkEnabled { evmChainID := evmconfig.GetEVMChainID(app.ChainID).Int64() app.InitBenchmark(context.Background(), app.ChainID, evmChainID) - app.SetPrepareProposalHandler(app.PrepareProposalBenchmarkHandler) - } else { - app.SetPrepareProposalHandler(app.PrepareProposalHandler) } app.SetProcessProposalHandler(app.ProcessProposalHandler) @@ -1196,14 +1193,6 @@ func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.Res return app.mm.InitGenesis(ctx, app.appCodec, genesisState, app.genesisImportConfig) } -func (app *App) PrepareProposalHandler(_ sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - return &abci.ResponsePrepareProposal{ - TxRecords: utils.Map(req.Txs, func(tx []byte) *abci.TxRecord { - return &abci.TxRecord{Action: abci.TxRecord_UNMODIFIED, Tx: tx} - }), - }, nil -} - func (app *App) GetOptimisticProcessingInfo() OptimisticProcessingInfo { app.optimisticProcessingInfoMutex.RLock() defer app.optimisticProcessingInfoMutex.RUnlock() diff --git a/app/benchmark.go b/app/benchmark.go index 5643eed018..465220fee3 100644 --- a/app/benchmark.go +++ b/app/benchmark.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/app/benchmark" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" evmcfg "github.com/sei-protocol/sei-chain/x/evm/config" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" ) @@ -31,24 +30,6 @@ func (app *App) InitBenchmark(ctx context.Context, chainID string, evmChainID in logger.Info("Benchmark system initialized") } -// PrepareProposalBenchmarkHandler generates benchmark transactions during PrepareProposal. -func (app *App) PrepareProposalBenchmarkHandler(_ sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - if app.benchmarkManager == nil { - return &abci.ResponsePrepareProposal{TxRecords: []*abci.TxRecord{}}, nil - } - - select { - case proposal, ok := <-app.benchmarkManager.ProposalChannel(): - if proposal == nil || !ok { - return &abci.ResponsePrepareProposal{TxRecords: []*abci.TxRecord{}}, nil - } - app.benchmarkManager.Logger.Increment(int64(len(proposal.TxRecords)), req.Header.Time, req.Header.Height) - return proposal, nil - default: - return &abci.ResponsePrepareProposal{TxRecords: []*abci.TxRecord{}}, nil - } -} - // ProcessBenchmarkReceipts extracts receipts from the block and forwards them to // the benchmark system for deployment tracking during the setup phase. func (app *App) ProcessBenchmarkReceipts(ctx sdk.Context) { diff --git a/app/benchmark/benchmark.go b/app/benchmark/benchmark.go index c468d9a06f..34d8449ed4 100644 --- a/app/benchmark/benchmark.go +++ b/app/benchmark/benchmark.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/sei-cosmos/client" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" evmcfg "github.com/sei-protocol/sei-chain/x/evm/config" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/seilog" @@ -38,7 +37,7 @@ var logger = seilog.NewLogger("app", "benchmark") type Manager struct { Generator *Generator Logger *Logger - proposalCh <-chan *abci.ResponsePrepareProposal + proposalCh <-chan [][]byte } // NewManager creates a new benchmark manager from configuration. @@ -79,7 +78,7 @@ func NewManager(ctx context.Context, txConfig client.TxConfig, chainID string, e } // ProposalChannel returns the channel of prepared proposals. -func (m *Manager) ProposalChannel() <-chan *abci.ResponsePrepareProposal { +func (m *Manager) ProposalChannel() <-chan [][]byte { return m.proposalCh } diff --git a/app/benchmark/generator.go b/app/benchmark/generator.go index 429ab312a5..9fc9119b14 100644 --- a/app/benchmark/generator.go +++ b/app/benchmark/generator.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/sei-protocol/sei-chain/sei-cosmos/client" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" "github.com/sei-protocol/sei-load/config" @@ -237,11 +236,11 @@ func (g *Generator) craftDeploymentTx(state *scenarioState, txScenario *loadtype // generateSetupBlock creates deployment transactions for undeployed scenarios. // This is called on every PrepareProposal during setup phase, but we only // create a deployment transaction ONCE per scenario. -func (g *Generator) generateSetupBlock() []*abci.TxRecord { +func (g *Generator) generateSetupBlock() [][]byte { g.mu.Lock() defer g.mu.Unlock() - txRecords := make([]*abci.TxRecord, 0, len(g.scenarios)) + txs := make([][]byte, 0, len(g.scenarios)) for _, state := range g.scenarios { // Skip if already deployed @@ -278,11 +277,11 @@ func (g *Generator) generateSetupBlock() []*abci.TxRecord { "txHash", deployTx.Hash()) // Convert to Cosmos SDK tx - txRecord, err := g.ethTxToTxRecord(deployTx) + tx, err := g.ethTxToTx(deployTx) if err != nil { panic(fmt.Sprintf("benchmark: Failed to convert deployment tx for %s: %v", state.config.Name, err)) } - txRecords = append(txRecords, txRecord) + txs = append(txs, tx) } // Fast-path: if no scenarios need contract deployment (e.g., all EVMTransfer), @@ -293,7 +292,7 @@ func (g *Generator) generateSetupBlock() []*abci.TxRecord { g.transitionToLoadPhase() } - return txRecords + return txs } // allScenariosDeployed returns true if all scenarios are marked as deployed. @@ -307,7 +306,7 @@ func (g *Generator) allScenariosDeployed() bool { } // generateLoadBlock generates load transactions. -func (g *Generator) generateLoadBlock() []*abci.TxRecord { +func (g *Generator) generateLoadBlock() [][]byte { g.mu.RLock() loadGen := g.loadGenerator g.mu.RUnlock() @@ -317,20 +316,20 @@ func (g *Generator) generateLoadBlock() []*abci.TxRecord { } loadTxs := loadGen.GenerateN(g.txsPerBatch) - txRecords := make([]*abci.TxRecord, 0, len(loadTxs)) + txs := make([][]byte, 0, len(loadTxs)) for _, loadTx := range loadTxs { - txRecord, err := g.ethTxToTxRecord(loadTx.EthTx) + tx, err := g.ethTxToTx(loadTx.EthTx) if err != nil { panic(fmt.Sprintf("benchmark: Failed to convert load tx: %v", err)) } - txRecords = append(txRecords, txRecord) + txs = append(txs, tx) } - return txRecords + return txs } -// ethTxToTxRecord converts an Ethereum transaction to a Cosmos SDK TxRecord. -func (g *Generator) ethTxToTxRecord(ethTx *ethtypes.Transaction) (*abci.TxRecord, error) { +// ethTxToTx converts an Ethereum transaction to an encoded Cosmos SDK tx. +func (g *Generator) ethTxToTx(ethTx *ethtypes.Transaction) ([]byte, error) { txData, err := ethtx.NewTxDataFromTx(ethTx) if err != nil { return nil, fmt.Errorf("failed to convert eth tx to tx data: %w", err) @@ -352,10 +351,7 @@ func (g *Generator) ethTxToTxRecord(ethTx *ethtypes.Transaction) (*abci.TxRecord return nil, fmt.Errorf("failed to encode tx: %w", err) } - return &abci.TxRecord{ - Action: abci.TxRecord_UNMODIFIED, - Tx: txbz, - }, nil + return txbz, nil } // ProcessReceipts handles receipts from FinalizeBlock to extract deployed addresses. @@ -451,8 +447,8 @@ func (g *Generator) transitionToLoadPhase() { logger.Info("benchmark: Load generator initialized and ready", "scenarios", len(weightedConfigs)) } -// Generate returns the next batch of transaction records. -func (g *Generator) Generate() []*abci.TxRecord { +// Generate returns the next batch of encoded txs. +func (g *Generator) Generate() [][]byte { g.mu.Lock() phase := g.phase @@ -493,9 +489,9 @@ func (g *Generator) GetPendingDeployHashes() []common.Hash { return hashes } -// StartProposalChannel creates a channel that generates proposals. -func (g *Generator) StartProposalChannel(ctx context.Context, logger *Logger) <-chan *abci.ResponsePrepareProposal { - ch := make(chan *abci.ResponsePrepareProposal, 100) +// StartProposalChannel creates a channel that generates raw tx batches. +func (g *Generator) StartProposalChannel(ctx context.Context, logger *Logger) <-chan [][]byte { + ch := make(chan [][]byte, 100) go func() { defer close(ch) @@ -504,17 +500,13 @@ func (g *Generator) StartProposalChannel(ctx context.Context, logger *Logger) <- return } - txRecords := g.Generate() - if len(txRecords) == 0 { + txs := g.Generate() + if len(txs) == 0 { continue } - proposal := &abci.ResponsePrepareProposal{ - TxRecords: txRecords, - } - select { - case ch <- proposal: + case ch <- txs: case <-ctx.Done(): return } diff --git a/app/benchmark_test.go b/app/benchmark_test.go index 92e9ffd200..d7996efde3 100644 --- a/app/benchmark_test.go +++ b/app/benchmark_test.go @@ -6,62 +6,9 @@ import ( "time" "github.com/sei-protocol/sei-chain/app/benchmark" - "github.com/sei-protocol/sei-chain/sei-cosmos/store/rootmulti" - sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" "github.com/stretchr/testify/require" - dbm "github.com/tendermint/tm-db" ) -func createTestContext() sdk.Context { - db := dbm.NewMemDB() - - ms := rootmulti.NewStore(db) - return sdk.NewContext(ms, tmtypes.Header{}, false) -} - -func TestPrepareProposalBenchmarkHandler(t *testing.T) { - // Create a mock app with benchmark mode enabled - - app := &App{} - - // Test handler with nil manager (should return empty proposal) - ctx := createTestContext() - req := &abci.RequestPrepareProposal{ - Header: &tmtypes.Header{ - Height: 1, - Time: time.Now(), - }, - } - resp, err := app.PrepareProposalBenchmarkHandler(ctx, req) - require.NoError(t, err) - require.NotNil(t, resp) - require.Len(t, resp.TxRecords, 0) - - // Create a mock manager with a channel - proposalCh := make(chan *abci.ResponsePrepareProposal, 1) - testProposal := &abci.ResponsePrepareProposal{ - TxRecords: []*abci.TxRecord{ - {Action: abci.TxRecord_UNMODIFIED, Tx: []byte("tx1")}, - {Action: abci.TxRecord_UNMODIFIED, Tx: []byte("tx2")}, - }, - } - proposalCh <- testProposal - - app.benchmarkManager = &benchmark.Manager{ - Logger: benchmark.NewLogger(), - } - // We can't easily set the proposalCh since it's unexported, so we test the nil case - - // Test that handler doesn't panic with nil manager - app.benchmarkManager = nil - resp2, err := app.PrepareProposalBenchmarkHandler(ctx, req) - require.NoError(t, err) - require.NotNil(t, resp2) - require.Len(t, resp2.TxRecords, 0) -} - func TestBenchmarkHelperMethods(t *testing.T) { app := &App{} @@ -166,7 +113,7 @@ func TestInitBenchmark_Success(t *testing.T) { if ok { require.NotNil(t, proposal, "Proposal should not be nil") // EVMTransfer scenario doesn't need deployment, so should get load txs immediately - t.Logf("Received proposal with %d tx records", len(proposal.TxRecords)) + t.Logf("Received proposal with %d txs", len(proposal)) } case <-time.After(5 * time.Second): t.Log("Timeout waiting for proposal (may be in setup phase)") diff --git a/sei-cosmos/baseapp/abci.go b/sei-cosmos/baseapp/abci.go index 0c1f0f53df..efad3bfdf3 100644 --- a/sei-cosmos/baseapp/abci.go +++ b/sei-cosmos/baseapp/abci.go @@ -23,7 +23,6 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" "github.com/sei-protocol/sei-chain/sei-cosmos/types/legacytm" - "github.com/sei-protocol/sei-chain/sei-cosmos/utils" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" "google.golang.org/grpc/codes" @@ -52,7 +51,6 @@ func (app *BaseApp) InitChain(ctx context.Context, req *abci.RequestInitChain) ( // initialize the deliver state and check state 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 @@ -60,7 +58,6 @@ func (app *BaseApp) InitChain(ctx context.Context, req *abci.RequestInitChain) ( // to state. if req.ConsensusParams != nil { app.StoreConsensusParams(app.deliverState.ctx, req.ConsensusParams) - app.StoreConsensusParams(app.prepareProposalState.ctx, req.ConsensusParams) app.StoreConsensusParams(app.processProposalState.ctx, req.ConsensusParams) app.StoreConsensusParams(app.checkState.ctx, req.ConsensusParams) } @@ -72,7 +69,6 @@ func (app *BaseApp) InitChain(ctx context.Context, req *abci.RequestInitChain) ( } resp := app.initChainer(app.deliverState.ctx, *req) - app.initChainer(app.prepareProposalState.ctx, *req) app.initChainer(app.processProposalState.ctx, *req) res = &resp @@ -947,54 +943,6 @@ func splitPath(requestPath string) (path []string) { } // ABCI++ -func (app *BaseApp) PrepareProposal(ctx context.Context, req *abci.RequestPrepareProposal) (resp *abci.ResponsePrepareProposal, err error) { - defer telemetry.MeasureSince(time.Now(), "abci", "prepare_proposal") - if app.ChainID != req.Header.ChainID { - return nil, fmt.Errorf("unexpected ChainID, got %q, want %q", req.Header.ChainID, app.ChainID) - } - if app.prepareProposalState == nil { - app.setPrepareProposalState(*req.Header) - } else { - // In the first block, app.prepareProposalState.ctx will already be initialized - // by InitChain. Context is now updated with Header information. - app.setPrepareProposalHeader(*req.Header) - } - - app.preparePrepareProposalState() - - defer func() { - if err := recover(); err != nil { - logger.Error( - "panic recovered in PrepareProposal", - "height", req.Header.Height, - "time", req.Header.Time, - "panic", err, - ) - - resp = &abci.ResponsePrepareProposal{ - TxRecords: utils.Map(req.Txs, func(tx []byte) *abci.TxRecord { - return &abci.TxRecord{Action: abci.TxRecord_UNMODIFIED, Tx: tx} - }), - } - } - }() - - if app.prepareProposalHandler != nil { - resp, err = app.prepareProposalHandler(app.prepareProposalState.ctx, req) - if err != nil { - return nil, err - } - - if cp := app.GetConsensusParams(app.prepareProposalState.ctx); cp != nil { - resp.ConsensusParamUpdates = cp - } - - return resp, nil - } - - return nil, errors.New("no prepare proposal handler") -} - func (app *BaseApp) ProcessProposal(ctx context.Context, req *abci.RequestProcessProposal) (resp *abci.ResponseProcessProposal, err error) { defer telemetry.MeasureSince(time.Now(), "abci", "process_proposal") if app.ChainID != req.Header.ChainID { diff --git a/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index 7b163f6688..3a5d2c91cc 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -86,7 +86,6 @@ type BaseApp struct { interfaceRegistry types.InterfaceRegistry txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx - prepareProposalHandler sdk.PrepareProposalHandler processProposalHandler sdk.ProcessProposalHandler finalizeBlocker sdk.FinalizeBlocker anteHandler sdk.AnteHandler // ante handler for fee and auth @@ -109,7 +108,6 @@ type BaseApp struct { // deliverState is set on InitChain and BeginBlock and set to nil on Commit checkState *state // for CheckTx deliverState *state // for DeliverTx - prepareProposalState *state processProposalState *state stateToCommit *state @@ -570,21 +568,6 @@ func (app *BaseApp) setDeliverState(header tmproto.Header) { app.deliverState.SetContext(ctx) } -func (app *BaseApp) setPrepareProposalState(header tmproto.Header) { - ms := app.cms.CacheMultiStore() - ctx := sdk.NewContext(ms, header, false) - if app.prepareProposalState == nil { - app.prepareProposalState = &state{ - ms: ms, - ctx: ctx, - mtx: &sync.RWMutex{}, - } - return - } - app.prepareProposalState.SetMultiStore(ms) - app.prepareProposalState.SetContext(ctx) -} - func (app *BaseApp) setProcessProposalState(header tmproto.Header) { ms := app.cms.CacheMultiStore() ctx := sdk.NewContext(ms, header, false) @@ -601,16 +584,11 @@ func (app *BaseApp) setProcessProposalState(header tmproto.Header) { } func (app *BaseApp) resetStatesExceptCheckState() { - app.prepareProposalState = nil app.processProposalState = nil app.deliverState = nil app.stateToCommit = nil } -func (app *BaseApp) setPrepareProposalHeader(header tmproto.Header) { - app.prepareProposalState.SetContext(app.prepareProposalState.Context().WithBlockHeader(header)) -} - func (app *BaseApp) setProcessProposalHeader(header tmproto.Header) { app.processProposalState.SetContext(app.processProposalState.Context().WithBlockHeader(header)) } @@ -619,12 +597,6 @@ func (app *BaseApp) setDeliverStateHeader(header tmproto.Header) { app.deliverState.SetContext(app.deliverState.Context().WithBlockHeader(header).WithBlockHeight(header.Height)) } -func (app *BaseApp) preparePrepareProposalState() { - if app.prepareProposalState.MultiStore().TracingEnabled() { - app.prepareProposalState.SetMultiStore(app.prepareProposalState.MultiStore().SetTracingContext(nil).(sdk.CacheMultiStore)) - } -} - func (app *BaseApp) prepareProcessProposalState(headerHash []byte) { app.processProposalState.SetContext(app.processProposalState.Context(). WithHeaderHash(headerHash). diff --git a/sei-cosmos/baseapp/options.go b/sei-cosmos/baseapp/options.go index 611278dde1..92ac00900e 100644 --- a/sei-cosmos/baseapp/options.go +++ b/sei-cosmos/baseapp/options.go @@ -175,14 +175,6 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { app.endBlocker = endBlocker } -func (app *BaseApp) SetPrepareProposalHandler(prepareProposalHandler sdk.PrepareProposalHandler) { - if app.sealed { - panic("SetPrepareProposalHandler() on sealed BaseApp") - } - - app.prepareProposalHandler = prepareProposalHandler -} - func (app *BaseApp) SetPreCommitHandler(preCommitHandler sdk.PreCommitHandler) { if app.sealed { panic("SetPreCommitHandler() on sealed BaseApp") diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index d44e1904b9..c0651c7ee3 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -94,10 +94,6 @@ func (m *mockApplication) ApplySnapshotChunk(ctx context.Context, req *abci.Requ return &abci.ResponseApplySnapshotChunk{}, nil } -func (m *mockApplication) PrepareProposal(ctx context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - return &abci.ResponsePrepareProposal{}, nil -} - func (m *mockApplication) ProcessProposal(ctx context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { return &abci.ResponseProcessProposal{}, nil } diff --git a/sei-cosmos/types/abci.go b/sei-cosmos/types/abci.go index e642f83e67..8adbeda34e 100644 --- a/sei-cosmos/types/abci.go +++ b/sei-cosmos/types/abci.go @@ -20,8 +20,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 -type PrepareProposalHandler func(ctx Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) - type ProcessProposalHandler func(ctx Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) type FinalizeBlocker func(ctx Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) diff --git a/sei-tendermint/abci/example/kvstore/kvstore.go b/sei-tendermint/abci/example/kvstore/kvstore.go index 90a6d0a775..4b4c2486aa 100644 --- a/sei-tendermint/abci/example/kvstore/kvstore.go +++ b/sei-tendermint/abci/example/kvstore/kvstore.go @@ -285,15 +285,6 @@ func (app *Application) Query(_ context.Context, reqQuery *types.RequestQuery) ( return &resQuery, nil } -func (app *Application) PrepareProposal(_ context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - app.mu.Lock() - defer app.mu.Unlock() - - return &types.ResponsePrepareProposal{ - TxRecords: app.substPrepareTx(req.Txs, req.MaxTxBytes), - }, nil -} - func (*Application) ProcessProposal(_ context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { for _, tx := range req.Txs { if len(tx) == 0 { @@ -442,35 +433,3 @@ func (app *Application) execPrepareTx(tx []byte) *types.ExecTxResult { // noop return &types.ExecTxResult{} } - -// substPrepareTx substitutes all the transactions prefixed with 'prepare' in the -// proposal for transactions with the prefix stripped. -// It marks all of the original transactions as 'REMOVED' so that -// Tendermint will remove them from its mempool. -func (app *Application) substPrepareTx(blockData [][]byte, maxTxBytes int64) []*types.TxRecord { - trs := make([]*types.TxRecord, 0, len(blockData)) - var removed []*types.TxRecord - var totalBytes int64 - for _, tx := range blockData { - txMod := tx - action := types.TxRecord_UNMODIFIED - if isPrepareTx(tx) { - removed = append(removed, &types.TxRecord{ - Tx: tx, - Action: types.TxRecord_UNMODIFIED, - }) - txMod = bytes.TrimPrefix(tx, []byte(PreparePrefix)) - action = types.TxRecord_UNMODIFIED - } - totalBytes += int64(len(txMod)) - if totalBytes > maxTxBytes { - break - } - trs = append(trs, &types.TxRecord{ - Tx: txMod, - Action: action, - }) - } - - return append(trs, removed...) -} diff --git a/sei-tendermint/abci/types/application.go b/sei-tendermint/abci/types/application.go index 7049d3b462..ac578f286e 100644 --- a/sei-tendermint/abci/types/application.go +++ b/sei-tendermint/abci/types/application.go @@ -17,7 +17,6 @@ type Application interface { // Consensus Connection InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore - PrepareProposal(context.Context, *RequestPrepareProposal) (*ResponsePrepareProposal, error) ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error) // Commit the state and return the application Merkle root hash Commit(context.Context) (*ResponseCommit, error) @@ -78,22 +77,6 @@ func (BaseApplication) ApplySnapshotChunk(_ context.Context, req *RequestApplySn return &ResponseApplySnapshotChunk{}, nil } -func (BaseApplication) PrepareProposal(_ context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) { - trs := make([]*TxRecord, 0, len(req.Txs)) - var totalBytes int64 - for _, tx := range req.Txs { - totalBytes += int64(len(tx)) - if totalBytes > req.MaxTxBytes { - break - } - trs = append(trs, &TxRecord{ - Action: TxRecord_UNMODIFIED, - Tx: tx, - }) - } - return &ResponsePrepareProposal{TxRecords: trs}, nil -} - func (BaseApplication) ProcessProposal(_ context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) { return &ResponseProcessProposal{Status: ResponseProcessProposal_ACCEPT}, nil } diff --git a/sei-tendermint/abci/types/mocks/application.go b/sei-tendermint/abci/types/mocks/application.go index 26b637b993..638282e71d 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -314,36 +314,6 @@ func (_m *Application) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOffe return r0, r1 } -// PrepareProposal provides a mock function with given fields: _a0, _a1 -func (_m *Application) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for PrepareProposal") - } - - var r0 *types.ResponsePrepareProposal - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.ResponsePrepareProposal) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ProcessProposal provides a mock function with given fields: _a0, _a1 func (_m *Application) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { ret := _m.Called(_a0, _a1) diff --git a/sei-tendermint/abci/types/plain_types.go b/sei-tendermint/abci/types/plain_types.go index c57c750e81..0752d7febd 100644 --- a/sei-tendermint/abci/types/plain_types.go +++ b/sei-tendermint/abci/types/plain_types.go @@ -39,15 +39,6 @@ type RequestQuery struct { type RequestCommit struct{} -type RequestPrepareProposal struct { - MaxTxBytes int64 - Txs [][]byte - ByzantineValidators []Misbehavior - LocalLastCommitInfo CommitInfo - - Header *tmproto.Header -} - type RequestBeginBlock struct { Hash []byte Header tmproto.Header @@ -77,14 +68,6 @@ type ResponseInitChain struct { AppHash []byte } -type ResponsePrepareProposal struct { - TxRecords []*TxRecord - AppHash []byte - TxResults []*ExecTxResult - ValidatorUpdates []ValidatorUpdate - ConsensusParamUpdates *tmproto.ConsensusParams -} - type ResponseGetTxPriorityHint struct { Priority int64 } @@ -297,26 +280,6 @@ type Evidence struct { TotalVotingPower int64 } -type TxRecord struct { - Action TxRecord_TxAction - Tx []byte -} - -type TxRecord_TxAction int32 - -const ( - TxRecord_UNMODIFIED TxRecord_TxAction = iota -) - -func (a TxRecord_TxAction) String() string { - switch a { - case TxRecord_UNMODIFIED: - return "UNMODIFIED" - default: - return "UNMODIFIED" - } -} - type Snapshot struct { Height uint64 Format uint32 @@ -395,20 +358,6 @@ func (m *RequestQuery) GetHeight() int64 { return 0 } -func (m *RequestPrepareProposal) GetTxs() [][]byte { - if m != nil { - return m.Txs - } - return nil -} - -func (m *RequestPrepareProposal) GetByzantineValidators() []Misbehavior { - if m != nil { - return m.ByzantineValidators - } - return nil -} - func (m *RequestBeginBlock) GetHash() []byte { if m != nil { return m.Hash @@ -465,34 +414,6 @@ func (m *ResponseInitChain) GetAppHash() []byte { return nil } -func (m *ResponsePrepareProposal) GetAppHash() []byte { - if m != nil { - return m.AppHash - } - return nil -} - -func (m *ResponsePrepareProposal) GetTxResults() []*ExecTxResult { - if m != nil { - return m.TxResults - } - return nil -} - -func (m *ResponsePrepareProposal) GetValidatorUpdates() []ValidatorUpdate { - if m != nil { - return m.ValidatorUpdates - } - return nil -} - -func (m *ResponsePrepareProposal) GetConsensusParamUpdates() *tmproto.ConsensusParams { - if m != nil { - return m.ConsensusParamUpdates - } - return nil -} - func (m *ResponseGetTxPriorityHint) GetPriority() int64 { if m != nil { return m.Priority @@ -619,20 +540,6 @@ func (m *Evidence) GetTotalVotingPower() int64 { return 0 } -func (m *TxRecord) GetAction() TxRecord_TxAction { - if m != nil { - return m.Action - } - return TxRecord_UNMODIFIED -} - -func (m *TxRecord) GetTx() []byte { - if m != nil { - return m.Tx - } - return nil -} - func (m *Snapshot) GetHeight() uint64 { if m != nil { return m.Height diff --git a/sei-tendermint/internal/consensus/mempool_test.go b/sei-tendermint/internal/consensus/mempool_test.go index c8cc7bb680..cb2570cf5f 100644 --- a/sei-tendermint/internal/consensus/mempool_test.go +++ b/sei-tendermint/internal/consensus/mempool_test.go @@ -363,22 +363,6 @@ func (app *CounterApplication) Commit(context.Context) (*abci.ResponseCommit, er return &abci.ResponseCommit{}, nil } -func (app *CounterApplication) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - trs := make([]*abci.TxRecord, 0, len(req.Txs)) - var totalBytes int64 - for _, tx := range req.Txs { - totalBytes += int64(len(tx)) - if totalBytes > req.MaxTxBytes { - break - } - trs = append(trs, &abci.TxRecord{ - Action: abci.TxRecord_UNMODIFIED, - Tx: tx, - }) - } - return &abci.ResponsePrepareProposal{TxRecords: trs}, nil -} - func (app *CounterApplication) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil } diff --git a/sei-tendermint/internal/consensus/state_test.go b/sei-tendermint/internal/consensus/state_test.go index 5bf958d399..a76922cb94 100644 --- a/sei-tendermint/internal/consensus/state_test.go +++ b/sei-tendermint/internal/consensus/state_test.go @@ -1905,7 +1905,6 @@ func TestProcessProposalAccept(t *testing.T) { status = abci.ResponseProcessProposal_ACCEPT } m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: status}, nil) - m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Maybe() cs1, _ := makeState(ctx, t, makeStateArgs{config: config, application: m}) height, round := cs1.roundState.Height(), cs1.roundState.Round() @@ -1953,7 +1952,6 @@ func TestFinalizeBlockCalled(t *testing.T) { m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{ Status: abci.ResponseProcessProposal_ACCEPT, }, nil) - m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) r := &abci.ResponseFinalizeBlock{AppHash: []byte("the_hash")} m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe() m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() @@ -2877,50 +2875,3 @@ func TestAddProposalBlockPartNilProposalBlockParts(t *testing.T) { require.False(t, added, "Part should not be added when ProposalBlockParts is nil") require.NoError(t, err, "No error expected when ProposalBlockParts is nil, just debug logging") } - -// TestCreateProposalBlockPanicRecovery tests that panics in createProposalBlock are recovered -func TestCreateProposalBlockPanicRecovery(t *testing.T) { - ctx := t.Context() - config := configSetup(t) - - // Create a consensus state with a panicking app - cs1, vss := makeState(ctx, t, makeStateArgs{ - config: config, - application: &panicConsensusApp{}, - }) - - // Make sure we're at the right height and have validators - incrementHeight(vss...) - - cs1.mtx.Lock() - // This should trigger the panic recovery mechanism in createProposalBlock - block, err := cs1.createProposalBlock(ctx) - cs1.mtx.Unlock() - - // Verify panic was recovered and converted to error - assert.Nil(t, block, "Block should be nil when panic is recovered") - assert.Error(t, err, "Should return error when panic is recovered") - assert.Contains(t, err.Error(), "CreateProposalBlock panic recovered", "Error should indicate panic recovery") - assert.Contains(t, err.Error(), "consensus panic test", "Error should contain original panic message") -} - -// panicConsensusApp is a test app that panics during PrepareProposal to test panic recovery -type panicConsensusApp struct { - abci.BaseApplication -} - -func (app *panicConsensusApp) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - panic("consensus panic test") -} - -func (app *panicConsensusApp) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { - return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil -} - -func (app *panicConsensusApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { - return &abci.ResponseFinalizeBlock{}, nil -} - -func (app *panicConsensusApp) Commit(_ context.Context) (*abci.ResponseCommit, error) { - return &abci.ResponseCommit{}, nil -} diff --git a/sei-tendermint/internal/proxy/client.go b/sei-tendermint/internal/proxy/client.go index 76588422ba..611f6287e8 100644 --- a/sei-tendermint/internal/proxy/client.go +++ b/sei-tendermint/internal/proxy/client.go @@ -28,11 +28,6 @@ func (app *proxyClient) InitChain(ctx context.Context, req *types.RequestInitCha return app.app.InitChain(ctx, req) } -func (app *proxyClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))() - return app.app.PrepareProposal(ctx, req) -} - func (app *proxyClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))() return app.app.ProcessProposal(ctx, req) diff --git a/sei-tendermint/internal/proxy/mocks/app_conn_consensus.go b/sei-tendermint/internal/proxy/mocks/app_conn_consensus.go index 0175ac2693..7312c436ab 100644 --- a/sei-tendermint/internal/proxy/mocks/app_conn_consensus.go +++ b/sei-tendermint/internal/proxy/mocks/app_conn_consensus.go @@ -98,29 +98,6 @@ func (_m *AppConnConsensus) InitChain(_a0 context.Context, _a1 types.RequestInit return r0, r1 } -// PrepareProposal provides a mock function with given fields: _a0, _a1 -func (_m *AppConnConsensus) PrepareProposal(_a0 context.Context, _a1 types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - ret := _m.Called(_a0, _a1) - - var r0 *types.ResponsePrepareProposal - if rf, ok := ret.Get(0).(func(context.Context, types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.ResponsePrepareProposal) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestPrepareProposal) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ProcessProposal provides a mock function with given fields: _a0, _a1 func (_m *AppConnConsensus) ProcessProposal(_a0 context.Context, _a1 types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { ret := _m.Called(_a0, _a1) diff --git a/sei-tendermint/internal/state/execution.go b/sei-tendermint/internal/state/execution.go index a856abf55e..30cef7e60c 100644 --- a/sei-tendermint/internal/state/execution.go +++ b/sei-tendermint/internal/state/execution.go @@ -110,40 +110,6 @@ func (blockExec *BlockExecutor) CreateProposalBlock( txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGasWanted, maxGas) block = state.MakeBlock(height, txs, lastCommit, evidence, proposerAddr) - rpp, err := blockExec.app.PrepareProposal( - ctx, - &abci.RequestPrepareProposal{ - MaxTxBytes: maxDataBytes, - Txs: block.Txs.ToSliceOfBytes(), - LocalLastCommitInfo: buildCommitInfo(lastCommit, blockExec.store, state.InitialHeight), - ByzantineValidators: block.Evidence.ToABCI(), - Header: block.Header.ToProto(), - }, - ) - if err != nil { - // The App MUST ensure that only valid (and hence 'processable') transactions - // enter the mempool. Hence, at this point, we can't have any non-processable - // transaction causing an error. - // - // Also, the App can simply skip any transaction that could cause any kind of trouble. - // Either way, we cannot recover in a meaningful way, unless we skip proposing - // this block, repair what caused the error and try again. Hence, we return an - // error for now (the production code calling this function is expected to panic). - return nil, err - } - txrSet := types.NewTxRecordSet(rpp.TxRecords) - - if err := txrSet.Validate(maxDataBytes, block.Txs); err != nil { - return nil, err - } - - for _, rtx := range txrSet.RemovedTxs() { - if err := blockExec.mempool.RemoveTxByKey(rtx.Key()); err != nil { - logger.Debug("error removing transaction from the mempool", "tx", rtx.Key(), "err", err) - } - } - itxs := txrSet.IncludedTxs() - block = state.MakeBlock(height, itxs, lastCommit, evidence, proposerAddr) return block, nil } diff --git a/sei-tendermint/internal/state/execution_test.go b/sei-tendermint/internal/state/execution_test.go index a7e9c85c3f..99d34f9f1f 100644 --- a/sei-tendermint/internal/state/execution_test.go +++ b/sei-tendermint/internal/state/execution_test.go @@ -2,7 +2,6 @@ package state_test import ( "context" - "errors" "testing" "time" @@ -570,247 +569,6 @@ func TestFinalizeBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { assert.NotEmpty(t, state.NextValidators.Validators) } -func TestEmptyPrepareProposal(t *testing.T) { - const height = 2 - ctx := t.Context() - var err error - - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - - app := abcimocks.NewApplication(t) - app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) - - state, stateDB, privVals := makeState(t, 1, height) - stateStore := sm.NewStore(stateDB) - mp := &mpmocks.Mempool{} - mp.On("Lock").Return() - mp.On("Unlock").Return() - mp.On("Update", - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything).Return(nil) - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(types.Txs{}) - mp.On("TxStore").Return(nil) - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - sm.EmptyEvidencePool{}, - nil, - eventBus, - sm.NopMetrics(), - ) - pa, _ := state.Validators.GetByIndex(0) - commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - _, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa) - require.NoError(t, err) -} - -// TestPrepareProposalErrorOnNonExistingRemoved tests that the block creation logic returns -// an error if the ResponsePrepareProposal returned from the application marks -// -// a transaction as REMOVED that was not present in the original proposal. -func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) { - const height = 2 - ctx := t.Context() - - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - - state, stateDB, privVals := makeState(t, 1, height) - stateStore := sm.NewStore(stateDB) - - evpool := &mocks.EvidencePool{} - evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) - - mp := &mpmocks.Mempool{} - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(types.Txs{}) - - app := abcimocks.NewApplication(t) - - // create an invalid ResponsePrepareProposal - rpp := &abci.ResponsePrepareProposal{ - TxRecords: []*abci.TxRecord{ - { - Action: abci.TxRecord_UNMODIFIED, - Tx: []byte("new tx"), - }, - }, - } - app.On("PrepareProposal", mock.Anything, mock.Anything).Return(rpp, nil) - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - evpool, - nil, - eventBus, - sm.NopMetrics(), - ) - pa, _ := state.Validators.GetByIndex(0) - commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) - require.ErrorContains(t, err, "new transaction incorrectly marked as removed") - require.Nil(t, block) - - mp.AssertExpectations(t) -} - -// TestPrepareProposalReorderTxs tests that CreateBlock produces a block with transactions -// in the order matching the order they are returned from PrepareProposal. -func TestPrepareProposalReorderTxs(t *testing.T) { - const height = 2 - ctx := t.Context() - var err error - - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - - state, stateDB, privVals := makeState(t, 1, height) - stateStore := sm.NewStore(stateDB) - - evpool := &mocks.EvidencePool{} - evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) - - txs := factory.MakeNTxs(height, 10) - mp := &mpmocks.Mempool{} - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(types.Txs(txs)) - - trs := txsToTxRecords(types.Txs(txs)) - trs = trs[2:] - trs = append(trs[len(trs)/2:], trs[:len(trs)/2]...) - - app := abcimocks.NewApplication(t) - app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ - TxRecords: trs, - }, nil) - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - evpool, - nil, - eventBus, - sm.NopMetrics(), - ) - pa, _ := state.Validators.GetByIndex(0) - commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) - require.NoError(t, err) - for i, tx := range block.Data.Txs { - require.Equal(t, types.Tx(trs[i].Tx), tx) - } - - mp.AssertExpectations(t) - -} - -// TestPrepareProposalErrorOnTooManyTxs tests that the block creation logic returns -// an error if the ResponsePrepareProposal returned from the application is invalid. -func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) { - const height = 2 - ctx := t.Context() - var err error - - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - - state, stateDB, privVals := makeState(t, 1, height) - // limit max block size - state.ConsensusParams.Block.MaxBytes = 60 * 1024 - stateStore := sm.NewStore(stateDB) - - evpool := &mocks.EvidencePool{} - evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) - - const nValidators = 1 - var bytesPerTx int64 = 3 - maxDataBytes := types.MaxDataBytes(state.ConsensusParams.Block.MaxBytes, 0, nValidators) - txs := factory.MakeNTxs(height, maxDataBytes/bytesPerTx+2) // +2 so that tx don't fit - mp := &mpmocks.Mempool{} - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(types.Txs(txs)) - - trs := txsToTxRecords(types.Txs(txs)) - - app := abcimocks.NewApplication(t) - app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ - TxRecords: trs, - }, nil) - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - evpool, - nil, - eventBus, - sm.NopMetrics(), - ) - pa, _ := state.Validators.GetByIndex(0) - commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) - require.ErrorContains(t, err, "transaction data size exceeds maximum") - require.Nil(t, block, "") - - mp.AssertExpectations(t) -} - -// TestPrepareProposalErrorOnPrepareProposalError tests when the client returns an error -// upon calling PrepareProposal on it. -func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) { - const height = 2 - ctx := t.Context() - var err error - - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - - state, stateDB, privVals := makeState(t, 1, height) - stateStore := sm.NewStore(stateDB) - - evpool := &mocks.EvidencePool{} - evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0)) - - txs := factory.MakeNTxs(height, 10) - mp := &mpmocks.Mempool{} - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(types.Txs(txs)) - - app := &failingPrepareProposalApp{} - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - evpool, - nil, - eventBus, - sm.NopMetrics(), - ) - pa, _ := state.Validators.GetByIndex(0) - commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa) - require.Nil(t, block) - require.ErrorContains(t, err, "an injected error") - - mp.AssertExpectations(t) -} - -type failingPrepareProposalApp struct { - abci.BaseApplication -} - -func (f failingPrepareProposalApp) PrepareProposal(context.Context, *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - return nil, errors.New("an injected error") -} - func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID { var ( h = make([]byte, crypto.HashSize) @@ -826,80 +584,3 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.Bloc }, } } - -func txsToTxRecords(txs []types.Tx) []*abci.TxRecord { - trs := make([]*abci.TxRecord, len(txs)) - for i, tx := range txs { - trs[i] = &abci.TxRecord{ - Action: abci.TxRecord_UNMODIFIED, - Tx: tx, - } - } - return trs -} - -// panicApp is a test app that panics during PrepareProposal to test panic recovery -type panicApp struct { - abci.BaseApplication -} - -func (app *panicApp) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - // This will trigger the panic recovery mechanism in CreateProposalBlock - panic("test panic for coverage") -} - -func (app *panicApp) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) { - return &abci.ResponseInfo{}, nil -} - -func (app *panicApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { - return &abci.ResponseFinalizeBlock{}, nil -} - -// TestCreateProposalBlockPanicRecovery tests that panics are recovered and converted to errors -func TestCreateProposalBlockPanicRecovery(t *testing.T) { - ctx := context.Background() - - // Create the panicking app - app := &panicApp{} - - // Create test state and executor - state, stateDB, _ := makeState(t, 1, 1) - stateStore := sm.NewStore(stateDB) - blockStore := store.NewBlockStore(dbm.NewMemDB()) - eventBus := eventbus.NewDefault() - require.NoError(t, eventBus.Start(ctx)) - defer eventBus.Stop() - - // Create mock mempool - mp := &mpmocks.Mempool{} - mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything, mock.Anything).Return(types.Txs{}) - - blockExec := sm.NewBlockExecutor( - stateStore, - app, - mp, - sm.EmptyEvidencePool{}, - blockStore, - eventBus, - sm.NopMetrics(), - ) - - // Get proposer address - pa, _ := state.Validators.GetByIndex(0) - - // Create commit - lastCommit := &types.Commit{} - - // This should trigger the panic recovery mechanism - block, err := blockExec.CreateProposalBlock(ctx, 1, state, lastCommit, pa) - - // Verify that panic was caught and converted to error - assert.Nil(t, block, "Block should be nil when panic is recovered") - assert.Error(t, err, "Should return error when panic is recovered") - assert.Contains(t, err.Error(), "CreateProposalBlock panic recovered", "Error should indicate panic recovery") - assert.Contains(t, err.Error(), "test panic for coverage", "Error should contain original panic message") - - // Verify mock expectations - mp.AssertExpectations(t) -} diff --git a/sei-tendermint/test/e2e/app/app.go b/sei-tendermint/test/e2e/app/app.go index 6df99e7c8e..e4039a52c1 100644 --- a/sei-tendermint/test/e2e/app/app.go +++ b/sei-tendermint/test/e2e/app/app.go @@ -325,32 +325,6 @@ func (app *Application) ApplySnapshotChunk(_ context.Context, req *abci.RequestA return &abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil } -// PrepareProposal will take the given transactions and attempt to prepare a -// proposal from them when it's our turn to do so. -// -// NB: Assumes that the supplied transactions do not exceed `req.MaxTxBytes`. -func (app *Application) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - // None of the transactions are modified by this application. - trs := make([]*abci.TxRecord, 0, len(req.Txs)) - var totalBytes int64 - for _, tx := range req.Txs { - totalBytes += int64(len(tx)) //nolint:gosec // tx length fits in int64 - if totalBytes > req.MaxTxBytes { - break - } - trs = append(trs, &abci.TxRecord{ - Action: abci.TxRecord_UNMODIFIED, - Tx: tx, - }) - } - - if app.cfg.PrepareProposalDelayMS != 0 { - time.Sleep(time.Duration(app.cfg.PrepareProposalDelayMS) * time.Millisecond) //nolint:gosec // PrepareProposalDelayMS is a small test config value - } - - return &abci.ResponsePrepareProposal{TxRecords: trs}, nil -} - // ProcessProposal implements part of the Application interface. // It accepts any proposal that does not contain a malformed transaction. func (app *Application) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { diff --git a/sei-tendermint/types/tx.go b/sei-tendermint/types/tx.go index d2dee380b0..ed66de4146 100644 --- a/sei-tendermint/types/tx.go +++ b/sei-tendermint/types/tx.go @@ -7,7 +7,6 @@ import ( "fmt" "sort" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/crypto" "github.com/sei-protocol/sei-chain/sei-tendermint/crypto/merkle" tmbytes "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" @@ -94,150 +93,6 @@ func (txs Txs) ToSliceOfBytes() [][]byte { return txBzs } -// TxRecordSet contains indexes into an underlying set of transactions. -// These indexes are useful for validating and working with a list of TxRecords -// from the PrepareProposal response. -// -// Only one copy of the original data is referenced by all of the indexes but a -// transaction may appear in multiple indexes. -type TxRecordSet struct { - // all holds the complete list of all transactions from the original list of - // TxRecords. - all Txs - - // included is an index of the transactions that will be included in the block - // and is constructed from the list of both added and unmodified transactions. - // included maintains the original order that the transactions were present - // in the list of TxRecords. - included Txs - - // added, unmodified, removed, and unknown are indexes for each of the actions - // that may be supplied with a transaction. - // - // Because each transaction only has one action, it can be referenced by - // at most 3 indexes in this data structure: the action-specific index, the - // included index, and the all index. - added Txs - unmodified Txs - removed Txs - unknown Txs -} - -// NewTxRecordSet constructs a new set from the given transaction records. -// The contents of the input transactions are shared by the set, and must not -// be modified during the lifetime of the set. -func NewTxRecordSet(trs []*abci.TxRecord) TxRecordSet { - txrSet := TxRecordSet{ - all: make([]Tx, len(trs)), - } - for i, tr := range trs { - - txrSet.all[i] = Tx(tr.Tx) - - // The following set of assignments do not allocate new []byte, they create - // pointers to the already allocated slice. - switch tr.GetAction() { - case abci.TxRecord_UNMODIFIED: - txrSet.unmodified = append(txrSet.unmodified, txrSet.all[i]) - txrSet.included = append(txrSet.included, txrSet.all[i]) - } - } - return txrSet -} - -// IncludedTxs returns the transactions marked for inclusion in a block. This -// list maintains the order that the transactions were included in the list of -// TxRecords that were used to construct the TxRecordSet. -func (t TxRecordSet) IncludedTxs() []Tx { - return t.included -} - -// RemovedTxs returns the transactions marked for removal by the application. -func (t TxRecordSet) RemovedTxs() []Tx { - return t.removed -} - -// Validate checks that the record set was correctly constructed from the original -// list of transactions. -func (t TxRecordSet) Validate(maxSizeBytes int64, otxs Txs) error { - if len(t.unknown) > 0 { - return fmt.Errorf("%d transactions marked unknown (first unknown hash: %x)", len(t.unknown), t.unknown[0].Hash()) - } - - // The following validation logic performs a set of sorts on the data in the TxRecordSet indexes. - // It sorts the original transaction list, otxs, once. - // It sorts the new transaction list twice: once when sorting 'all', the total list, - // and once by sorting the set of the added, removed, and unmodified transactions indexes, - // which, when combined, comprise the complete list of modified transactions. - // - // Each of the added, removed, and unmodified indices is then iterated and once - // and each value index is checked against the sorted original list for containment. - // Asymptotically, this yields a total runtime of O(N*log(N) + 2*M*log(M) + M*log(N)). - // in the input size of the original list, N, and the input size of the new list, M, respectively. - // Performance gains are likely possible, but this was preferred for readability and maintainability. - - // Sort a copy of the complete transaction slice so we can check for - // duplication. The copy is so we do not change the original ordering. - // Only the slices are copied, the transaction contents are shared. - allCopy := sortedCopy(t.all) - - for i, cur := range allCopy { - // allCopy is sorted, so any duplicated data will be adjacent. - if i+1 < len(allCopy) && bytes.Equal(cur, allCopy[i+1]) { - return fmt.Errorf("found duplicate transaction with hash: %x", cur.Key()) - } - } - - // create copies of each of the action-specific indexes so that order of the original - // indexes can be preserved. - addedCopy := sortedCopy(t.added) - removedCopy := sortedCopy(t.removed) - unmodifiedCopy := sortedCopy(t.unmodified) - - var size int64 - // Count size of all transactions that will be included in the block: - // unmodified and added transactions all count toward MaxTxBytes - for _, cur := range append(unmodifiedCopy, addedCopy...) { - size += int64(len(cur)) - if size > maxSizeBytes { - return fmt.Errorf("transaction data size exceeds maximum %d", maxSizeBytes) - } - } - - // make a defensive copy of otxs so that the order of - // the caller's data is not altered. - otxsCopy := sortedCopy(otxs) - - // Validate that UNMODIFIED transactions exist in the original mempool. - // In benchmark mode (when benchmark build tag is enabled), we allow UNMODIFIED - // transactions that don't exist in the mempool to support generated transactions - // marked as UNMODIFIED. This is a security consideration: we still count their - // size toward MaxTxBytes (see size calculation above), but we relax the mempool - // existence check to allow benchmark transactions. - // - // Note: In normal operation, UNMODIFIED transactions must exist in the mempool. - // This relaxation is only enabled when built with the benchmark build tag. - // For backward compatibility with tests that expect "removed" error messages, - // we return the "removed" error message when UNMODIFIED transactions don't exist - // in the mempool (when not in benchmark mode). - if !benchmarkEnabled { - // Normal mode: UNMODIFIED must exist in mempool - if ix, ok := containsAll(otxsCopy, unmodifiedCopy); !ok { - return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", unmodifiedCopy[ix].Hash()) - } - } - // In benchmark mode, we skip the mempool existence check for UNMODIFIED transactions, - // but size validation above ensures security is maintained. - - if ix, ok := containsAll(otxsCopy, removedCopy); !ok { - return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", removedCopy[ix].Hash()) - } - if ix, ok := containsAny(otxsCopy, addedCopy); ok { - return fmt.Errorf("existing transaction incorrectly marked as added, transaction hash: %x", addedCopy[ix].Hash()) - } - return nil -} - func sortedCopy(txs Txs) Txs { cp := make(Txs, len(txs)) copy(cp, txs) diff --git a/sei-tendermint/types/tx_test.go b/sei-tendermint/types/tx_test.go index c8b18ddbd2..c809b75f4a 100644 --- a/sei-tendermint/types/tx_test.go +++ b/sei-tendermint/types/tx_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" ctest "github.com/sei-protocol/sei-chain/sei-tendermint/internal/libs/test" tmrand "github.com/sei-protocol/sei-chain/sei-tendermint/libs/rand" tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" @@ -48,20 +47,6 @@ func TestTxIndexByHash(t *testing.T) { } } -func TestValidateTxRecordSet(t *testing.T) { - t.Run("should error on new transactions marked UNMODIFIED", func(t *testing.T) { - trs := []*abci.TxRecord{ - { - Action: abci.TxRecord_UNMODIFIED, - Tx: Tx([]byte{1, 2, 3, 4, 5}), - }, - } - txrSet := NewTxRecordSet(trs) - err := txrSet.Validate(100, []Tx{}) - require.Error(t, err) - }) -} - func TestValidTxProof(t *testing.T) { cases := []struct { txs Txs diff --git a/sei-wasmd/app/app.go b/sei-wasmd/app/app.go index 3b49540726..deabf4cfff 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -642,7 +642,6 @@ func NewWasmApp( app.SetAnteHandler(anteHandler) app.SetInitChainer(app.InitChainer) - app.SetPrepareProposalHandler(app.PrepareProposalHandler) app.SetProcessProposalHandler(app.ProcessProposalHandler) app.SetFinalizeBlocker(app.FinalizeBlocker) @@ -682,14 +681,6 @@ func NewWasmApp( // Name returns the name of the App func (app *WasmApp) Name() string { return app.BaseApp.Name() } -func (app *WasmApp) PrepareProposalHandler(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - return &abci.ResponsePrepareProposal{ - TxRecords: utils.Map(req.Txs, func(tx []byte) *abci.TxRecord { - return &abci.TxRecord{Action: abci.TxRecord_UNMODIFIED, Tx: tx} - }), - }, nil -} - func (app *WasmApp) ProcessProposalHandler(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { return &abci.ResponseProcessProposal{ Status: abci.ResponseProcessProposal_ACCEPT,