Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
341 lines
12 KiB
Go
341 lines
12 KiB
Go
package ibctesting
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strconv"
|
|
|
|
"github.com/cosmos/gogoproto/proto"
|
|
testifysuite "github.com/stretchr/testify/suite"
|
|
|
|
abci "git.cw.tr/mukan-network/mukan-consensus/abci/types"
|
|
|
|
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
|
|
connectiontypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/03-connection/types"
|
|
channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types"
|
|
channeltypesv2 "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/v2/types"
|
|
)
|
|
|
|
// ParseClientIDFromEvents parses events emitted from a MsgCreateClient and returns the
|
|
// client identifier.
|
|
func ParseClientIDFromEvents(events []abci.Event) (string, error) {
|
|
for _, ev := range events {
|
|
if ev.Type == clienttypes.EventTypeCreateClient {
|
|
if attribute, found := attributeByKey(ev.Attributes, clienttypes.AttributeKeyClientID); found {
|
|
return attribute.Value, nil
|
|
}
|
|
}
|
|
}
|
|
return "", errors.New("client identifier event attribute not found")
|
|
}
|
|
|
|
// ParseConnectionIDFromEvents parses events emitted from a MsgConnectionOpenInit or
|
|
// MsgConnectionOpenTry and returns the connection identifier.
|
|
func ParseConnectionIDFromEvents(events []abci.Event) (string, error) {
|
|
for _, ev := range events {
|
|
if ev.Type == connectiontypes.EventTypeConnectionOpenInit ||
|
|
ev.Type == connectiontypes.EventTypeConnectionOpenTry {
|
|
if attribute, found := attributeByKey(ev.Attributes, connectiontypes.AttributeKeyConnectionID); found {
|
|
return attribute.Value, nil
|
|
}
|
|
}
|
|
}
|
|
return "", errors.New("connection identifier event attribute not found")
|
|
}
|
|
|
|
// ParseChannelIDFromEvents parses events emitted from a MsgChannelOpenInit or
|
|
// MsgChannelOpenTry or a MsgCreateChannel and returns the channel identifier.
|
|
func ParseChannelIDFromEvents(events []abci.Event) (string, error) {
|
|
for _, ev := range events {
|
|
if ev.Type == channeltypes.EventTypeChannelOpenInit || ev.Type == channeltypes.EventTypeChannelOpenTry {
|
|
if attribute, found := attributeByKey(ev.Attributes, channeltypes.AttributeKeyChannelID); found {
|
|
return attribute.Value, nil
|
|
}
|
|
}
|
|
}
|
|
return "", errors.New("channel identifier event attribute not found")
|
|
}
|
|
|
|
// ParseV1PacketFromEvents parses events emitted from a send packet and returns
|
|
// the first EventTypeSendPacket packet found.
|
|
// Returns an error if no packet is found.
|
|
func ParseV1PacketFromEvents(events []abci.Event) (channeltypes.Packet, error) {
|
|
packets, err := ParseIBCV1Packets(channeltypes.EventTypeSendPacket, events)
|
|
if err != nil {
|
|
return channeltypes.Packet{}, err
|
|
}
|
|
return packets[0], nil
|
|
}
|
|
|
|
// ParseRecvV1PacketFromEvents parses events emitted from a MsgRecvPacket and returns
|
|
// the first EventTypeRecvPacket packet found.
|
|
// Returns an error if no packet is found.
|
|
func ParseRecvV1PacketFromEvents(events []abci.Event) (channeltypes.Packet, error) {
|
|
packets, err := ParseIBCV1Packets(channeltypes.EventTypeRecvPacket, events)
|
|
if err != nil {
|
|
return channeltypes.Packet{}, err
|
|
}
|
|
return packets[0], nil
|
|
}
|
|
|
|
// ParseIBCV1Packets parses events and returns all the v1 packets found.
|
|
// Returns an error if no v1 packet is found.
|
|
func ParseIBCV1Packets(eventType string, events []abci.Event) ([]channeltypes.Packet, error) {
|
|
ferr := func(err error) ([]channeltypes.Packet, error) {
|
|
return nil, fmt.Errorf("ibctesting.ParseIBCV1Packets: %w", err)
|
|
}
|
|
var packets []channeltypes.Packet
|
|
for _, ev := range events {
|
|
if ev.Type != eventType {
|
|
continue
|
|
}
|
|
|
|
var packet channeltypes.Packet
|
|
for _, attr := range ev.Attributes {
|
|
switch attr.Key {
|
|
case channeltypes.AttributeKeyDataHex:
|
|
data, err := hex.DecodeString(attr.Value)
|
|
if err != nil {
|
|
return ferr(err)
|
|
}
|
|
packet.Data = data
|
|
case channeltypes.AttributeKeySequence:
|
|
seq, err := strconv.ParseUint(attr.Value, 10, 64)
|
|
if err != nil {
|
|
return ferr(err)
|
|
}
|
|
|
|
packet.Sequence = seq
|
|
|
|
case channeltypes.AttributeKeySrcPort:
|
|
packet.SourcePort = attr.Value
|
|
|
|
case channeltypes.AttributeKeySrcChannel:
|
|
packet.SourceChannel = attr.Value
|
|
|
|
case channeltypes.AttributeKeyDstPort:
|
|
packet.DestinationPort = attr.Value
|
|
|
|
case channeltypes.AttributeKeyDstChannel:
|
|
packet.DestinationChannel = attr.Value
|
|
|
|
case channeltypes.AttributeKeyTimeoutHeight:
|
|
height, err := clienttypes.ParseHeight(attr.Value)
|
|
if err != nil {
|
|
return ferr(err)
|
|
}
|
|
|
|
packet.TimeoutHeight = height
|
|
|
|
case channeltypes.AttributeKeyTimeoutTimestamp:
|
|
timestamp, err := strconv.ParseUint(attr.Value, 10, 64)
|
|
if err != nil {
|
|
return ferr(err)
|
|
}
|
|
|
|
packet.TimeoutTimestamp = timestamp
|
|
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
|
|
packets = append(packets, packet)
|
|
}
|
|
if len(packets) == 0 {
|
|
return ferr(errors.New("acknowledgement event attribute not found"))
|
|
}
|
|
return packets, nil
|
|
}
|
|
|
|
// ParseIBCV2Packets parses events and returns all the v2 packets found.
|
|
// Returns an error if no v2 packet is found.
|
|
func ParseIBCV2Packets(eventType string, events []abci.Event) ([]channeltypesv2.Packet, error) {
|
|
packets := make([]channeltypesv2.Packet, 0)
|
|
for _, event := range events {
|
|
if event.Type != eventType {
|
|
continue
|
|
}
|
|
|
|
var packet channeltypesv2.Packet
|
|
Loop:
|
|
for _, attr := range event.Attributes {
|
|
switch attr.Key {
|
|
// If we find a complete packet, we unmarshall it. We don't need to check for any other
|
|
// attributes from this event.
|
|
case channeltypesv2.AttributeKeyEncodedPacketHex:
|
|
data, err := hex.DecodeString(attr.Value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ibctesting.ParseIBCV2Packets: %w", err)
|
|
}
|
|
if err := proto.Unmarshal(data, &packet); err != nil {
|
|
return nil, fmt.Errorf("ibctesting.ParseIBCV2Packets: %w", err)
|
|
}
|
|
break Loop
|
|
|
|
case channeltypesv2.AttributeKeySequence:
|
|
seq, err := strconv.ParseUint(attr.Value, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ibctesting.ParseIBCV2Packets: %w", err)
|
|
}
|
|
packet.Sequence = seq
|
|
|
|
case channeltypesv2.AttributeKeyTimeoutTimestamp:
|
|
timestamp, err := strconv.ParseUint(attr.Value, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ibctesting.ParseIBCV2Packets: %w", err)
|
|
}
|
|
packet.TimeoutTimestamp = timestamp
|
|
|
|
case channeltypesv2.AttributeKeyDstClient:
|
|
packet.DestinationClient = attr.Value
|
|
|
|
case channeltypesv2.AttributeKeySrcClient:
|
|
packet.SourceClient = attr.Value
|
|
}
|
|
}
|
|
packets = append(packets, packet)
|
|
}
|
|
|
|
if len(packets) == 0 {
|
|
return nil, errors.New("no IBC v2 packets found in events")
|
|
}
|
|
|
|
return packets, nil
|
|
}
|
|
|
|
// ParseAckFromEvents parses events emitted from a MsgRecvPacket and returns the
|
|
// acknowledgement.
|
|
func ParseAckFromEvents(events []abci.Event) ([]byte, error) {
|
|
for _, ev := range events {
|
|
if ev.Type == channeltypes.EventTypeWriteAck {
|
|
if attribute, found := attributeByKey(ev.Attributes, channeltypes.AttributeKeyAckHex); found {
|
|
value, err := hex.DecodeString(attribute.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return value, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, errors.New("acknowledgement event attribute not found")
|
|
}
|
|
|
|
// ParseProposalIDFromEvents parses events emitted from MsgSubmitProposal and returns proposalID
|
|
func ParseProposalIDFromEvents(events []abci.Event) (uint64, error) {
|
|
for _, event := range events {
|
|
if attribute, found := attributeByKey(event.Attributes, "proposal_id"); found {
|
|
return strconv.ParseUint(attribute.Value, 10, 64)
|
|
}
|
|
}
|
|
return 0, errors.New("proposalID event attribute not found")
|
|
}
|
|
|
|
// ParsePacketSequenceFromEvents parses events emitted from MsgRecvPacket and returns the packet sequence
|
|
func ParsePacketSequenceFromEvents(events []abci.Event) (uint64, error) {
|
|
for _, event := range events {
|
|
if attribute, found := attributeByKey(event.Attributes, "packet_sequence"); found {
|
|
return strconv.ParseUint(attribute.Value, 10, 64)
|
|
}
|
|
}
|
|
return 0, errors.New("packet sequence event attribute not found")
|
|
}
|
|
|
|
// AssertEvents asserts that expected events are present in the actual events.
|
|
func AssertEvents(
|
|
suite *testifysuite.Suite,
|
|
expected []abci.Event,
|
|
actual []abci.Event,
|
|
) {
|
|
foundEvents := make(map[int]bool)
|
|
|
|
for i, expectedEvent := range expected {
|
|
for _, actualEvent := range actual {
|
|
if shouldProcessEvent(expectedEvent, actualEvent) {
|
|
attributeMatch := true
|
|
for _, expectedAttr := range expectedEvent.Attributes {
|
|
// any expected attributes that are not contained in the actual events will cause this event
|
|
// not to match
|
|
attributeMatch = attributeMatch && containsAttribute(actualEvent.Attributes, expectedAttr.Key, expectedAttr.Value)
|
|
}
|
|
|
|
if attributeMatch {
|
|
foundEvents[i] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for i, expectedEvent := range expected {
|
|
suite.Require().True(foundEvents[i], "event: %s was not found in events", expectedEvent.Type)
|
|
}
|
|
}
|
|
|
|
// shouldProcessEvent returns true if the given expected event should be processed based on event type.
|
|
func shouldProcessEvent(expectedEvent abci.Event, actualEvent abci.Event) bool {
|
|
if expectedEvent.Type != actualEvent.Type {
|
|
return false
|
|
}
|
|
// the actual event will have an extra attribute added automatically
|
|
// by Cosmos SDK since v0.50, that's why we subtract 1 when comparing
|
|
// with the number of attributes in the expected event.
|
|
if containsAttributeKey(actualEvent.Attributes, "msg_index") {
|
|
return len(expectedEvent.Attributes) == len(actualEvent.Attributes)-1
|
|
}
|
|
|
|
return len(expectedEvent.Attributes) == len(actualEvent.Attributes)
|
|
}
|
|
|
|
// containsAttribute returns true if the given key/value pair is contained in the given attributes.
|
|
// NOTE: this ignores the indexed field, which can be set or unset depending on how the events are retrieved.
|
|
func containsAttribute(attrs []abci.EventAttribute, key, value string) bool {
|
|
return slices.ContainsFunc(attrs, func(attr abci.EventAttribute) bool {
|
|
return attr.Key == key && attr.Value == value
|
|
})
|
|
}
|
|
|
|
// containsAttributeKey returns true if the given key is contained in the given attributes.
|
|
func containsAttributeKey(attrs []abci.EventAttribute, key string) bool {
|
|
_, found := attributeByKey(attrs, key)
|
|
return found
|
|
}
|
|
|
|
// attributeByKey returns the event attribute's value keyed by the given key and a boolean indicating its presence in the given attributes.
|
|
func attributeByKey(attributes []abci.EventAttribute, key string) (abci.EventAttribute, bool) {
|
|
idx := slices.IndexFunc(attributes, func(a abci.EventAttribute) bool { return a.Key == key })
|
|
if idx == -1 {
|
|
return abci.EventAttribute{}, false
|
|
}
|
|
return attributes[idx], true
|
|
}
|
|
|
|
// ParsePacketFromEvents parses events emitted from a send packet and returns
|
|
// the first EventTypeSendPacket packet found.
|
|
// Returns an error if no packet is found.
|
|
//
|
|
// Deprecated: This function will be removed in the next major release. Use
|
|
// ParseV1PacketFromEvents instead
|
|
func ParsePacketFromEvents(events []abci.Event) (channeltypes.Packet, error) {
|
|
return ParseV1PacketFromEvents(events)
|
|
}
|
|
|
|
// ParseRecvPacketFromEvents parses events emitted from a MsgRecvPacket and returns
|
|
// the first EventTypeRecvPacket packet found.
|
|
// Returns an error if no packet is found.
|
|
//
|
|
// Deprecated: This function will be removed in the next major release. Use
|
|
// ParseRecvV1PacketFromEvents instead
|
|
func ParseRecvPacketFromEvents(events []abci.Event) (channeltypes.Packet, error) {
|
|
return ParseRecvV1PacketFromEvents(events)
|
|
}
|
|
|
|
// ParsePacketsFromEvents parses events emitted from a MsgRecvPacket and returns
|
|
// all the packets found.
|
|
// Returns an error if no packet is found.
|
|
//
|
|
// Deprecated: This function will be removed in the next major release. Use ParseIBCV1Packets instead.
|
|
func ParsePacketsFromEvents(eventType string, events []abci.Event) ([]channeltypes.Packet, error) {
|
|
return ParseIBCV1Packets(eventType, events)
|
|
}
|