334 lines
12 KiB
Go
334 lines
12 KiB
Go
package baseapp_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
|
|
tmproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cosmossdk.io/schema/appdata"
|
|
storetypes "cosmossdk.io/store/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
)
|
|
|
|
var _ storetypes.ABCIListener = (*MockABCIListener)(nil)
|
|
|
|
type MockABCIListener struct {
|
|
name string
|
|
ChangeSet []*storetypes.StoreKVPair
|
|
}
|
|
|
|
func NewMockABCIListener(name string) MockABCIListener {
|
|
return MockABCIListener{
|
|
name: name,
|
|
ChangeSet: make([]*storetypes.StoreKVPair, 0),
|
|
}
|
|
}
|
|
|
|
func (m MockABCIListener) ListenFinalizeBlock(_ context.Context, _ abci.FinalizeBlockRequest, _ abci.FinalizeBlockResponse) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *MockABCIListener) ListenCommit(_ context.Context, _ abci.CommitResponse, cs []*storetypes.StoreKVPair) error {
|
|
m.ChangeSet = cs
|
|
return nil
|
|
}
|
|
|
|
var distKey1 = storetypes.NewKVStoreKey("distKey1")
|
|
|
|
func TestABCI_MultiListener_StateChanges(t *testing.T) {
|
|
anteKey := []byte("ante-key")
|
|
anteOpt := func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) }
|
|
distOpt := func(bapp *baseapp.BaseApp) { bapp.MountStores(distKey1) }
|
|
mockListener1 := NewMockABCIListener("lis_1")
|
|
mockListener2 := NewMockABCIListener("lis_2")
|
|
streamingManager := storetypes.StreamingManager{ABCIListeners: []storetypes.ABCIListener{&mockListener1, &mockListener2}}
|
|
streamingManagerOpt := func(bapp *baseapp.BaseApp) { bapp.SetStreamingManager(streamingManager) }
|
|
addListenerOpt := func(bapp *baseapp.BaseApp) { bapp.CommitMultiStore().AddListeners([]storetypes.StoreKey{distKey1}) }
|
|
suite := NewBaseAppSuite(t, anteOpt, distOpt, streamingManagerOpt, addListenerOpt)
|
|
|
|
_, err := suite.baseApp.InitChain(
|
|
&abci.InitChainRequest{
|
|
ConsensusParams: &tmproto.ConsensusParams{},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
deliverKey := []byte("deliver-key")
|
|
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, deliverKey})
|
|
|
|
nBlocks := 3
|
|
txPerHeight := 5
|
|
|
|
for blockN := 0; blockN < nBlocks; blockN++ {
|
|
txs := [][]byte{}
|
|
|
|
var expectedChangeSet []*storetypes.StoreKVPair
|
|
|
|
// create final block context state
|
|
_, err := suite.baseApp.FinalizeBlock(&abci.FinalizeBlockRequest{Height: int64(blockN) + 1, Txs: txs})
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < txPerHeight; i++ {
|
|
counter := int64(blockN*txPerHeight + i)
|
|
tx := newTxCounter(t, suite.txConfig, suite.ac, counter, counter)
|
|
|
|
txBytes, err := suite.txConfig.TxEncoder()(tx)
|
|
require.NoError(t, err)
|
|
|
|
sKey := []byte(fmt.Sprintf("distKey%d", i))
|
|
sVal := []byte(fmt.Sprintf("distVal%d", i))
|
|
store := getFinalizeBlockStateCtx(suite.baseApp).KVStore(distKey1)
|
|
store.Set(sKey, sVal)
|
|
|
|
expectedChangeSet = append(expectedChangeSet, &storetypes.StoreKVPair{
|
|
StoreKey: distKey1.Name(),
|
|
Delete: false,
|
|
Key: sKey,
|
|
Value: sVal,
|
|
})
|
|
|
|
txs = append(txs, txBytes)
|
|
}
|
|
|
|
res, err := suite.baseApp.FinalizeBlock(&abci.FinalizeBlockRequest{Height: int64(blockN) + 1, Txs: txs})
|
|
require.NoError(t, err)
|
|
for _, tx := range res.TxResults {
|
|
events := tx.GetEvents()
|
|
require.Len(t, events, 3, "should contain ante handler, message type and counter events respectively")
|
|
// require.Equal(t, sdk.MarkEventsToIndex(counterEvent("ante_handler", counter).ToABCIEvents(), map[string]struct{}{})[0], events[0], "ante handler event")
|
|
// require.Equal(t, sdk.MarkEventsToIndex(counterEvent(sdk.EventTypeMessage, counter).ToABCIEvents(), map[string]struct{}{})[0], events[2], "msg handler update counter event")
|
|
}
|
|
|
|
_, err = suite.baseApp.Commit()
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedChangeSet, mockListener1.ChangeSet, "should contain the same changeSet")
|
|
require.Equal(t, expectedChangeSet, mockListener2.ChangeSet, "should contain the same changeSet")
|
|
}
|
|
}
|
|
|
|
func Test_Ctx_with_StreamingManager(t *testing.T) {
|
|
mockListener1 := NewMockABCIListener("lis_1")
|
|
mockListener2 := NewMockABCIListener("lis_2")
|
|
listeners := []storetypes.ABCIListener{&mockListener1, &mockListener2}
|
|
streamingManager := storetypes.StreamingManager{ABCIListeners: listeners, StopNodeOnErr: true}
|
|
streamingManagerOpt := func(bapp *baseapp.BaseApp) { bapp.SetStreamingManager(streamingManager) }
|
|
addListenerOpt := func(bapp *baseapp.BaseApp) { bapp.CommitMultiStore().AddListeners([]storetypes.StoreKey{distKey1}) }
|
|
suite := NewBaseAppSuite(t, streamingManagerOpt, addListenerOpt)
|
|
|
|
_, err := suite.baseApp.InitChain(&abci.InitChainRequest{
|
|
ConsensusParams: &tmproto.ConsensusParams{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
ctx := getFinalizeBlockStateCtx(suite.baseApp)
|
|
sm := ctx.StreamingManager()
|
|
require.NotNil(t, sm, fmt.Sprintf("nil StreamingManager: %v", sm))
|
|
require.Equal(t, listeners, sm.ABCIListeners, fmt.Sprintf("should contain same listeners: %v", listeners))
|
|
require.Equal(t, true, sm.StopNodeOnErr, "should contain StopNodeOnErr = true")
|
|
|
|
nBlocks := 2
|
|
|
|
for blockN := 0; blockN < nBlocks; blockN++ {
|
|
|
|
_, err = suite.baseApp.FinalizeBlock(&abci.FinalizeBlockRequest{Height: int64(blockN) + 1})
|
|
require.NoError(t, err)
|
|
ctx := getFinalizeBlockStateCtx(suite.baseApp)
|
|
sm := ctx.StreamingManager()
|
|
require.NotNil(t, sm, fmt.Sprintf("nil StreamingManager: %v", sm))
|
|
require.Equal(t, listeners, sm.ABCIListeners, fmt.Sprintf("should contain same listeners: %v", listeners))
|
|
require.Equal(t, true, sm.StopNodeOnErr, "should contain StopNodeOnErr = true")
|
|
|
|
_, err = suite.baseApp.Commit()
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
type mockAppDataListener struct {
|
|
appdata.Listener
|
|
|
|
startBlockData []appdata.StartBlockData
|
|
txData []appdata.TxData
|
|
eventData []appdata.EventData
|
|
kvPairData []appdata.KVPairData
|
|
commitData []appdata.CommitData
|
|
}
|
|
|
|
func newMockAppDataListener() *mockAppDataListener {
|
|
listener := &mockAppDataListener{}
|
|
|
|
// Initialize the Listener with custom behavior to store data
|
|
listener.Listener = appdata.Listener{
|
|
StartBlock: func(data appdata.StartBlockData) error {
|
|
listener.startBlockData = append(listener.startBlockData, data) // Store StartBlockData
|
|
return nil
|
|
},
|
|
OnTx: func(data appdata.TxData) error {
|
|
listener.txData = append(listener.txData, data) // Store TxData
|
|
return nil
|
|
},
|
|
OnEvent: func(data appdata.EventData) error {
|
|
listener.eventData = append(listener.eventData, data) // Store EventData
|
|
return nil
|
|
},
|
|
OnKVPair: func(data appdata.KVPairData) error {
|
|
listener.kvPairData = append(listener.kvPairData, data) // Store KVPairData
|
|
return nil
|
|
},
|
|
Commit: func(data appdata.CommitData) (func() error, error) {
|
|
listener.commitData = append(listener.commitData, data) // Store CommitData
|
|
return nil, nil
|
|
},
|
|
}
|
|
|
|
return listener
|
|
}
|
|
|
|
func TestAppDataListener(t *testing.T) {
|
|
anteKey := []byte("ante-key")
|
|
anteOpt := func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) }
|
|
distOpt := func(bapp *baseapp.BaseApp) { bapp.MountStores(distKey1) }
|
|
mockListener := newMockAppDataListener()
|
|
streamingManager := storetypes.StreamingManager{ABCIListeners: []storetypes.ABCIListener{baseapp.NewListenerWrapper(mockListener.Listener)}}
|
|
streamingManagerOpt := func(bapp *baseapp.BaseApp) { bapp.SetStreamingManager(streamingManager) }
|
|
addListenerOpt := func(bapp *baseapp.BaseApp) { bapp.CommitMultiStore().AddListeners([]storetypes.StoreKey{distKey1}) }
|
|
|
|
// for event tests
|
|
baseappOpts := func(app *baseapp.BaseApp) {
|
|
app.SetPreBlocker(func(ctx sdk.Context, req *abci.FinalizeBlockRequest) error {
|
|
ctx.EventManager().EmitEvent(sdk.NewEvent("pre-block"))
|
|
return nil
|
|
})
|
|
app.SetBeginBlocker(func(_ sdk.Context) (sdk.BeginBlock, error) {
|
|
return sdk.BeginBlock{
|
|
Events: []abci.Event{
|
|
{Type: "begin-block"},
|
|
},
|
|
}, nil
|
|
})
|
|
app.SetEndBlocker(func(_ sdk.Context) (sdk.EndBlock, error) {
|
|
return sdk.EndBlock{
|
|
Events: []abci.Event{
|
|
{Type: "end-block"},
|
|
},
|
|
}, nil
|
|
})
|
|
}
|
|
|
|
suite := NewBaseAppSuite(t, anteOpt, distOpt, streamingManagerOpt, addListenerOpt, baseappOpts)
|
|
|
|
_, err := suite.baseApp.InitChain(
|
|
&abci.InitChainRequest{
|
|
ConsensusParams: &tmproto.ConsensusParams{},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
deliverKey := []byte("deliver-key")
|
|
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, deliverKey})
|
|
|
|
txCount := 5
|
|
txs := make([][]byte, txCount)
|
|
|
|
for i := 0; i < txCount; i++ {
|
|
tx := newTxCounter(t, suite.txConfig, suite.ac, int64(i), int64(i))
|
|
|
|
txBytes, err := suite.txConfig.TxEncoder()(tx)
|
|
require.NoError(t, err)
|
|
|
|
sKey := []byte(fmt.Sprintf("distKey%d", i))
|
|
sVal := []byte(fmt.Sprintf("distVal%d", i))
|
|
store := getFinalizeBlockStateCtx(suite.baseApp).KVStore(distKey1)
|
|
store.Set(sKey, sVal)
|
|
|
|
txs[i] = txBytes
|
|
}
|
|
|
|
_, err = suite.baseApp.FinalizeBlock(&abci.FinalizeBlockRequest{Height: 1, Txs: txs})
|
|
require.NoError(t, err)
|
|
_, err = suite.baseApp.Commit()
|
|
require.NoError(t, err)
|
|
|
|
// StartBlockData
|
|
require.Len(t, mockListener.startBlockData, 1)
|
|
require.Equal(t, uint64(1), mockListener.startBlockData[0].Height)
|
|
// TxData
|
|
txData := mockListener.txData
|
|
require.Len(t, txData, len(txs))
|
|
for i := 0; i < txCount; i++ {
|
|
require.Equal(t, int32(i), txData[i].TxIndex)
|
|
txBytes, err := txData[i].Bytes()
|
|
require.NoError(t, err)
|
|
require.Equal(t, txs[i], txBytes)
|
|
}
|
|
// KVPairData
|
|
require.Len(t, mockListener.kvPairData, 1)
|
|
updates := mockListener.kvPairData[0].Updates
|
|
for i := 0; i < txCount; i++ {
|
|
require.Equal(t, []byte(distKey1.Name()), updates[i].Actor)
|
|
require.Len(t, updates[i].StateChanges, 1)
|
|
sKey := []byte(fmt.Sprintf("distKey%d", i))
|
|
sVal := []byte(fmt.Sprintf("distVal%d", i))
|
|
require.Equal(t, sKey, updates[i].StateChanges[0].Key)
|
|
require.Equal(t, sVal, updates[i].StateChanges[0].Value)
|
|
}
|
|
// CommitData
|
|
require.Len(t, mockListener.commitData, 1)
|
|
// EventData
|
|
require.Len(t, mockListener.eventData, 1)
|
|
events := mockListener.eventData[0].Events
|
|
require.Len(t, events, 3+txCount*3)
|
|
|
|
for i := 0; i < 3; i++ {
|
|
require.Equal(t, int32(0), events[i].TxIndex)
|
|
require.Equal(t, int32(0), events[i].MsgIndex)
|
|
require.Equal(t, int32(1), events[i].EventIndex)
|
|
attrs, err := events[i].Attributes()
|
|
require.NoError(t, err)
|
|
require.Len(t, attrs, 2)
|
|
switch i {
|
|
case 0:
|
|
require.Equal(t, appdata.PreBlockStage, events[i].BlockStage)
|
|
require.Equal(t, "pre-block", events[i].Type)
|
|
case 1:
|
|
require.Equal(t, appdata.BeginBlockStage, events[i].BlockStage)
|
|
require.Equal(t, "begin-block", events[i].Type)
|
|
case 2:
|
|
require.Equal(t, appdata.EndBlockStage, events[i].BlockStage)
|
|
require.Equal(t, "end-block", events[i].Type)
|
|
}
|
|
}
|
|
|
|
for i := 3; i < 3+txCount*3; i++ {
|
|
require.Equal(t, appdata.TxProcessingStage, events[i].BlockStage)
|
|
require.Equal(t, int32(i/3), events[i].TxIndex)
|
|
switch i % 3 {
|
|
case 0:
|
|
require.Equal(t, "ante_handler", events[i].Type)
|
|
require.Equal(t, int32(0), events[i].MsgIndex)
|
|
require.Equal(t, int32(0), events[i].EventIndex)
|
|
attrs, err := events[i].Attributes()
|
|
require.NoError(t, err)
|
|
require.Len(t, attrs, 2)
|
|
case 1:
|
|
require.Equal(t, "message", events[i].Type)
|
|
require.Equal(t, int32(1), events[i].MsgIndex)
|
|
require.Equal(t, int32(1), events[i].EventIndex)
|
|
attrs, err := events[i].Attributes()
|
|
require.NoError(t, err)
|
|
require.Len(t, attrs, 5)
|
|
case 2:
|
|
require.Equal(t, "message", events[i].Type)
|
|
require.Equal(t, int32(1), events[i].MsgIndex)
|
|
require.Equal(t, int32(2), events[i].EventIndex)
|
|
attrs, err := events[i].Attributes()
|
|
require.NoError(t, err)
|
|
require.Len(t, attrs, 4)
|
|
}
|
|
}
|
|
}
|