Skip to content

Commit

Permalink
explicitly propagate events from cached contexts to original context …
Browse files Browse the repository at this point in the history
…in delaymsg endblocker (#393)

* explicitly propagate events from cached contexts to original context in delaymsg endblocker

* add test case that fails before code change and succeeds after
  • Loading branch information
tqin7 authored Sep 29, 2023
1 parent 906b91c commit 46011c0
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
19 changes: 17 additions & 2 deletions protocol/x/delaymsg/keeper/dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ func DispatchMessagesForBlock(k types.DelayMsgKeeper, ctx sdk.Context) {
return
}

// Maintain a list of events emitted by all delayed messages executed in this block.
// As message handlers create new event managers, such emitted events need to be
// explicitly propagated to the current context.
// Note: events in EndBlocker can be found in `end_block_events` in response from
// `/block_results` endpoint.
var events sdk.Events

// Execute all delayed messages scheduled for this block and delete them from the store.
for _, id := range blockMessageIds.Ids {
delayedMsg, found := k.GetMessage(ctx, id)
Expand All @@ -33,13 +40,21 @@ func DispatchMessagesForBlock(k types.DelayMsgKeeper, ctx sdk.Context) {

if err = abci.RunCached(ctx, func(ctx sdk.Context) error {
handler := k.Router().Handler(msg)
_, err := handler(ctx, msg)
return err
res, err := handler(ctx, msg)
if err != nil {
return err
}
// Append events emitted in message handler to `events`.
events = append(events, res.GetEvents()...)
return nil
}); err != nil {
k.Logger(ctx).Error("failed to execute delayed message with id %v: %v", id, err)
}
}

// Propagate events emitted in message handlers to current context.
ctx.EventManager().EmitEvents(events)

for _, id := range blockMessageIds.Ids {
if err := k.DeleteMessage(ctx, id); err != nil {
k.Logger(ctx).Error("failed to delete delayed message: %w", err)
Expand Down
58 changes: 57 additions & 1 deletion protocol/x/delaymsg/keeper/dispatch_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package keeper_test

import (
sdkmath "cosmossdk.io/math"
"fmt"
"reflect"
"testing"

sdkmath "cosmossdk.io/math"

"github.com/cometbft/cometbft/libs/log"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
cometbfttypes "github.com/cometbft/cometbft/types"
Expand Down Expand Up @@ -504,3 +506,57 @@ func TestSendDelayedCompleteBridgeMessage_Failure(t *testing.T) {
_, found = tApp.App.DelayMsgKeeper.GetBlockMessageIds(ctx, 2)
require.False(t, found)
}

// This test case verifies that events emitted from message executions are correctly
// propagated to the base context.
func TestDispatchMessagesForBlock_EventsArePropagated(t *testing.T) {
ctx, k, _, _, bankKeeper, _ := keepertest.DelayMsgKeepers(t)
// Mint coins to the bridge module account so that it has enough balance for completing bridges.
err := bankKeeper.MintCoins(ctx, bridgetypes.ModuleName, sdk.NewCoins(BridgeGenesisAccountBalance))
require.NoError(t, err)

// Delay a complete bridge message, which calls bank transfer that emits a transfer event.
bridgeEvent := bridgetypes.BridgeEvent{
Id: 1,
Coin: sdk.NewCoin("dv4tnt", sdkmath.NewInt(1_000)),
Address: constants.AliceAccAddress.String(),
EthBlockHeight: 0,
}
_, err = k.DelayMessageByBlocks(
ctx,
&bridgetypes.MsgCompleteBridge{
Authority: authtypes.NewModuleAddress(types.ModuleName).String(),
Event: bridgeEvent,
},
0,
)
require.NoError(t, err)

// Sanity check: messages appear for block 0.
blockMessageIds, found := k.GetBlockMessageIds(ctx, 0)
require.True(t, found)
require.Equal(t, []uint32{0}, blockMessageIds.Ids)

// Dispatch messages for block 0.
keeper.DispatchMessagesForBlock(k, ctx)

_, found = k.GetBlockMessageIds(ctx, 0)
require.False(t, found)

emittedEvents := ctx.EventManager().Events()
expectedTransferEvent := sdk.NewEvent(
"transfer",
sdk.NewAttribute("recipient", bridgeEvent.Address),
sdk.NewAttribute("sender", BridgeAccountAddress.String()),
sdk.NewAttribute("amount", bridgeEvent.Coin.String()),
)

// Verify that emitted events contains the expected transfer event exactly once.
foundExpectedTransferEvent := 0
for _, emittedEvent := range emittedEvents {
if reflect.DeepEqual(expectedTransferEvent, emittedEvent) {
foundExpectedTransferEvent++
}
}
require.Equal(t, 1, foundExpectedTransferEvent)
}

0 comments on commit 46011c0

Please sign in to comment.