-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add Eureka receive packet entrypoint call
- Loading branch information
1 parent
2b4efde
commit 3fe0582
Showing
12 changed files
with
300 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package e2e_test | ||
|
||
import ( | ||
"testing" | ||
|
||
sdkmath "cosmossdk.io/math" | ||
ibcfee "github.com/cosmos/ibc-go/v10/modules/apps/29-fee/types" | ||
ibctransfertypes "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types" | ||
channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" | ||
ibctesting "github.com/cosmos/ibc-go/v10/testing" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/CosmWasm/wasmd/app" | ||
"github.com/CosmWasm/wasmd/tests/e2e" | ||
wasmibctesting "github.com/CosmWasm/wasmd/tests/wasmibctesting" | ||
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" | ||
mockv2 "github.com/cosmos/ibc-go/v10/testing/mock/v2" | ||
) | ||
|
||
func TestEurekaReceiveEntrypoint(t *testing.T) { | ||
coord := wasmibctesting.NewCoordinator(t, 2) | ||
chainA := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1))) | ||
chainB := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(2))) | ||
contractAddr := e2e.InstantiateStargateReflectContract(t, chainA) | ||
chainA.Fund(contractAddr, sdkmath.NewIntFromUint64(1_000_000_000)) | ||
marshaler := app.MakeEncodingConfig(t).Codec | ||
|
||
contractCode := chainA.StoreCodeFile("./testdata/eureka.wasm").CodeID | ||
contractAddrA := chainA.InstantiateContract(contractCode, []byte(`{}`)) | ||
contractPortA := wasmkeeper.PortIDForContract(contractAddrA) | ||
contractPortB := "wasm.ChainBContractAddr" | ||
|
||
require.NotEmpty(t, contractAddrA) | ||
|
||
path := wasmibctesting.NewWasmPath(chainA, chainB) | ||
path.EndpointA.ChannelConfig = &ibctesting.ChannelConfig{ | ||
PortID: contractPortA, | ||
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.V1})), | ||
Order: channeltypes.UNORDERED, | ||
} | ||
path.EndpointB.ChannelConfig = &ibctesting.ChannelConfig{ | ||
PortID: contractPortB, | ||
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.V1})), | ||
Order: channeltypes.UNORDERED, | ||
} | ||
|
||
// TODO tkulik: Is it necessary? | ||
path.EndpointA.Counterparty.ChannelConfig = &ibctesting.ChannelConfig{ | ||
PortID: contractPortB, | ||
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.V1})), | ||
Order: channeltypes.UNORDERED, | ||
} | ||
path.EndpointB.Counterparty.ChannelConfig = &ibctesting.ChannelConfig{ | ||
PortID: contractPortA, | ||
Version: string(marshaler.MustMarshalJSON(&ibcfee.Metadata{FeeVersion: ibcfee.Version, AppVersion: ibctransfertypes.V1})), | ||
Order: channeltypes.UNORDERED, | ||
} | ||
|
||
path.Path.SetupV2() | ||
|
||
timeoutTimestamp := chainA.GetTimeoutTimestampSecs() | ||
|
||
// TODO tkulik: Not sure about if endpoint A or B should be source | ||
_, err := path.EndpointA.MsgSendPacket(timeoutTimestamp, mockv2.NewMockPayload(contractPortA, contractPortB)) | ||
require.NoError(t, err) | ||
|
||
// eurekaMsg := &wasmvmtypes.EurekaMsg{ | ||
// SendPacket: &wasmvmtypes.EurekaSendPacketMsg{ | ||
// Payloads: []wasmvmtypes.EurekaPayload{{ | ||
// DestinationPort: "port-1", | ||
// Version: "v1", | ||
// Encoding: icatypes.EncodingProto3JSON, | ||
// Value: []byte{}, | ||
// }}, | ||
// ChannelID: "channel-1", | ||
// Timeout: 100, | ||
// }, | ||
// } | ||
|
||
// _, err = chain.SendMsgs(&eurekaMsg) | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,5 +18,6 @@ func BuiltInCapabilities() []string { | |
"cosmwasm_2_0", | ||
"cosmwasm_2_1", | ||
"cosmwasm_2_2", | ||
"eureka", | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package keeper | ||
|
||
import ( | ||
"time" | ||
|
||
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types" | ||
channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" | ||
ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
|
||
"github.com/cosmos/cosmos-sdk/telemetry" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/CosmWasm/wasmd/x/wasm/types" | ||
channeltypesv2 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/v2/types" | ||
ibcapi "github.com/cosmos/ibc-go/v10/modules/core/api" | ||
) | ||
|
||
var _ ibcapi.IBCModule = EurekaHandler{} | ||
|
||
// internal interface that is implemented by ibc middleware | ||
type appVersionGetter interface { | ||
// GetAppVersion returns the application level version with all middleware data stripped out | ||
GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) | ||
} | ||
|
||
type EurekaHandler struct { | ||
keeper types.EurekaContractKeeper | ||
} | ||
|
||
func NewEurekaHandler(keeper types.EurekaContractKeeper) EurekaHandler { | ||
return EurekaHandler{ | ||
keeper: keeper, | ||
} | ||
} | ||
|
||
func (module EurekaHandler) OnSendPacket( | ||
ctx sdk.Context, | ||
sourceClient string, | ||
destinationClient string, | ||
sequence uint64, | ||
payload channeltypesv2.Payload, | ||
signer sdk.AccAddress, | ||
) error { | ||
return nil | ||
} | ||
|
||
func (module EurekaHandler) OnRecvPacket( | ||
ctx sdk.Context, | ||
sourceClient string, | ||
destinationClient string, | ||
sequence uint64, | ||
payload channeltypesv2.Payload, | ||
relayer sdk.AccAddress, | ||
) channeltypesv2.RecvPacketResult { | ||
contractAddr, err := ContractFromPortID(payload.DestinationPort) | ||
if err != nil { | ||
// this must not happen as ports were registered before | ||
panic(errorsmod.Wrapf(err, "contract port id")) | ||
} | ||
|
||
em := sdk.NewEventManager() | ||
msg := wasmvmtypes.EurekaPacketReceiveMsg{Packet: newEurekaPacket(payload), Relayer: relayer.String()} | ||
|
||
ack, err := module.keeper.OnRecvEurekaPacket(ctx.WithEventManager(em), contractAddr, msg) | ||
|
||
if err != nil { | ||
ack = CreateErrorAcknowledgement(err) | ||
// the state gets reverted, so we drop all captured events | ||
} else if ack == nil || ack.Success() { | ||
// emit all contract and submessage events on success | ||
// nil ack is a success case, see: https://github.com/cosmos/ibc-go/blob/v7.0.0/modules/core/keeper/msg_server.go#L453 | ||
ctx.EventManager().EmitEvents(em.Events()) | ||
} | ||
types.EmitAcknowledgementEvent(ctx, contractAddr, ack, err) | ||
return ack | ||
} | ||
|
||
func (module EurekaHandler) OnTimeoutPacket( | ||
ctx sdk.Context, | ||
sourceClient string, | ||
destinationClient string, | ||
sequence uint64, | ||
payload channeltypesv2.Payload, | ||
relayer sdk.AccAddress, | ||
) error { | ||
return nil | ||
} | ||
|
||
func (module EurekaHandler) OnAcknowledgementPacket( | ||
ctx sdk.Context, | ||
sourceClient string, | ||
destinationClient string, | ||
sequence uint64, | ||
acknowledgement []byte, | ||
payload channeltypesv2.Payload, | ||
relayer sdk.AccAddress, | ||
) error { | ||
return nil | ||
} | ||
|
||
// The method calls the contract to process the incoming Eureka packet. The contract fully owns the data processing and | ||
// returns the acknowledgement data for the chain level. This allows custom applications and protocols on top | ||
// of IBC Eureka. | ||
func (k Keeper) OnRecvEurekaPacket( | ||
ctx sdk.Context, | ||
contractAddr sdk.AccAddress, | ||
msg wasmvmtypes.EurekaPacketReceiveMsg, | ||
) (ibcexported.Acknowledgement, error) { | ||
defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "ibc-recv-packet") | ||
contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
env := types.NewEnv(ctx, contractAddr) | ||
querier := k.newQueryHandler(ctx, contractAddr) | ||
|
||
gasLeft := k.runtimeGasForContract(ctx) | ||
res, gasUsed, execErr := k.wasmVM.EUPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gasLeft, costJSONDeserialization) | ||
k.consumeRuntimeGas(ctx, gasUsed) | ||
if execErr != nil { | ||
panic(execErr) // let the contract fully abort an IBC packet receive. | ||
// Throwing a panic here instead of an error ack will revert | ||
// all state downstream and not persist any data in ibc-go. | ||
// This can be triggered by throwing a panic in the contract | ||
} | ||
if res == nil { | ||
// If this gets executed, that's a bug in wasmvm | ||
return nil, errorsmod.Wrap(types.ErrVMError, "internal wasmvm error") | ||
} | ||
if res.Err != "" { | ||
// return error ACK with non-redacted contract message, state will be reverted | ||
return channeltypes.Acknowledgement{ | ||
Response: &channeltypes.Acknowledgement_Error{Error: res.Err}, | ||
}, nil | ||
} | ||
// note submessage reply results can overwrite the `Acknowledgement` data | ||
data, err := k.handleContractResponse(ctx, contractAddr, contractInfo.IBCPortID, res.Ok.Messages, res.Ok.Attributes, res.Ok.Acknowledgement, res.Ok.Events) | ||
if err != nil { | ||
// submessage errors result in error ACK with state reverted. Error message is redacted | ||
return nil, err | ||
} | ||
|
||
// TODO tkulik: What about this? Should we support async? | ||
// if data == nil { | ||
// // Protocol might never write acknowledgement or contract | ||
// // wants async acknowledgements, we don't know. | ||
// // So store the packet for later. | ||
// err = k.StoreAsyncAckPacket(ctx, convertPacket(msg.Packet)) | ||
// if err != nil { | ||
// return nil, err | ||
// } | ||
// return nil, nil | ||
// } | ||
|
||
// success ACK, state will be committed | ||
return ContractConfirmStateAck(data), nil | ||
} | ||
|
||
func newEurekaPacket(payload channeltypesv2.Payload) wasmvmtypes.EurekaPayload { | ||
return wasmvmtypes.EurekaPayload{ | ||
DestinationPort: payload.DestinationPort, | ||
Version: payload.Version, | ||
Encoding: payload.Encoding, | ||
Value: payload.Value, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters