diff --git a/relayer/chains/cosmos/message_handlers.go b/relayer/chains/cosmos/message_handlers.go index db9634ac74..dc285c824f 100644 --- a/relayer/chains/cosmos/message_handlers.go +++ b/relayer/chains/cosmos/message_handlers.go @@ -3,7 +3,6 @@ package cosmos import ( "context" "encoding/hex" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/cosmos/relayer/v2/relayer/processor" @@ -82,6 +81,7 @@ func (ccp *CosmosChainProcessor) handleChannelMessage(eventType string, ci provi Open: false, ConnectionHops: ci.ConnectionHops(), }) + } } else { switch eventType { diff --git a/relayer/chains/cosmos/multihop.go b/relayer/chains/cosmos/multihop.go index 46148133b9..b1dffd6cb8 100644 --- a/relayer/chains/cosmos/multihop.go +++ b/relayer/chains/cosmos/multihop.go @@ -9,7 +9,6 @@ import ( commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/cosmos/ibc-go/v7/modules/core/multihop" - "github.com/cosmos/relayer/v2/relayer/provider" ) var _ multihop.Endpoint = (*endpoint)(nil) @@ -148,9 +147,9 @@ func (e endpoint) Counterparty() multihop.Endpoint { return e.counterparty } -func newEndpoint(provider provider.ChainProvider, clientID, connectionID string) multihop.Endpoint { +func (cc *CosmosProvider) newEndpoint(clientID, connectionID string) multihop.Endpoint { return &endpoint{ - provider: provider.(*CosmosProvider), + provider: cc, clientID: clientID, connectionID: connectionID, } diff --git a/relayer/chains/cosmos/provider.go b/relayer/chains/cosmos/provider.go index b990f6dcd7..c897d07e57 100644 --- a/relayer/chains/cosmos/provider.go +++ b/relayer/chains/cosmos/provider.go @@ -94,7 +94,7 @@ func (pc CosmosProviderConfig) NewProvider(log *zap.Logger, homepath string, deb // TODO: this is a bit of a hack, we should probably have a better way to inject modules Cdc: MakeCodec(pc.Modules, pc.ExtraCodecs), - chanPaths: map[string]*multihop.ChanPath{}, + chanPaths: map[string]multihop.ChanPath{}, } return cp, nil @@ -126,14 +126,22 @@ type CosmosProvider struct { cometLegacyEncoding bool // chanPaths tracks paths for multi-hop proofs - chanPaths map[string]*multihop.ChanPath + chanPaths map[string]multihop.ChanPath } -func (cc *CosmosProvider) AddChanPath(connectionHops []string, chanPath *multihop.ChanPath) { +func (cc *CosmosProvider) MultihopEndpoint(clientID, connectionID string) multihop.Endpoint { + return cc.newEndpoint(clientID, connectionID) +} + +func (cc *CosmosProvider) SetMultihopCounterparty(ep, counterparty multihop.Endpoint) { + ep.(*endpoint).counterparty = counterparty.(*endpoint) +} + +func (cc *CosmosProvider) AddChanPath(connectionHops []string, chanPath multihop.ChanPath) { cc.chanPaths[chantypes.FormatConnectionID(connectionHops)] = chanPath } -func (cc *CosmosProvider) GetChanPath(connectionHops []string) *multihop.ChanPath { +func (cc *CosmosProvider) GetChanPath(connectionHops []string) multihop.ChanPath { return cc.chanPaths[chantypes.FormatConnectionID(connectionHops)] } diff --git a/relayer/chains/cosmos/tx.go b/relayer/chains/cosmos/tx.go index 37cb0310cb..6d783635ab 100644 --- a/relayer/chains/cosmos/tx.go +++ b/relayer/chains/cosmos/tx.go @@ -538,7 +538,7 @@ func (cc *CosmosProvider) ValidatePacket(msgTransfer provider.PacketInfo, latest } func (cc *CosmosProvider) newProof(proof []byte, connectionHops []string) ([]byte, error) { - if len(connectionHops) == 0 { + if len(connectionHops) < 2 { return proof, nil } chanPath := cc.GetChanPath(connectionHops) diff --git a/relayer/channel.go b/relayer/channel.go index f6ea05d44d..be20cca080 100644 --- a/relayer/channel.go +++ b/relayer/channel.go @@ -12,13 +12,12 @@ import ( "go.uber.org/zap" ) -func newPathEnd(pathName, chainID, clientID string) processor.PathEnd { - return processor.NewPathEnd(pathName, chainID, clientID, "", []processor.ChainChannelKey{}) +func newPathEnd(pathName, chainID, clientID, connectionID string) processor.PathEnd { + return processor.NewPathEnd(pathName, chainID, clientID, connectionID, "", []processor.ChainChannelKey{}) } func newRelayPathEnds(pathName string, hops []*Chain) ([]*processor.PathEnd, []*processor.PathEnd) { relayPathEndsSrcToDst := make([]*processor.PathEnd, len(hops)) - relayPathEndsDstToSrc := make([]*processor.PathEnd, len(hops)) // RelayPathEnds are set in user friendly order so they're just listed as they appear left to right without // acounting for directionality. So for a 1 hop case they would look like this: // A -> B (BA, BC) -> C @@ -26,10 +25,17 @@ func newRelayPathEnds(pathName string, hops []*Chain) ([]*processor.PathEnd, []* // BC, BA // Hence the index reversal in the call to newPathEnd(). for i, hop := range hops { - pathEnd1 := newPathEnd(pathName, hop.RelayPathEnds[1].ChainID, hop.RelayPathEnds[1].ClientID) - pathEnd2 := newPathEnd(pathName, hop.RelayPathEnds[0].ChainID, hop.RelayPathEnds[0].ClientID) + relayPath1 := hop.RelayPathEnds[1] + pathEnd1 := newPathEnd(pathName, relayPath1.ChainID, relayPath1.ClientID, relayPath1.ConnectionID) relayPathEndsSrcToDst[i] = &pathEnd1 - relayPathEndsDstToSrc[i] = &pathEnd2 + } + var relayPathEndsDstToSrc []*processor.PathEnd + // TODO: is it ok to reverse here? + for i := len(hops) - 1; i >= 0; i-- { + hop := hops[i] + relayPath2 := hop.RelayPathEnds[0] + pathEnd2 := newPathEnd(pathName, relayPath2.ChainID, relayPath2.ClientID, relayPath2.ConnectionID) + relayPathEndsDstToSrc = append(relayPathEndsDstToSrc, &pathEnd2) } return relayPathEndsSrcToDst, relayPathEndsDstToSrc } @@ -74,11 +80,17 @@ func (c *Chain) CreateOpenChannels( ctx, cancel := context.WithTimeout(ctx, processorTimeout) defer cancel() + hopConnectionIDs := make([]string, len(hops)+1) + hopConnectionIDs[0] = c.PathEnd.ConnectionID + for i, hop := range hops { + hopConnectionIDs[i+1] = hop.RelayPathEnds[1].ConnectionID + } + relayPathEndsSrcToDst, relayPathEndsDstToSrc := newRelayPathEnds(pathName, hops) pp := processor.NewPathProcessor( c.log, - newPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID), - newPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID), + newPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID, c.PathEnd.ConnectionID), + newPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID, dst.PathEnd.ConnectionID), relayPathEndsSrcToDst, relayPathEndsDstToSrc, nil, @@ -93,11 +105,6 @@ func (c *Chain) CreateOpenChannels( zap.String("dst_chain_id", dst.PathEnd.ChainID), zap.String("dst_port_id", dstPortID), ) - hopConnectionIDs := make([]string, len(hops)+1) - hopConnectionIDs[0] = c.PathEnd.ConnectionID - for i, hop := range hops { - hopConnectionIDs[i+1] = hop.RelayPathEnds[1].ConnectionID - } connectionHops := chantypes.FormatConnectionID(hopConnectionIDs) openInitMsg := &processor.ChannelMessage{ ChainID: c.PathEnd.ChainID, @@ -170,8 +177,8 @@ func (c *Chain) CloseChannel( WithChainProcessors(chainProcessors...). WithPathProcessors(processor.NewPathProcessor( c.log, - newPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID), - newPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID), + newPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID, c.PathEnd.ConnectionID), + newPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID, dst.PathEnd.ConnectionID), relayPathEndsSrcToDst, relayPathEndsDstToSrc, nil, diff --git a/relayer/connection.go b/relayer/connection.go index 036d064c2f..75e063ded0 100644 --- a/relayer/connection.go +++ b/relayer/connection.go @@ -32,11 +32,10 @@ func (c *Chain) CreateOpenConnections( ctx, cancel := context.WithTimeout(ctx, processorTimeout) defer cancel() - // TODO: account for hops pp := processor.NewPathProcessor( c.log, - processor.NewPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID, "", []processor.ChainChannelKey{}), - processor.NewPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID, "", []processor.ChainChannelKey{}), + processor.NewPathEnd(pathName, c.PathEnd.ChainID, c.PathEnd.ClientID, "", "", []processor.ChainChannelKey{}), + processor.NewPathEnd(pathName, dst.PathEnd.ChainID, dst.PathEnd.ClientID, "", "", []processor.ChainChannelKey{}), nil, nil, nil, diff --git a/relayer/processor/message_processor.go b/relayer/processor/message_processor.go index b125964d2a..4278f00c65 100644 --- a/relayer/processor/message_processor.go +++ b/relayer/processor/message_processor.go @@ -165,7 +165,6 @@ func (mp *messageProcessor) shouldUpdateClientNow(ctx context.Context, src, dst func (mp *messageProcessor) assembleMessages(ctx context.Context, messages pathEndMessages, src, dst *pathEndRuntime) { var wg sync.WaitGroup - // TODO: instantiate ChanPath on chain providers before getting here mp.connMsgs = make([]connectionMessageToTrack, len(messages.connectionMessages)) for i, msg := range messages.connectionMessages { wg.Add(1) diff --git a/relayer/processor/path_end.go b/relayer/processor/path_end.go index 9ba05b5427..191b575837 100644 --- a/relayer/processor/path_end.go +++ b/relayer/processor/path_end.go @@ -7,6 +7,8 @@ type PathEnd struct { ChainID string ClientID string // ConnectionIDs are tracked by pathEndRuntime in PathProcessor for known connections on this client + // TODO: we are tracking connection IDs here to create multihop channel paths, check that it isn't a problem + ConnectionID string // Can be either "allowlist" or "denylist" Rule string @@ -20,13 +22,14 @@ type ChainChannelKey struct { } // NewPathEnd constructs a PathEnd, validating initial parameters. -func NewPathEnd(pathName string, chainID string, clientID string, rule string, filterList []ChainChannelKey) PathEnd { +func NewPathEnd(pathName, chainID, clientID, connectionID, rule string, filterList []ChainChannelKey) PathEnd { return PathEnd{ - PathName: pathName, - ChainID: chainID, - ClientID: clientID, - Rule: rule, - FilterList: filterList, + PathName: pathName, + ChainID: chainID, + ClientID: clientID, + ConnectionID: connectionID, + Rule: rule, + FilterList: filterList, } } diff --git a/relayer/processor/path_processor.go b/relayer/processor/path_processor.go index 1ec9922565..fb85ce8a08 100644 --- a/relayer/processor/path_processor.go +++ b/relayer/processor/path_processor.go @@ -3,6 +3,7 @@ package processor import ( "context" "fmt" + "github.com/cosmos/ibc-go/v7/modules/core/multihop" "reflect" "time" @@ -428,8 +429,61 @@ func (pp *PathProcessor) processAvailableSignals(ctx context.Context, cancel fun return false } +func (pp *PathProcessor) setChanPaths() error { + if len(pp.hopsPathEnd1to2) == 0 { + return nil + } + paths1 := make([]*multihop.Path, 0, len(pp.hopsPathEnd1to2)) + // We start with src and proceed with the corresponding path ends + hopA := pp.pathEnd1 + connectionHops1 := make([]string, 0, len(pp.hopsPathEnd1to2)) + for i, hopB := range append(pp.hopsPathEnd2to1, pp.pathEnd2) { + connectionHops1[i] = hopB.info.ConnectionID + endpointA := hopA.chainProvider.MultihopEndpoint(hopA.info.ClientID, hopA.info.ConnectionID) + endpointB := hopB.chainProvider.MultihopEndpoint(hopB.info.ClientID, hopB.info.ConnectionID) + hopA.chainProvider.SetMultihopCounterparty(endpointA, endpointB) + hopB.chainProvider.SetMultihopCounterparty(endpointB, endpointA) + paths1 = append(paths1, &multihop.Path{ + EndpointA: endpointA, + EndpointB: endpointB, + }) + if i < len(pp.hopsPathEnd1to2) { + hopA = pp.hopsPathEnd1to2[i] + } + } + paths2 := make([]*multihop.Path, 0, len(pp.hopsPathEnd2to1)) + connectionHops2 := make([]string, 0, len(pp.hopsPathEnd2to1)) + // We start with dst and go in reverse order (path ends in hopsPathEnd2to1 are already reversed) + for i, hopA := range append([]*pathEndRuntime{pp.pathEnd2}, pp.hopsPathEnd2to1...) { + connectionHops2[i] = hopA.info.ConnectionID + endpointA := hopA.chainProvider.MultihopEndpoint(hopA.info.ClientID, hopA.info.ConnectionID) + hopB := pp.pathEnd1 + if i < len(pp.hopsPathEnd1to2) { + hopB = pp.hopsPathEnd1to2[i] + } + endpointB := hopB.chainProvider.MultihopEndpoint(hopB.info.ClientID, hopB.info.ConnectionID) + hopA.chainProvider.SetMultihopCounterparty(endpointA, endpointB) + hopB.chainProvider.SetMultihopCounterparty(endpointB, endpointA) + paths2 = append(paths2, &multihop.Path{ + EndpointA: endpointA, + EndpointB: endpointB, + }) + } + chanPath1 := multihop.NewChanPath(paths1) + chanPath2 := multihop.NewChanPath(paths2) + // We need to key channel paths by the opposite connection hops as each chain needs to use the counterparty to + // generate a proof + pp.pathEnd1.chainProvider.AddChanPath(connectionHops2, chanPath1) + pp.pathEnd2.chainProvider.AddChanPath(connectionHops1, chanPath2) + return nil +} + // Run executes the main path process. func (pp *PathProcessor) Run(ctx context.Context, cancel func()) { + if err := pp.setChanPaths(); err != nil { + panic(err) + } + var retryTimer *time.Timer pp.flushTicker = time.NewTicker(pp.flushInterval) diff --git a/relayer/provider/provider.go b/relayer/provider/provider.go index ee86863097..18f233aceb 100644 --- a/relayer/provider/provider.go +++ b/relayer/provider/provider.go @@ -241,11 +241,17 @@ type ChainProvider interface { Init(ctx context.Context) error + // MultihopEndpoint returns a multihop endpoint for the chain to be used for multihop proofs + MultihopEndpoint(clientID, connectionID string) multihop.Endpoint + + // SetMultihopCounterparty sets the multihop counterparty for the chain + SetMultihopCounterparty(endpoint, counterparty multihop.Endpoint) + // AddChanPath adds multihop channel path to a destination chain - AddChanPath(connectionHops []string, chanPath *multihop.ChanPath) + AddChanPath(connectionHops []string, chanPath multihop.ChanPath) // GetChanPath gets multihop channel path to a destination chain - GetChanPath(connectionHops []string) *multihop.ChanPath + GetChanPath(connectionHops []string) multihop.ChanPath // [Begin] Client IBC message assembly functions NewClientState(dstChainID string, dstIBCHeader IBCHeader, dstTrustingPeriod, dstUbdPeriod time.Duration, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour bool) (ibcexported.ClientState, error) @@ -591,12 +597,3 @@ func (c *ClientOutdatedError) Error() string { return fmt.Sprintf("client for chain %q on chain %q is outdated (required: %d, actual: %d)", c.srcChainID, c.dstChainID, c.height, c.requiredHeight) } - -func NewClientOutdatedError(srcChainID, dstChainID string, height, requiredHeight uint64) *ClientOutdatedError { - return &ClientOutdatedError{ - srcChainID: srcChainID, - dstChainID: dstChainID, - height: height, - requiredHeight: requiredHeight, - } -} diff --git a/relayer/strategies.go b/relayer/strategies.go index 539d32e71a..a5f44f0845 100644 --- a/relayer/strategies.go +++ b/relayer/strategies.go @@ -28,6 +28,27 @@ const ( DefaultFlushInterval = 5 * time.Minute ) +func hopPathEnds(p *Path, pathName string, filterSrc, filterDst []processor.ChainChannelKey) ([]*processor.PathEnd, + []*processor.PathEnd) { + hopsSrcToDst := make([]*processor.PathEnd, len(p.Hops)) + for j, hop := range p.Hops { + pathEnd := hop.PathEnds[1] + srcToDst := processor.NewPathEnd(pathName, hop.ChainID, pathEnd.ClientID, pathEnd.ConnectionID, + p.Filter.Rule, filterSrc) + hopsSrcToDst[j] = &srcToDst + } + var hopsDstToSrc []*processor.PathEnd + // TODO: is it ok to reverse here? + for i := len(p.Hops) - 1; i >= 0; i-- { + hop := p.Hops[i] + pathEnd := hop.PathEnds[0] + dstToSrc := processor.NewPathEnd(pathName, hop.ChainID, pathEnd.ClientID, pathEnd.ConnectionID, + p.Filter.Rule, filterDst) + hopsDstToSrc = append(hopsDstToSrc, &dstToSrc) + } + return hopsSrcToDst, hopsDstToSrc +} + // StartRelayer starts the main relaying loop and returns a channel that will contain any control-flow related errors. func StartRelayer( ctx context.Context, @@ -67,19 +88,12 @@ func StartRelayer( filterSrc = append(filterSrc, ruleSrc) filterDst = append(filterDst, ruleDst) } - hopsSrcToDst := make([]*processor.PathEnd, len(p.Hops)) - hopsDstToSrc := make([]*processor.PathEnd, len(p.Hops)) - for j, hop := range p.Hops { - srcToDst := processor.NewPathEnd(pathName, hop.ChainID, hop.PathEnds[1].ClientID, filter.Rule, - filterSrc) - hopsSrcToDst[j] = &srcToDst - dstToSrc := processor.NewPathEnd(pathName, hop.ChainID, hop.PathEnds[0].ClientID, filter.Rule, - filterSrc) - hopsDstToSrc[j] = &dstToSrc - } + hopsSrcToDst, hopsDstToSrc := hopPathEnds(p, pathName, filterSrc, filterDst) ePaths[i] = path{ - src: processor.NewPathEnd(pathName, p.Src.ChainID, p.Src.ClientID, filter.Rule, filterSrc), - dst: processor.NewPathEnd(pathName, p.Dst.ChainID, p.Dst.ClientID, filter.Rule, filterDst), + src: processor.NewPathEnd(pathName, p.Src.ChainID, p.Src.ClientID, p.Src.ConnectionID, + filter.Rule, filterSrc), + dst: processor.NewPathEnd(pathName, p.Dst.ChainID, p.Dst.ClientID, p.Dst.ConnectionID, + filter.Rule, filterDst), hopsSrcToDst: hopsSrcToDst, hopsDstToSrc: hopsDstToSrc, }