-
Notifications
You must be signed in to change notification settings - Fork 581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement v2 RecvPacket rpc handler #7421
base: feat/ibc-eureka
Are you sure you want to change the base?
Changes from all commits
b7d3c74
6761671
0411d52
483606d
b1ce0c6
33d9e6a
999c04e
8c0ce79
9dc9b67
a8cd088
dbc39b0
bf2927d
acbfb8c
0a3a588
352749e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package keeper | ||
|
||
import ( | ||
"context" | ||
|
||
hostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2" | ||
) | ||
|
||
// GetPacketAcknowledgement fetches the packet acknowledgement from the store. | ||
func (k *Keeper) GetPacketAcknowledgement(ctx context.Context, sourceID string, sequence uint64) []byte { | ||
store := k.storeService.OpenKVStore(ctx) | ||
bz, err := store.Get(hostv2.PacketAcknowledgementKey(sourceID, sequence)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return bz | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,13 +2,16 @@ package keeper | |
|
||
import ( | ||
"context" | ||
"slices" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
channeltypesv1 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types" | ||
channeltypesv2 "github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types" | ||
telemetryv2 "github.com/cosmos/ibc-go/v9/modules/core/internal/v2/telemetry" | ||
coretypes "github.com/cosmos/ibc-go/v9/modules/core/types" | ||
) | ||
|
||
var _ channeltypesv2.MsgServer = &Keeper{} | ||
|
@@ -80,30 +83,80 @@ func (k *Keeper) Acknowledgement(ctx context.Context, msg *channeltypesv2.MsgAck | |
// RecvPacket implements the PacketMsgServer RecvPacket method. | ||
func (k *Keeper) RecvPacket(ctx context.Context, msg *channeltypesv2.MsgRecvPacket) (*channeltypesv2.MsgRecvPacketResponse, error) { | ||
sdkCtx := sdk.UnwrapSDKContext(ctx) | ||
err := k.recvPacket(ctx, msg.Packet, msg.ProofCommitment, msg.ProofHeight) | ||
if err != nil { | ||
sdkCtx.Logger().Error("receive packet failed", "source-channel", msg.Packet.SourceChannel, "dest-channel", msg.Packet.DestinationChannel, "error", errorsmod.Wrap(err, "send packet failed")) | ||
return nil, errorsmod.Wrapf(err, "receive packet failed for source id: %s and destination id: %s", msg.Packet.SourceChannel, msg.Packet.DestinationChannel) | ||
} | ||
|
||
signer, err := sdk.AccAddressFromBech32(msg.Signer) | ||
if err != nil { | ||
sdkCtx.Logger().Error("receive packet failed", "error", errorsmod.Wrap(err, "invalid address for msg Signer")) | ||
return nil, errorsmod.Wrap(err, "invalid address for msg Signer") | ||
} | ||
|
||
_ = signer | ||
// Perform TAO verification | ||
// | ||
// If the packet was already received, perform a no-op | ||
// Use a cached context to prevent accidental state changes | ||
cacheCtx, writeFn := sdkCtx.CacheContext() | ||
err = k.recvPacket(cacheCtx, msg.Packet, msg.ProofCommitment, msg.ProofHeight) | ||
|
||
// TODO: implement once app router is wired up. | ||
// https://github.com/cosmos/ibc-go/issues/7384 | ||
// for _, pd := range packet.PacketData { | ||
// cbs := k.PortKeeper.AppRouter.Route(pd.SourcePort) | ||
// err := cbs.OnRecvPacket(ctx, packet, msg.ProofCommitment, msg.ProofHeight, signer) | ||
// if err != nil { | ||
// return nil, err | ||
// } | ||
// } | ||
switch err { | ||
case nil: | ||
writeFn() | ||
case channeltypesv1.ErrNoOpMsg: | ||
// no-ops do not need event emission as they will be ignored | ||
sdkCtx.Logger().Debug("no-op on redundant relay", "source-channel", msg.Packet.SourceChannel) | ||
return &channeltypesv2.MsgRecvPacketResponse{Result: channeltypesv1.NOOP}, nil | ||
default: | ||
sdkCtx.Logger().Error("receive packet failed", "source-channel", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "receive packet verification failed")) | ||
return nil, errorsmod.Wrap(err, "receive packet verification failed") | ||
} | ||
|
||
// build up the recv results for each application callback. | ||
ack := channeltypesv2.Acknowledgement{ | ||
AcknowledgementResults: []channeltypesv2.AcknowledgementResult{}, | ||
} | ||
|
||
for _, pd := range msg.Packet.Data { | ||
// Cache context so that we may discard state changes from callback if the acknowledgement is unsuccessful. | ||
cacheCtx, writeFn = sdkCtx.CacheContext() | ||
cb := k.Router.Route(pd.DestinationPort) | ||
res := cb.OnRecvPacket(cacheCtx, msg.Packet.SourceChannel, msg.Packet.DestinationChannel, pd, signer) | ||
|
||
if res.Status != channeltypesv2.PacketStatus_Failure { | ||
// write application state changes for asynchronous and successful acknowledgements | ||
writeFn() | ||
} else { | ||
// Modify events in cached context to reflect unsuccessful acknowledgement | ||
sdkCtx.EventManager().EmitEvents(convertToErrorEvents(cacheCtx.EventManager().Events())) | ||
} | ||
|
||
ack.AcknowledgementResults = append(ack.AcknowledgementResults, channeltypesv2.AcknowledgementResult{ | ||
AppName: pd.DestinationPort, | ||
RecvPacketResult: res, | ||
}) | ||
} | ||
|
||
// note this should never happen as the packet data would have had to be empty. | ||
if len(ack.AcknowledgementResults) == 0 { | ||
sdkCtx.Logger().Error("receive packet failed", "source-channel", msg.Packet.SourceChannel, "error", errorsmod.Wrap(err, "invalid acknowledgement results")) | ||
return &channeltypesv2.MsgRecvPacketResponse{Result: channeltypesv1.FAILURE}, nil | ||
} | ||
|
||
// NOTE: TBD how we will handle async acknowledgements with more than one packet data. | ||
isAsync := slices.ContainsFunc(ack.AcknowledgementResults, func(ackResult channeltypesv2.AcknowledgementResult) bool { | ||
return ackResult.RecvPacketResult.Status == channeltypesv2.PacketStatus_Async | ||
}) | ||
|
||
if !isAsync { | ||
// Set packet acknowledgement only if the acknowledgement is not async. | ||
// NOTE: IBC applications modules may call the WriteAcknowledgement asynchronously if the | ||
// acknowledgement is async. | ||
if err := k.WriteAcknowledgement(ctx, msg.Packet, ack); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
defer telemetryv2.ReportRecvPacket(msg.Packet) | ||
|
||
sdkCtx.Logger().Info("receive packet callback succeeded", "source-channel", msg.Packet.SourceChannel, "dest-channel", msg.Packet.DestinationChannel, "result", channeltypesv1.SUCCESS.String()) | ||
return &channeltypesv2.MsgRecvPacketResponse{Result: channeltypesv1.SUCCESS}, nil | ||
} | ||
|
||
|
@@ -135,3 +188,24 @@ func (k *Keeper) Timeout(ctx context.Context, timeout *channeltypesv2.MsgTimeout | |
|
||
return &channeltypesv2.MsgTimeoutResponse{Result: channeltypesv1.SUCCESS}, nil | ||
} | ||
|
||
// convertToErrorEvents converts all events to error events by appending the | ||
// error attribute prefix to each event's attribute key. | ||
// TODO: https://github.com/cosmos/ibc-go/issues/7436 | ||
func convertToErrorEvents(events sdk.Events) sdk.Events { | ||
if events == nil { | ||
return nil | ||
} | ||
|
||
newEvents := make(sdk.Events, len(events)) | ||
for i, event := range events { | ||
newAttributes := make([]sdk.Attribute, len(event.Attributes)) | ||
for j, attribute := range event.Attributes { | ||
newAttributes[j] = sdk.NewAttribute(coretypes.ErrorAttributeKeyPrefix+attribute.Key, attribute.Value) | ||
} | ||
|
||
newEvents[i] = sdk.NewEvent(coretypes.ErrorAttributeKeyPrefix+event.Type, newAttributes...) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can avoid having the newEvents := make(sdk.Events, len(events))
for i, event := range events {
newEvents[i] = sdk.NewEvent(coretypes.ErrorAttributeKeyPrefix+event.Type)
for _, attribute := range event.Attributes {
newEvents[i].AppendAttributes(sdk.NewAttribute(coretypes.ErrorAttributeKeyPrefix+attribute.Key, attribute.Value))
}
} WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was just copy pasted from the existing fn in v1 msg_server.go, I'll create another issue to remove the duplication, and maybe we can make these changes here? I'll link this comment in the issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah sure no big deal! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
return newEvents | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed one thing: this case can never be true, since we check for
err != nil
above. I believe this is supposed to be a differenterr
(the one at line 92? But in that case we still don't need thiscase
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
really nice catch! Also highlights the missing NoOp case, I'll push a fix for this