package ibctesting import ( "bytes" "errors" abci "git.cw.tr/mukan-network/mukan-consensus/abci/types" transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types" channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types" ) // Path contains two endpoints representing two chains connected over IBC type Path struct { EndpointA *Endpoint EndpointB *Endpoint } // NewPath constructs an endpoint for each chain using the default values // for the endpoints. Each endpoint is updated to have a pointer to the // counterparty endpoint. func NewPath(chainA, chainB *TestChain) *Path { endpointA := NewDefaultEndpoint(chainA) endpointB := NewDefaultEndpoint(chainB) endpointA.Counterparty = endpointB endpointB.Counterparty = endpointA return &Path{ EndpointA: endpointA, EndpointB: endpointB, } } // NewTransferPath constructs a new path between each chain suitable for use with // the transfer module. func NewTransferPath(chainA, chainB *TestChain) *Path { path := NewPath(chainA, chainB) path.EndpointA.ChannelConfig.PortID = TransferPort path.EndpointB.ChannelConfig.PortID = TransferPort path.EndpointA.ChannelConfig.Version = transfertypes.V1 path.EndpointB.ChannelConfig.Version = transfertypes.V1 return path } // SetChannelOrdered sets the channel order for both endpoints to ORDERED. func (path *Path) SetChannelOrdered() { path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED } // DisableUniqueChannelIDs provides an opt-out way to not have all channel IDs be different // while testing. func (path *Path) DisableUniqueChannelIDs() *Path { path.EndpointA.disableUniqueChannelIDs = true path.EndpointB.disableUniqueChannelIDs = true return path } // RelayPacket attempts to relay the packet first on EndpointA and then on EndpointB // if EndpointA does not contain a packet commitment for that packet. An error is returned // if a relay step fails or the packet commitment does not exist on either endpoint. func (path *Path) RelayPacket(packet channeltypes.Packet) error { _, _, err := path.RelayPacketWithResults(packet) return err } // RelayPacketWithResults attempts to relay the packet first on EndpointA and then on EndpointB // if EndpointA does not contain a packet commitment for that packet. The function returns: // - The result of the packet receive transaction. // - The acknowledgement written on the receiving chain. // - An error if a relay step fails or the packet commitment does not exist on either endpoint. func (path *Path) RelayPacketWithResults(packet channeltypes.Packet) (*abci.ExecTxResult, []byte, error) { pc := path.EndpointA.Chain.App.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(path.EndpointA.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if bytes.Equal(pc, channeltypes.CommitPacket(packet)) { // packet found, relay from A to B if err := path.EndpointB.UpdateClient(); err != nil { return nil, nil, err } res, err := path.EndpointB.RecvPacketWithResult(packet) if err != nil { return nil, nil, err } ack, err := ParseAckFromEvents(res.Events) if err != nil { return nil, nil, err } if err := path.EndpointA.AcknowledgePacket(packet, ack); err != nil { return nil, nil, err } return res, ack, nil } pc = path.EndpointB.Chain.App.GetIBCKeeper().ChannelKeeper.GetPacketCommitment(path.EndpointB.Chain.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if bytes.Equal(pc, channeltypes.CommitPacket(packet)) { // packet found, relay B to A if err := path.EndpointA.UpdateClient(); err != nil { return nil, nil, err } res, err := path.EndpointA.RecvPacketWithResult(packet) if err != nil { return nil, nil, err } ack, err := ParseAckFromEvents(res.Events) if err != nil { return nil, nil, err } if err := path.EndpointB.AcknowledgePacket(packet, ack); err != nil { return nil, nil, err } return res, ack, nil } return nil, nil, errors.New("packet commitment does not exist on either endpoint for provided packet") } // Reversed returns a new path with endpoints reversed. func (path *Path) Reversed() *Path { reversedPath := *path reversedPath.EndpointA, reversedPath.EndpointB = path.EndpointB, path.EndpointA return &reversedPath } // Setup constructs a TM client, connection, and channel on both chains provided. It will // fail if any error occurs. func (path *Path) Setup() { path.SetupConnections() // channels can also be referenced through the returned connections path.CreateChannels() } // SetupV2 constructs clients on both sides and then provides the counterparties for both sides // This is all that is necessary for path setup with the IBC v2 protocol func (path *Path) SetupV2() { path.SetupClients() path.SetupCounterparties() } // SetupClients is a helper function to create clients on both chains. It assumes the // caller does not anticipate any errors. func (path *Path) SetupClients() { err := path.EndpointA.CreateClient() if err != nil { panic(err) } err = path.EndpointB.CreateClient() if err != nil { panic(err) } } // SetupCounterparties is a helper function to set the counterparties supporting IBC v2 on both // chains. It assumes the caller does not anticipate any errors. func (path *Path) SetupCounterparties() { if err := path.EndpointB.RegisterCounterparty(); err != nil { panic(err) } if err := path.EndpointA.RegisterCounterparty(); err != nil { panic(err) } } // SetupConnections is a helper function to create clients and the appropriate // connections on both the source and counterparty chain. It assumes the caller does not // anticipate any errors. func (path *Path) SetupConnections() { path.SetupClients() path.CreateConnections() } // CreateConnections constructs and executes connection handshake messages in order to create // OPEN connections on chainA and chainB. The function expects the connections to be // successfully opened otherwise testing will fail. func (path *Path) CreateConnections() { err := path.EndpointA.ConnOpenInit() if err != nil { panic(err) } err = path.EndpointB.ConnOpenTry() if err != nil { panic(err) } err = path.EndpointA.ConnOpenAck() if err != nil { panic(err) } err = path.EndpointB.ConnOpenConfirm() if err != nil { panic(err) } // ensure counterparty is up to date err = path.EndpointA.UpdateClient() if err != nil { panic(err) } } // CreateChannels constructs and executes channel handshake messages in order to create // OPEN channels on chainA and chainB. The function expects the channels to be successfully // opened otherwise testing will fail. func (path *Path) CreateChannels() { err := path.EndpointA.ChanOpenInit() if err != nil { panic(err) } err = path.EndpointB.ChanOpenTry() if err != nil { panic(err) } err = path.EndpointA.ChanOpenAck() if err != nil { panic(err) } err = path.EndpointB.ChanOpenConfirm() if err != nil { panic(err) } // ensure counterparty is up to date err = path.EndpointA.UpdateClient() if err != nil { panic(err) } }