From f2d8df99f9232693d710c3dd6b8fcfbc1e28ad4d Mon Sep 17 00:00:00 2001 From: DeepakBomjan Date: Thu, 3 Aug 2023 09:17:55 +0545 Subject: [PATCH 01/15] ci: update tag regex --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e9673730..35be8c1f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: "Release" on: push: tags: - - '*' # Run release on any tag. Will be marked as draft by default anyway. + - 'v*.*.*-alpha.*' # Run release on any tag. Will be marked as draft by default anyway. jobs: goreleaser: From f7d34136e00af46faa48fb858a047b8a6486925a Mon Sep 17 00:00:00 2001 From: DeepakBomjan Date: Thu, 3 Aug 2023 11:23:35 +0545 Subject: [PATCH 02/15] ci: keep go version to 1.19 --- .github/workflows/release.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 35be8c1f1..9f6aeef3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,9 +17,15 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.20 + go-version: 1.19 - - run: echo https://github.com/cosmos/relayer/blob/${GITHUB_REF#refs/tags/}/CHANGELOG.md#${GITHUB_REF#refs/tags/} > ../release_notes.md + # setup gopath + - name: Set PATH + run: | + echo "$(go env GOPATH)/bin" >> $GITHUB_PATH + shell: bash + + - run: echo https://github.com/icon-project/ibc-relay/blob/${GITHUB_REF#refs/tags/}/CHANGELOG.md#${GITHUB_REF#refs/tags/} > ../release_notes.md - name: setup release environment run: |- From 82de866f646393a92239e4d92dbd87face0b4756 Mon Sep 17 00:00:00 2001 From: DeepakBomjan Date: Thu, 3 Aug 2023 11:43:54 +0545 Subject: [PATCH 03/15] ci: update url --- .goreleaser.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index f976a7dd0..a965ce851 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,5 +1,4 @@ -project_name: Cosmos Relayer - +project_name: ibc-relay builds: - id: darwin-amd64 main: ./main.go @@ -14,7 +13,7 @@ builds: flags: - -mod=readonly ldflags: - - -s -w -X github.com/cosmos/relayer/v2/cmd.Version={{ .Tag }} + - -s -w -X github.com/icon-project/ibc-relay/v2/cmd.Version={{ .Tag }} - id: darwin-arm64 main: ./main.go binary: rly @@ -28,7 +27,7 @@ builds: flags: - -mod=readonly ldflags: - - -s -w -X github.com/cosmos/relayer/v2/cmd.Version={{ .Tag }} + - -s -w -X github.com/icon-project/ibc-relay/v2/cmd.Version={{ .Tag }} - id: linux-amd64 main: ./main.go binary: rly @@ -42,7 +41,7 @@ builds: flags: - -mod=readonly ldflags: - - -s -w -X github.com/cosmos/relayer/v2/cmd.Version={{ .Tag }} + - -s -w -X github.com/icon-project/ibc-relay/v2/cmd.Version={{ .Tag }} - id: linux-arm64 main: ./main.go binary: rly From 7756c603ffd7a4a698109ee8042cd7e8deea8dd5 Mon Sep 17 00:00:00 2001 From: DeepakBomjan Date: Mon, 7 Aug 2023 09:24:36 +0545 Subject: [PATCH 04/15] docs: update badges --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index de9c5a153..678e5905e 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ ![banner](./docs/images/comp.gif) [![Project Status: Initial Release](https://img.shields.io/badge/repo%20status-active-green.svg?style=flat-square)](https://www.repostatus.org/#active) -![GitHub Workflow Status](https://github.com/cosmos/relayer/actions/workflows/build.yml/badge.svg) +![GitHub Workflow Status](https://github.com/icon-project/ibc-relay/actions/workflows/build.yml/badge.svg) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue?style=flat-square&logo=go)](https://godoc.org/github.com/cosmos/relayer) -[![Go Report Card](https://goreportcard.com/badge/github.com/cosmos/relayer)](https://goreportcard.com/report/github.com/cosmos/relayer) -[![License: Apache-2.0](https://img.shields.io/github/license/cosmos/relayer.svg?style=flat-square)](https://github.com/cosmos/relayer/blob/main/LICENSE) -[![Lines Of Code](https://img.shields.io/tokei/lines/github/cosmos/relayer?style=flat-square)](https://github.com/cosmos/relayer) -[![Version](https://img.shields.io/github/tag/cosmos/relayer.svg?style=flat-square)](https://github.com/cosmos/relayer/latest) +[![Go Report Card](https://goreportcard.com/badge/github.com/icon-project/ibc-relay)](https://goreportcard.com/report/github.com/icon-project/ibc-relay) +[![License: Apache-2.0](https://img.shields.io/github/license/icon-project/ibc-relay.svg?style=flat-square)](https://github.com/icon-project/ibc-relay/blob/main/LICENSE) +[![Lines Of Code](https://img.shields.io/tokei/lines/github/icon-project/ibc-relay?style=flat-square)](https://github.com/icon-project/ibc-relay) +[![Version](https://img.shields.io/github/tag/icon-project/ibc-relay.svg?style=flat-square)](https://github.com/icon-project/ibc-relay/latest) [![codecov](https://codecov.io/gh/icon-project/ibc-relay/branch/main/graph/badge.svg?token=3OSG4KPSPZ)](https://codecov.io/gh/icon-project/ibc-relay) From 0d172e253e96ca2bf29ba2d4b46b03fe1556ae25 Mon Sep 17 00:00:00 2001 From: DeepakBomjan Date: Tue, 15 Aug 2023 10:16:32 +0545 Subject: [PATCH 05/15] ci: add release template --- .github/release.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..45f03e3fb --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,27 @@ +# .github/release.yml + +changelog: + exclude: + labels: + - cicd + - scripts + - test + categories: + - title: Breaking Changes 🛠 + labels: + - Major + - breaking-change + - title: New Features 🎉 + labels: + - Minor + - enhancement + - Feature + + - title: Bug Fixes 🎉 + labels: + - Patch + - bug + - title: Other Changes + labels: + - "*" + From 2eca6fd89c383c380800368fe3a6824a31bf734e Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:38:20 +0545 Subject: [PATCH 06/15] fix: do not update path end for height for request timeout (#124) Co-authored-by: izyak --- relayer/processor/path_end_runtime.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/relayer/processor/path_end_runtime.go b/relayer/processor/path_end_runtime.go index 302fb086f..3c7408258 100644 --- a/relayer/processor/path_end_runtime.go +++ b/relayer/processor/path_end_runtime.go @@ -446,8 +446,7 @@ func (pathEnd *pathEndRuntime) shouldSendPacketMessage(message packetIBCMessage, pathEndForHeight := counterparty if eventType == chantypes.EventTypeTimeoutPacket || - eventType == chantypes.EventTypeTimeoutPacketOnClose || - eventType == common.EventTimeoutRequest { + eventType == chantypes.EventTypeTimeoutPacketOnClose { pathEndForHeight = pathEnd } From adcde89b7589cb4e3f268a2b7416206ee8b8fde2 Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:31:10 +0545 Subject: [PATCH 07/15] feat: better logging for tx, provider, query, processor (#120) Co-authored-by: izyak --- relayer/chains/icon/icon_chain_processor.go | 86 ++++++++++++++------- relayer/chains/icon/msg.go | 2 - relayer/chains/icon/provider.go | 3 +- relayer/chains/icon/query.go | 6 +- relayer/chains/wasm/tx.go | 7 +- relayer/chains/wasm/wasm_chain_processor.go | 61 ++++++++++----- relayer/common/utils.go | 2 +- relayer/processor/message_processor.go | 10 +-- 8 files changed, 110 insertions(+), 67 deletions(-) diff --git a/relayer/chains/icon/icon_chain_processor.go b/relayer/chains/icon/icon_chain_processor.go index 66c98eb07..079a3c2f4 100644 --- a/relayer/chains/icon/icon_chain_processor.go +++ b/relayer/chains/icon/icon_chain_processor.go @@ -76,7 +76,7 @@ type Verifier struct { func NewIconChainProcessor(log *zap.Logger, provider *IconProvider, metrics *processor.PrometheusMetrics) *IconChainProcessor { return &IconChainProcessor{ - log: log.With(zap.String("chain_name", "Icon")), + log: log.With(zap.String("chain_name", provider.ChainName()), zap.String("chain_id", provider.ChainId())), chainProvider: provider, latestClientState: make(latestClientState), connectionStateCache: make(processor.ConnectionStateCache), @@ -149,7 +149,7 @@ func (icp *IconChainProcessor) Run(ctx context.Context, initialBlockHistory uint } // start_query_cycle - icp.log.Debug(" **************** Entering main query loop **************** ") + icp.log.Debug("Starting query cycle") err := icp.monitoring(ctx, &persistence) return err } @@ -168,6 +168,14 @@ func (icp *IconChainProcessor) StartFromHeight(ctx context.Context) int { return snapshotHeight } +func (icp *IconChainProcessor) getLastSavedHeight() int { + snapshotHeight, err := rlycommon.LoadSnapshotHeight(icp.Provider().ChainId()) + if err != nil || snapshotHeight < 0 { + return 0 + } + return snapshotHeight +} + func (icp *IconChainProcessor) initializeConnectionState(ctx context.Context) error { // TODO: review ctx, cancel := context.WithTimeout(ctx, queryTimeout) @@ -187,9 +195,9 @@ func (icp *IconChainProcessor) initializeConnectionState(ctx context.Context) er CounterpartyClientID: c.Counterparty.ClientId, }] = c.State == conntypes.OPEN - icp.log.Info("found connection", - zap.String("ClientId ", c.ClientId), - zap.String("ConnectionID ", c.Id), + icp.log.Debug("Found open connection", + zap.String("client-id ", c.ClientId), + zap.String("connection-id ", c.Id), ) } return nil @@ -221,11 +229,11 @@ func (icp *IconChainProcessor) initializeChannelState(ctx context.Context) error CounterpartyPortID: ch.Counterparty.PortId, }] = ch.State == chantypes.OPEN - icp.log.Info("Found channel", - zap.String("channelID", ch.ChannelId), - zap.String("Port id ", ch.PortId)) - zap.String("Counterparty Channel Id ", ch.Counterparty.ChannelId) - zap.String("Counterparty Port Id", ch.Counterparty.PortId) + icp.log.Debug("Found open channel", + zap.String("channel-id", ch.ChannelId), + zap.String("port-id ", ch.PortId), + zap.String("counterparty-channel-id", ch.Counterparty.ChannelId), + zap.String("counterparty-port-id", ch.Counterparty.PortId)) } return nil @@ -276,7 +284,7 @@ func (icp *IconChainProcessor) monitoring(ctx context.Context, persistence *quer } // } - icp.log.Debug("Start to query from height", zap.Int64("height", processedheight)) + icp.log.Info("Start to query from height", zap.Int64("height", processedheight)) // subscribe to monitor block ctxMonitorBlock, cancelMonitorBlock := context.WithCancel(ctx) reconnect() @@ -304,7 +312,7 @@ loop: go func(ctx context.Context, cancel context.CancelFunc) { blockReq.Height = types.NewHexInt(processedheight) - icp.log.Debug("Querying Height", zap.Int64("height", processedheight)) + icp.log.Debug("Try to reconnect from", zap.Int64("height", processedheight)) err := icp.chainProvider.client.MonitorBlock(ctx, blockReq, func(conn *websocket.Conn, v *types.BlockNotification) error { if !errors.Is(ctx.Err(), context.Canceled) { btpBlockNotifCh <- v @@ -313,7 +321,10 @@ loop: }, func(conn *websocket.Conn) { }, func(conn *websocket.Conn, err error) {}) if err != nil { - icp.SnapshotHeight(int(processedheight) - 5) + ht := icp.getHeightToSave(processedheight) + if ht != icp.getLastSavedHeight() { + icp.SnapshotHeight(ht) + } if errors.Is(err, context.Canceled) { return } @@ -329,8 +340,8 @@ loop: err := icp.verifyBlock(ctx, br.Header) if err != nil { reconnect() - icp.log.Warn("failed to Verify BTP Block", - zap.Int64("got", br.Height), + icp.log.Warn("Failed to verify BTP Block", + zap.Int64("height", br.Height), zap.Error(err), ) break @@ -348,8 +359,7 @@ loop: } ibcHeaderCache[uint64(br.Height)] = br.Header - icp.log.Info("Queried Latest height: ", - zap.String("chain id ", icp.chainProvider.ChainId()), + icp.log.Debug("Queried block ", zap.Int64("height", br.Height)) err = icp.handlePathProcessorUpdate(ctx, br.Header, ibcMessageCache, ibcHeaderCache.Clone()) if err != nil { @@ -364,7 +374,10 @@ loop: if br = nil; len(btpBlockRespCh) > 0 { br = <-btpBlockRespCh } - icp.SnapshotHeight(int(icp.latestBlock.Height) - 5) + ht, takeSnapshot := icp.shouldSnapshot(int(icp.latestBlock.Height)) + if takeSnapshot { + icp.SnapshotHeight(ht) + } } // remove unprocessed blockResponses for len(btpBlockRespCh) > 0 { @@ -455,19 +468,32 @@ loop: } } -func (icp *IconChainProcessor) SnapshotHeight(height int) { - +func (icp *IconChainProcessor) shouldSnapshot(height int) (int, bool) { blockInterval := icp.Provider().ProviderConfig().GetBlockInterval() snapshotThreshold := rlycommon.ONE_HOUR / int(blockInterval) - retryAfter := icp.Provider().ProviderConfig().GetFirstRetryBlockAfter() - snapshotHeight := height - int(retryAfter) + snapshotHeight := icp.getHeightToSave(int64(height)) if snapshotHeight%snapshotThreshold == 0 { - err := rlycommon.SnapshotHeight(icp.Provider().ChainId(), height) - if err != nil { - icp.log.Warn("Failed saving height snapshot for height", zap.Int("height", height)) - } + return snapshotHeight, true + } + return 0, false +} + +func (icp *IconChainProcessor) getHeightToSave(height int64) int { + retryAfter := icp.Provider().ProviderConfig().GetFirstRetryBlockAfter() + ht := int(height - int64(retryAfter)) + if ht < 0 { + return 0 + } + return ht +} + +func (icp *IconChainProcessor) SnapshotHeight(height int) { + icp.log.Info("Save height for snapshot", zap.Int("height", height)) + err := rlycommon.SnapshotHeight(icp.Provider().ChainId(), height) + if err != nil { + icp.log.Warn("Failed saving height snapshot for height", zap.Int("height", height)) } } @@ -526,6 +552,8 @@ func (icp *IconChainProcessor) verifyBlock(ctx context.Context, ibcHeader provid icp.verifier.nextProofContext = header.Validators icp.verifier.verifiedHeight = int64(header.Height()) icp.verifier.prevNetworkSectionHash = types.NewNetworkSection(header.Header).Hash() + icp.log.Debug("Verified block ", + zap.Uint64("height", header.Height())) return nil } @@ -602,8 +630,8 @@ func (icp *IconChainProcessor) handleBTPBlockRequest( request.err = errors.Wrapf(err, "event.UnmarshalFromBytes: %v", err) return } - icp.log.Info("Detected eventlog: ", zap.Int64("Height", request.height), - zap.String("Eventlog", string(el.Indexed[0]))) + icp.log.Info("Detected eventlog ", zap.Int64("height", request.height), + zap.String("eventlog", IconCosmosEventMap[string(el.Indexed[0])])) eventlogs = append(eventlogs, el) } @@ -629,7 +657,7 @@ func (icp *IconChainProcessor) handleBTPBlockRequest( request.response.IsProcessed = processed return } - request.err = errors.Wrapf(err, "failed to get btp header: %v", err) + request.err = errors.Wrapf(err, "Failed to get btp header: %v", err) return } request.response.Header = NewIconIBCHeader(btpHeader, validators, int64(btpHeader.MainHeight)) diff --git a/relayer/chains/icon/msg.go b/relayer/chains/icon/msg.go index 565a46a43..27015d352 100644 --- a/relayer/chains/icon/msg.go +++ b/relayer/chains/icon/msg.go @@ -28,7 +28,5 @@ func (icp *IconProvider) NewIconMessage(msg interface{}, method string) provider Method: method, } - // icp.log.Debug("Icon Message ", zap.String("method", method), zap.Any("message ", msg)) - return im } diff --git a/relayer/chains/icon/provider.go b/relayer/chains/icon/provider.go index 31feeb314..3d8120b57 100644 --- a/relayer/chains/icon/provider.go +++ b/relayer/chains/icon/provider.go @@ -92,7 +92,6 @@ func (pp *IconProviderConfig) GetFirstRetryBlockAfter() uint64 { return 8 } -// NewProvider should provide a new Icon provider // NewProvider should provide a new Icon provider func (pp *IconProviderConfig) NewProvider(log *zap.Logger, homepath string, debug bool, chainName string) (provider.ChainProvider, error) { @@ -118,7 +117,7 @@ func (pp *IconProviderConfig) NewProvider(log *zap.Logger, homepath string, debu codec := MakeCodec(ModuleBasics, []string{}) return &IconProvider{ - log: log.With(zap.String("sys", "chain_client")), + log: log.With(zap.String("chain_id", pp.ChainID)), client: NewClient(pp.getRPCAddr(), log), PCfg: pp, wallet: wallet, diff --git a/relayer/chains/icon/query.go b/relayer/chains/icon/query.go index b9d5fe453..adfd59c6d 100644 --- a/relayer/chains/icon/query.go +++ b/relayer/chains/icon/query.go @@ -615,12 +615,12 @@ func (icp *IconProvider) QueryChannels(ctx context.Context) ([]*chantypes.Identi "portId": portId, }), &_channel) if err != nil { - icp.log.Error("unable to fetch channel for ", zap.String("channel id ", channelId), zap.Error(err)) + icp.log.Error("unable to fetch channel for ", zap.String("channel-id ", channelId), zap.Error(err)) continue } if _channel == "" { - icp.log.Debug("channel not present for ", zap.String("Channel id ", channelId), zap.String("port id ", portId)) + icp.log.Debug("Channel not present for ", zap.String("channel-id ", channelId), zap.String("port-id ", portId)) continue } @@ -628,7 +628,7 @@ func (icp *IconProvider) QueryChannels(ctx context.Context) ([]*chantypes.Identi _, err = HexBytesToProtoUnmarshal(_channel, &channel) if err != nil { icp.log.Info("Unable to unmarshal channel for ", - zap.String("channel id ", channelId), zap.Error(err)) + zap.String("channel-id ", channelId), zap.Error(err)) continue } diff --git a/relayer/chains/wasm/tx.go b/relayer/chains/wasm/tx.go index 212565418..38a631373 100644 --- a/relayer/chains/wasm/tx.go +++ b/relayer/chains/wasm/tx.go @@ -920,7 +920,7 @@ func (ap *WasmProvider) buildMessages(clientCtx client.Context, txf tx.Factory, } txf = txf.WithGas(adjusted) - _, _ = fmt.Fprintf(os.Stderr, "%s\n", tx.GasEstimateResponse{GasEstimate: txf.Gas()}) + // _, _ = fmt.Fprintf(os.Stderr, "%s\n", tx.GasEstimateResponse{GasEstimate: txf.Gas()}) } if clientCtx.Simulate { @@ -1003,9 +1003,8 @@ func (ap *WasmProvider) BroadcastTx( ap.log.Info("Submitted transaction", zap.String("chain_id", ap.PCfg.ChainID), - zap.String("txHash", res.TxHash), - zap.Int64("Height", res.Height), - zap.Any("Methods called", msgTypesField(msgs)), + zap.String("tx_hash", res.TxHash), + msgTypesField(msgs), ) if shouldWait { diff --git a/relayer/chains/wasm/wasm_chain_processor.go b/relayer/chains/wasm/wasm_chain_processor.go index ef3f2680d..4b7dc186a 100644 --- a/relayer/chains/wasm/wasm_chain_processor.go +++ b/relayer/chains/wasm/wasm_chain_processor.go @@ -218,7 +218,7 @@ func (ccp *WasmChainProcessor) StartFromHeight(ctx context.Context) int { func (ccp *WasmChainProcessor) Run(ctx context.Context, initialBlockHistory uint64) error { // this will be used for persistence across query cycle loop executions persistence := queryCyclePersistence{ - minQueryLoopDuration: defaultMinQueryLoopDuration, + minQueryLoopDuration: time.Duration(ccp.chainProvider.PCfg.BlockInterval * uint64(common.NanosecondRatio)), lastBalanceUpdate: time.Unix(0, 0), balanceUpdateWaitDuration: defaultBalanceUpdateWaitDuration, } @@ -312,6 +312,11 @@ func (ccp *WasmChainProcessor) initializeConnectionState(ctx context.Context) er CounterpartyConnID: c.Counterparty.ConnectionId, CounterpartyClientID: c.Counterparty.ClientId, }] = c.State == conntypes.OPEN + + ccp.log.Debug("Found open connection", + zap.String("client-id ", c.ClientId), + zap.String("connection-id ", c.Id), + ) } return nil } @@ -340,12 +345,11 @@ func (ccp *WasmChainProcessor) initializeChannelState(ctx context.Context) error CounterpartyChannelID: ch.Counterparty.ChannelId, CounterpartyPortID: ch.Counterparty.PortId, }] = ch.State == chantypes.OPEN - ccp.log.Info("Found channel", - zap.String("channelID", ch.ChannelId), - zap.String("Port id ", ch.PortId)) - zap.String("Counterparty Channel Id ", ch.Counterparty.ChannelId) - zap.String("Counterparty Port Id", ch.Counterparty.PortId) - + ccp.log.Debug("Found open channel", + zap.String("channel-id", ch.ChannelId), + zap.String("port-id ", ch.PortId), + zap.String("counterparty-channel-id", ch.Counterparty.ChannelId), + zap.String("counterparty-port-id", ch.Counterparty.PortId)) } return nil } @@ -359,13 +363,15 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer zap.Uint("attempts", latestHeightQueryRetries), zap.Error(err), ) + + ccp.SnapshotHeight(ccp.getHeightToSave(status.SyncInfo.LatestBlockHeight)) return nil } persistence.latestHeight = status.SyncInfo.LatestBlockHeight // ccp.chainProvider.setCometVersion(ccp.log, status.NodeInfo.Version) - ccp.log.Info("Queried latest height", + ccp.log.Debug("Queried latest height", zap.Int64("latest_height", persistence.latestHeight), ) @@ -398,7 +404,10 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer chainID := ccp.chainProvider.ChainId() var latestHeader provider.IBCHeader - ccp.SnapshotHeight(int(persistence.latestHeight)) + ht, takeSnapshot := ccp.shouldSnapshot(int(persistence.latestHeight)) + if takeSnapshot { + ccp.SnapshotHeight(ht) + } for i := persistence.latestQueriedBlock + 1; i <= persistence.latestHeight; i++ { var eg errgroup.Group @@ -408,7 +417,6 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer eg.Go(func() (err error) { queryCtx, cancelQueryCtx := context.WithTimeout(ctx, blockResultsQueryTimeout) defer cancelQueryCtx() - // fmt.Println("the lastqueried Block", i) blockRes, err = ccp.chainProvider.RPCClient.BlockResults(queryCtx, &i) return err }) @@ -425,7 +433,7 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer } if err := ccp.Verify(ctx, lightBlock); err != nil { - ccp.log.Error("failed to Verify Wasm Header", zap.Int64("Height", blockRes.Height)) + ccp.log.Warn("Failed to verify block", zap.Int64("height", blockRes.Height), zap.Error(err)) return err } @@ -451,7 +459,7 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer messages := ibcMessagesFromEvents(ccp.log, tx.Events, chainID, heightUint64, ccp.chainProvider.PCfg.IbcHandlerAddress, base64Encoded) for _, m := range messages { - ccp.log.Info("Detected Eventlog", zap.String("Eventlog", m.eventType)) + ccp.log.Info("Detected eventlog", zap.String("eventlog", m.eventType)) ccp.handleMessage(ctx, m, ibcMessagesCache) } } @@ -501,19 +509,32 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer return nil } -func (ccp *WasmChainProcessor) SnapshotHeight(height int) { - +func (ccp *WasmChainProcessor) shouldSnapshot(height int) (int, bool) { blockInterval := ccp.Provider().ProviderConfig().GetBlockInterval() snapshotThreshold := common.ONE_HOUR / int(blockInterval) - retryAfter := ccp.Provider().ProviderConfig().GetFirstRetryBlockAfter() - snapshotHeight := height - int(retryAfter) + snapshotHeight := ccp.getHeightToSave(int64(height)) if snapshotHeight%snapshotThreshold == 0 { - err := common.SnapshotHeight(ccp.Provider().ChainId(), height) - if err != nil { - ccp.log.Warn("Failed saving height snapshot for height", zap.Int("height", height)) - } + return snapshotHeight, true + } + return 0, false +} + +func (ccp *WasmChainProcessor) getHeightToSave(height int64) int { + retryAfter := ccp.Provider().ProviderConfig().GetFirstRetryBlockAfter() + ht := int(height - int64(retryAfter)) + if ht < 0 { + return 0 + } + return ht +} + +func (ccp *WasmChainProcessor) SnapshotHeight(height int) { + ccp.log.Info("Save height for snapshot", zap.Int("height", height)) + err := common.SnapshotHeight(ccp.Provider().ChainId(), height) + if err != nil { + ccp.log.Warn("Failed saving height snapshot for height", zap.Int("height", height)) } } diff --git a/relayer/common/utils.go b/relayer/common/utils.go index b634a7f9c..878a0d2df 100644 --- a/relayer/common/utils.go +++ b/relayer/common/utils.go @@ -56,5 +56,5 @@ func LoadSnapshotHeight(chain_id string) (int, error) { if err != nil { return -1, fmt.Errorf("Failed reading file, %w", err) } - return strconv.Atoi(string(content)) + return strconv.Atoi(strings.TrimSuffix(string(content), "\n")) } diff --git a/relayer/processor/message_processor.go b/relayer/processor/message_processor.go index 5af580e36..a8fef005d 100644 --- a/relayer/processor/message_processor.go +++ b/relayer/processor/message_processor.go @@ -311,16 +311,14 @@ func (mp *messageProcessor) handleMsgUpdateClientForIcon(ctx context.Context, sr latestConsensusHeight := dst.clientState.ConsensusHeight if !src.latestHeader.IsCompleteBlock() { - mp.log.Debug("Src latest IbcHeader is not complete Block", - zap.Int64("Height", int64(src.latestHeader.Height()))) return nil } if src.latestHeader.Height() <= latestConsensusHeight.RevisionHeight { mp.log.Debug("Src latest header is less then latest client State", - zap.String("Chainid ", src.info.ChainID), - zap.Int64("LatestHeader height", int64(src.latestHeader.Height())), - zap.Int64("Client State height", int64(latestConsensusHeight.RevisionHeight))) + zap.String("chain-id", src.info.ChainID), + zap.Int64("latest-header-height", int64(src.latestHeader.Height())), + zap.Int64("client-state-height", int64(latestConsensusHeight.RevisionHeight))) return nil } @@ -502,7 +500,7 @@ func (mp *messageProcessor) sendSingleMessage( msgType := tracker.msgType() - dst.log.Debug(fmt.Sprintf("Will broadcast %s message", msgType), zap.Object("msg", tracker)) + // dst.log.Debug(fmt.Sprintf("Will broadcast %s message", msgType), zap.Object("msg", tracker)) // Set callback for packet messages so that we increment prometheus metrics on successful relays. var callback func(rtr *provider.RelayerTxResponse, err error) From 416cb17ad910612c0b2351af33d684d79c3de806 Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:33:38 +0545 Subject: [PATCH 08/15] docs: deviation from cosmos relay (#129) Co-authored-by: izyak Co-authored-by: viveksharmapoudel --- README.md | 8 ++++ docs/deviations_from_ibc.md | 71 +++++++++++++++++++++++++++++++++++ examples/config_IBC_ICON.yaml | 59 +++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 docs/deviations_from_ibc.md create mode 100644 examples/config_IBC_ICON.yaml diff --git a/README.md b/README.md index 678e5905e..29aea88af 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ [![codecov](https://codecov.io/gh/icon-project/ibc-relay/branch/main/graph/badge.svg?token=3OSG4KPSPZ)](https://codecov.io/gh/icon-project/ibc-relay) +--- + +This repo is a fork of cosmos [relayer](https://github.com/cosmos/relayer). The goal of this project is to relay packets between ICON and Wasm chains by following the IBC Specs. +1. [What is ICON-IBC Integration](https://github.com/icon-project/IBC-Integration) +2. [Deviations from Cosmos Relayer](./docs/deviations_from_ibc.md) +--- + + In IBC, blockchains do not directly pass messages to each other over the network. This is where `relayer` comes in. A relayer process monitors for updates on opens paths between sets of [IBC](https://ibcprotocol.org/) enabled chains. The relayer submits these updates in the form of specific message types to the counterparty chain. Clients are then used to diff --git a/docs/deviations_from_ibc.md b/docs/deviations_from_ibc.md new file mode 100644 index 000000000..dd4f9a971 --- /dev/null +++ b/docs/deviations_from_ibc.md @@ -0,0 +1,71 @@ +# Deviations from IBC Relay + +This relayer was meant to be used for [ICON-IBC Integration](https://github.com/icon-project/IBC-Integration). So, it is made to accomodate all the changes made on IBC Integration such that IBC can be established between ICON and Cosmos based chains. + +All the deviations that the ICON-IBC Integrations can be found [here](). + +## Changes +The major changes in the relayer include: +- [ICON Module](#icon-module) +- [Wasm Module](#wasm-module) +- [Relayer Internal](#relayer-internal) +- [Cmd](#cmd) +- [Config](#config) + +## Icon Module +- Icon Chain Module has been added to support ICON Chain. +- We use [BTP Blocks](https://icon.community/glossary/btp-blocks/) provided by ICON chain significantly. +- Icon provides a websocket api which we can use to stream all blocks of mainnet. We do use this websockets instead of polling each height as done on cosmos chain processor. +- The relayer interfaces with IBC contract deployed on ICON and calls methods of the contract via ICON Provider. +- To update counterparty light client, we use information obtained BTP Blocks rather than actual main ICON chain. +- We query if the block is a BTP Block, and if yes, it initiates messages that'll be sent to counterparty contract. So, for each BTP Block produced, light client is updated. + +## Wasm Module +- Wasm Module has been added to support CosmWasm contracts. +- Like cosmos chain processor, polling is done for each height of the chain. +- Wasm Provider provides methods to interface with the IBC Wasm contract deployed on respective cosmos chain. + +## Relayer Internal +- Minimal changes has had to be added on the internal logic of the relayer to support for BTP Blocks. +- Since ICON cannot produce non membership proofs via BTP Blocks, a method called `requestTimeout` was introduced on ICON contract for when packet is to be timed out on wasm contracts. Logic for this has been added on relayer internal. +- Update client and the message to be sent after, has been sent on different transaction. [Reason for this decision]() + +## Cmd +- Relevant methods for ICON has been added accordingly. +- When a BTP client is to be created on WASM contract, we need to specify a BTP height for the relayer. By default, it takes the first BTP height for this network. If we are to give a custom height, a flag `btp-block-height` has been added. The height should be a BTP Block for given BTP network type and BTP Network Id. + ```sh + rly tx clients icon-path-name --client-tp "1000000m" --btp-block-height 11313986 + ``` + +## Config +- Example Config for ICON-IBC Integration + - [Here](../examples/config_IBC_ICON.yaml) + + + +## Wasm Config +Other parameters are same as in cosmos chains. The specifications for added fields in config are +- IBC Handler Address + - WASM Contract Address of IBC Handler +- First Retry Block After: + - For first retry transaction, wait for this number of blocks (default set to 3) +- Start Height: + - Custom height to start chain processor from +- Block Interval: + - Block Interval of cosmos chain + +## Icon Config +- keystore and password + - Keystore and password is used to generate a wallet for relay +- Icon Network ID + - Network ID for ICON chain (varies for testnet, localnet and mainnet) +- BTP Network ID, BTP Network Type ID + - BTP Specific IDs required +- Start Height: + - Height to start ICON chain processor from +- IBC Handler Address: + - Address of IBC java contract +- Block Interval: + - Block Interval of ICON chain +- First Retry Block After: + - For first retry transaction, wait for this number of blocks (default set to 8) diff --git a/examples/config_IBC_ICON.yaml b/examples/config_IBC_ICON.yaml new file mode 100644 index 000000000..f6a917792 --- /dev/null +++ b/examples/config_IBC_ICON.yaml @@ -0,0 +1,59 @@ +global: + api-listen-addr: :5183 + timeout: 10s + memo: "" + light-cache-size: 20 +chains: + archway: + type: wasm + value: + key-directory: /home/user/.relayer/keys/localnet + key: relayWallet + chain-id: localnet + rpc-addr: http://localhost:26657 + account-prefix: archway + keyring-backend: test + gas-adjustment: 1.5 + gas-prices: 0.025stake + min-gas-amount: 1000000 + debug: true + timeout: 20s + block-timeout: "" + output-format: json + sign-mode: direct + extra-codecs: [] + coin-type: 0 + broadcast-mode: batch + ibc-handler-address: archway14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sy85n2u + first-retry-block-after: 0 + start-height: 0 + block-interval: 6000 + icon: + type: icon + value: + key: "" + chain-id: ibc-icon + rpc-addr: http://localhost:9082/api/v3/ + timeout: 30s + keystore: /home/user/keystore/godWallet.json + password: gochain + icon-network-id: 3 + btp-network-id: 1 + btp-network-type-id: 1 + start-height: 0 + ibc-handler-address: cxa1daa337788364a72644860a5097e3eef7a5d416 + first-retry-block-after: 0 + block-interval: 2000 +paths: + icon-archway: + src: + chain-id: ibc-icon + client-id: 07-tendermint-0 + connection-id: connection-0 + dst: + chain-id: localnet + client-id: iconclient-0 + connection-id: connection-0 + src-channel-filter: + rule: "" + channel-list: [] \ No newline at end of file From 8dee80a614288b5cb5b8f1433e2b465dfde6ab0e Mon Sep 17 00:00:00 2001 From: viveksharmapoudel Date: Wed, 9 Aug 2023 15:58:13 +0545 Subject: [PATCH 09/15] fix: icon update client retry issue (#127) * fix: send packet on chain with icon client only after client is updated * fix: update client only retry * fix: check from clientState instead of querying consensus State * fix: tx wasm multiple retry * fix: build and next sequence not fetched issue * fix: test message save position during debug position change --- relayer/chains/icon/provider.go | 16 +++--- relayer/chains/icon/query.go | 10 ++-- relayer/chains/wasm/helper_debug_msg.go | 9 +--- relayer/chains/wasm/query.go | 11 ++-- relayer/chains/wasm/tx.go | 20 +++++--- relayer/chains/wasm/types/types.go | 14 ++--- relayer/common/const.go | 24 ++++++++- relayer/processor/message_processor.go | 54 +++++++++++++------- relayer/processor/path_end_runtime.go | 43 ++++++++++++++-- relayer/processor/path_processor_internal.go | 4 +- relayer/processor/utils.go | 2 +- 11 files changed, 140 insertions(+), 67 deletions(-) diff --git a/relayer/chains/icon/provider.go b/relayer/chains/icon/provider.go index 3d8120b57..4cec9c55d 100644 --- a/relayer/chains/icon/provider.go +++ b/relayer/chains/icon/provider.go @@ -265,7 +265,7 @@ func (icp *IconProvider) ConnectionHandshakeProof(ctx context.Context, msgOpenIn func (icp *IconProvider) ConnectionProof(ctx context.Context, msgOpenAck provider.ConnectionInfo, height uint64) (provider.ConnectionProof, error) { - connState, err := icp.QueryConnection(ctx, int64(height), msgOpenAck.ConnID) + connState, err := icp.QueryConnection(ctx, int64(msgOpenAck.Height), msgOpenAck.ConnID) if err != nil { return provider.ConnectionProof{}, err } @@ -276,18 +276,16 @@ func (icp *IconProvider) ConnectionProof(ctx context.Context, msgOpenAck provide } func (icp *IconProvider) ChannelProof(ctx context.Context, msg provider.ChannelInfo, height uint64) (provider.ChannelProof, error) { + channelResult, err := icp.QueryChannel(ctx, int64(msg.Height), msg.ChannelID, msg.PortID) if err != nil { return provider.ChannelProof{}, nil } return provider.ChannelProof{ - Proof: channelResult.Proof, - ProofHeight: clienttypes.Height{ - RevisionNumber: 0, - RevisionHeight: height, - }, - Ordering: chantypes.Order(channelResult.Channel.GetOrdering()), - Version: channelResult.Channel.Version, + Proof: channelResult.Proof, + ProofHeight: channelResult.ProofHeight, + Ordering: chantypes.Order(channelResult.Channel.GetOrdering()), + Version: channelResult.Channel.Version, }, nil } @@ -337,7 +335,7 @@ func (icp *IconProvider) PacketAcknowledgement(ctx context.Context, msgRecvPacke } return provider.PacketProof{ Proof: packetAckResponse.Proof, - ProofHeight: packetAckResponse.GetProofHeight(), + ProofHeight: packetAckResponse.ProofHeight, }, nil } diff --git a/relayer/chains/icon/query.go b/relayer/chains/icon/query.go index adfd59c6d..ae7bc341d 100644 --- a/relayer/chains/icon/query.go +++ b/relayer/chains/icon/query.go @@ -673,20 +673,24 @@ func (icp *IconProvider) QueryNextSeqRecv(ctx context.Context, height int64, cha "portId": portid, "channelId": channelid, }, callParamsWithHeight(types.NewHexInt(height))) - var nextSeqRecv uint64 + var nextSeqRecv types.HexInt if err := icp.client.Call(callParam, &nextSeqRecv); err != nil { return nil, err } key := common.GetNextSequenceRecvCommitmentKey(portid, channelid) - keyHash := common.Sha3keccak256(key, []byte(types.NewHexInt(int64(nextSeqRecv)))) + keyHash := common.Sha3keccak256(key, []byte(nextSeqRecv)) proof, err := icp.QueryIconProof(ctx, height, keyHash) if err != nil { return nil, err } + nextSeq, err := nextSeqRecv.Value() + if err != nil { + return nil, err + } return &chantypes.QueryNextSequenceReceiveResponse{ - NextSequenceReceive: nextSeqRecv, + NextSequenceReceive: uint64(nextSeq), Proof: proof, ProofHeight: clienttypes.NewHeight(0, uint64(height)), }, nil diff --git a/relayer/chains/wasm/helper_debug_msg.go b/relayer/chains/wasm/helper_debug_msg.go index 98108f3eb..5ab3f0b8d 100644 --- a/relayer/chains/wasm/helper_debug_msg.go +++ b/relayer/chains/wasm/helper_debug_msg.go @@ -58,7 +58,6 @@ func readExistingData(filename string, opPointer interface{}) error { func SaveMsgToFile(filename string, msgs []provider.RelayerMessage) { type DataFormat struct { Step string `json:"step"` - Update types.HexBytes `json:"update"` Message types.HexBytes `json:"message"` } @@ -73,20 +72,14 @@ func SaveMsgToFile(filename string, msgs []provider.RelayerMessage) { return } - var update types.HexBytes // update on msg n will be added to n+1 message for _, m := range msgs { if m == nil { continue } b, _ := m.MsgBytes() - if m.Type() == "update_client" { - update = types.NewHexBytes(b) - continue - } - d = append(d, DataFormat{Step: m.Type(), Update: update, Message: types.NewHexBytes(b)}) + d = append(d, DataFormat{Step: m.Type(), Message: types.NewHexBytes(b)}) // resetting update - update = "" } jsonDumpDataFile(filename, d) } diff --git a/relayer/chains/wasm/query.go b/relayer/chains/wasm/query.go index 460d07064..053fcfadd 100644 --- a/relayer/chains/wasm/query.go +++ b/relayer/chains/wasm/query.go @@ -313,22 +313,23 @@ func (ap *WasmProvider) QueryChannelContract(ctx context.Context, portId, channe } func (ap *WasmProvider) QueryClientConsensusState(ctx context.Context, chainHeight int64, clientid string, clientHeight ibcexported.Height) (*clienttypes.QueryConsensusStateResponse, error) { - consensusStateParam, err := types.NewConsensusState(clientid, uint64(chainHeight)).Bytes() + + consensusStateParam, err := types.NewConsensusStateByHeight(clientid, uint64(clientHeight.GetRevisionHeight())).Bytes() consensusState, err := ap.QueryIBCHandlerContractProcessed(ctx, consensusStateParam) if err != nil { return nil, err } - var consensusS icon.ConsensusState - if err := ap.Cdc.Marshaler.Unmarshal(consensusState, &consensusS); err != nil { + cdc := codec.NewProtoCodec(ap.Cdc.InterfaceRegistry) + csState, err := clienttypes.UnmarshalConsensusState(cdc, consensusState) + if err != nil { return nil, err } - anyConsensusState, err := clienttypes.PackConsensusState(&consensusS) + anyConsensusState, err := clienttypes.PackConsensusState(csState) if err != nil { return nil, err } - return clienttypes.NewQueryConsensusStateResponse(anyConsensusState, nil, clienttypes.NewHeight(0, uint64(chainHeight))), nil } diff --git a/relayer/chains/wasm/tx.go b/relayer/chains/wasm/tx.go index 38a631373..fdd7f7581 100644 --- a/relayer/chains/wasm/tx.go +++ b/relayer/chains/wasm/tx.go @@ -726,6 +726,9 @@ func (ap *WasmProvider) SendMessagesToMempool( return err } + // uncomment for saving msg + // SaveMsgToFile(WasmDebugMessagePath, msgs) + for _, msg := range msgs { if msg == nil { ap.log.Debug("One of the message of is nil ") @@ -743,13 +746,17 @@ func (ap *WasmProvider) SendMessagesToMempool( } if msg.Type() == MethodUpdateClient { - if err := ap.BroadcastTx(cliCtx, txBytes, []provider.RelayerMessage{msg}, asyncCtx, defaultBroadcastWaitTimeout, asyncCallback, true); err != nil { - if strings.Contains(err.Error(), sdkerrors.ErrWrongSequence.Error()) { - ap.handleAccountSequenceMismatchError(err) + if err := retry.Do(func() error { + if err := ap.BroadcastTx(cliCtx, txBytes, []provider.RelayerMessage{msg}, asyncCtx, defaultBroadcastWaitTimeout, asyncCallback, true); err != nil { + if strings.Contains(err.Error(), sdkerrors.ErrWrongSequence.Error()) { + ap.handleAccountSequenceMismatchError(err) + } } - return fmt.Errorf("Wasm: failed during updateClient %v", err) + return err + }, retry.Context(ctx), rtyAtt, retry.Delay(time.Millisecond*time.Duration(ap.PCfg.BlockInterval)), rtyErr); err != nil { + ap.log.Error("Failed to update client", zap.Any("Message", msg)) + return err } - ap.updateNextAccountSequence(sequence + 1) continue } if err := ap.BroadcastTx(cliCtx, txBytes, []provider.RelayerMessage{msg}, asyncCtx, defaultBroadcastWaitTimeout, asyncCallback, false); err != nil { @@ -760,9 +767,6 @@ func (ap *WasmProvider) SendMessagesToMempool( ap.updateNextAccountSequence(sequence + 1) } - //uncomment for saving msg - // SaveMsgToFile(WasmDebugMessagePath, msgs) - return nil } diff --git a/relayer/chains/wasm/types/types.go b/relayer/chains/wasm/types/types.go index 3ccb89093..b0826d463 100644 --- a/relayer/chains/wasm/types/types.go +++ b/relayer/chains/wasm/types/types.go @@ -71,20 +71,20 @@ func NewClientState(clientId string) *GetClientState { } } -type GetConsensusState struct { - ConsensusState struct { +type GetConsensusStateByHeight struct { + ConsensusStateByHeight struct { ClientId string "json:\"client_id\"" Height uint64 "json:\"height\"" - } `json:"get_consensus_state"` + } `json:"get_consensus_state_by_height"` } -func (x *GetConsensusState) Bytes() ([]byte, error) { +func (x *GetConsensusStateByHeight) Bytes() ([]byte, error) { return json.Marshal(x) } -func NewConsensusState(clientId string, height uint64) *GetConsensusState { - return &GetConsensusState{ - ConsensusState: struct { +func NewConsensusStateByHeight(clientId string, height uint64) *GetConsensusStateByHeight { + return &GetConsensusStateByHeight{ + ConsensusStateByHeight: struct { ClientId string "json:\"client_id\"" Height uint64 "json:\"height\"" }{ diff --git a/relayer/common/const.go b/relayer/common/const.go index 73c7d6ba7..596e9630b 100644 --- a/relayer/common/const.go +++ b/relayer/common/const.go @@ -1,5 +1,10 @@ package common +import ( + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + var ( EventTimeoutRequest = "TimeoutRequest(bytes)" IconModule = "icon" @@ -10,5 +15,22 @@ var ( ConnectionKey = "connection" ChannelKey = "channel" ONE_HOUR = 60 * 60 * 1000 - NanosecondRatio = 1000_000 + NanosecondRatio = 1000_000 +) + +var ( + EventRequiresClientUpdate = map[string]bool{ + chantypes.EventTypeRecvPacket: true, + chantypes.EventTypeAcknowledgePacket: true, + chantypes.EventTypeTimeoutPacket: true, + + conntypes.EventTypeConnectionOpenTry: true, + conntypes.EventTypeConnectionOpenAck: true, + conntypes.EventTypeConnectionOpenConfirm: true, + + chantypes.EventTypeChannelOpenTry: true, + chantypes.EventTypeChannelOpenAck: true, + chantypes.EventTypeChannelOpenConfirm: true, + chantypes.EventTypeChannelCloseConfirm: true, + } ) diff --git a/relayer/processor/message_processor.go b/relayer/processor/message_processor.go index a8fef005d..63102b95f 100644 --- a/relayer/processor/message_processor.go +++ b/relayer/processor/message_processor.go @@ -83,13 +83,13 @@ func (mp *messageProcessor) processMessages( src, dst *pathEndRuntime, ) error { - // 2/3 rule enough_time_pass && context change in case of BTPBlock + // 2/3 rule enough_time_pass && Valid BTP Block needsClientUpdate, err := mp.shouldUpdateClientNow(ctx, src, dst) if err != nil { return err } - if err := mp.assembleMsgUpdateClient(ctx, src, dst); err != nil { + if err := mp.assembleMsgUpdateClient(ctx, src, dst, needsClientUpdate); err != nil { return err } @@ -106,18 +106,21 @@ func (mp *messageProcessor) processMessages( func (mp *messageProcessor) shouldUpdateClientNow(ctx context.Context, src, dst *pathEndRuntime) (bool, error) { var err error // handle if dst is IconLightClient - if ClientIsIcon(dst.clientState) { - header, found := nextIconIBCHeader(src.ibcHeaderCache.Clone(), dst.lastClientUpdateHeight) + if IsBTPLightClient(dst.clientState) { + + // if the latestblock is less than clientState height + if dst.clientState.ConsensusHeight.RevisionHeight >= src.latestBlock.Height { + return false, nil + } + + header, found := src.ibcHeaderCache[src.latestBlock.Height] if !found { header, err = src.chainProvider.QueryIBCHeader(ctx, int64(src.latestBlock.Height)) if err != nil { return false, err } - if !header.IsCompleteBlock() { - return false, nil - } } - if header.ShouldUpdateWithZeroMessage() { + if header.IsCompleteBlock() { return true, nil } return false, nil @@ -229,10 +232,10 @@ func (mp *messageProcessor) assembleMessage( // assembleMsgUpdateClient uses the ChainProvider from both pathEnds to assemble the client update header // from the source and then assemble the update client message in the correct format for the destination. -func (mp *messageProcessor) assembleMsgUpdateClient(ctx context.Context, src, dst *pathEndRuntime) error { +func (mp *messageProcessor) assembleMsgUpdateClient(ctx context.Context, src, dst *pathEndRuntime, shouldUpdate bool) error { - if ClientIsIcon(dst.clientState) { - err := mp.handleMsgUpdateClientForIcon(ctx, src, dst) + if IsBTPLightClient(dst.clientState) { + err := mp.handleMsgUpdateClientForIcon(ctx, src, dst, shouldUpdate) return err } @@ -305,15 +308,19 @@ func (mp *messageProcessor) assembleMsgUpdateClient(ctx context.Context, src, ds return nil } -func (mp *messageProcessor) handleMsgUpdateClientForIcon(ctx context.Context, src, dst *pathEndRuntime) error { +func (mp *messageProcessor) handleMsgUpdateClientForIcon(ctx context.Context, src, dst *pathEndRuntime, shouldUpdate bool) error { clientID := dst.info.ClientID latestConsensusHeight := dst.clientState.ConsensusHeight - if !src.latestHeader.IsCompleteBlock() { + if !shouldUpdate { return nil } + if !src.latestHeader.IsCompleteBlock() { + return fmt.Errorf("Should Update is true but the Header is incomplete") + } + if src.latestHeader.Height() <= latestConsensusHeight.RevisionHeight { mp.log.Debug("Src latest header is less then latest client State", zap.String("chain-id", src.info.ChainID), @@ -338,13 +345,12 @@ func (mp *messageProcessor) handleMsgUpdateClientForIcon(ctx context.Context, sr } mp.msgUpdateClient = msgUpdateClient - return nil } func (mp *messageProcessor) findNextIBCHeader(ctx context.Context, src, dst *pathEndRuntime) (provider.IBCHeader, error) { clientConsensusHeight := dst.clientState.ConsensusHeight - if ClientIsIcon(dst.clientState) { + if IsBTPLightClient(dst.clientState) { header, found := nextIconIBCHeader(src.ibcHeaderCache.Clone(), dst.lastClientUpdateHeight) if !found { return nil, fmt.Errorf("unable to find Icon IBC header for Next height of %d ", clientConsensusHeight.RevisionHeight) @@ -374,6 +380,7 @@ func (mp *messageProcessor) trackAndSendMessages( batch = append(batch, t) continue } + go mp.sendSingleMessage(ctx, src, dst, t) } @@ -435,11 +442,16 @@ func (mp *messageProcessor) sendBatchMessages( defer cancel() // messages are batch with appended MsgUpdateClient - msgs := make([]provider.RelayerMessage, 1+len(batch)) - msgs[0] = mp.msgUpdateClient + msgs := make([]provider.RelayerMessage, 0, 1+len(batch)) + + // shouldn't send update incase of icon + if !IsBTPLightClient(dst.clientState) { + msgs = append(msgs, mp.msgUpdateClient) + } + fields := []zapcore.Field{} for i, t := range batch { - msgs[i+1] = t.assembledMsg() + msgs = append(msgs, t.assembledMsg()) fields = append(fields, zap.Object(fmt.Sprintf("msg_%d", i), t)) } @@ -493,7 +505,11 @@ func (mp *messageProcessor) sendSingleMessage( tracker messageToTrack, ) { - msgs := []provider.RelayerMessage{mp.msgUpdateClient, tracker.assembledMsg()} + msgs := make([]provider.RelayerMessage, 0, 2) + if !IsBTPLightClient(dst.clientState) { + msgs = append(msgs, mp.msgUpdateClient) + } + msgs = append(msgs, tracker.assembledMsg()) broadcastCtx, cancel := context.WithTimeout(ctx, messageSendTimeout) defer cancel() diff --git a/relayer/processor/path_end_runtime.go b/relayer/processor/path_end_runtime.go index 3c7408258..d4a8445aa 100644 --- a/relayer/processor/path_end_runtime.go +++ b/relayer/processor/path_end_runtime.go @@ -2,7 +2,6 @@ package processor import ( "context" - "strings" "sync" "time" @@ -10,6 +9,7 @@ import ( chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/cosmos/relayer/v2/relayer/common" "github.com/cosmos/relayer/v2/relayer/provider" + "go.uber.org/zap" ) @@ -450,7 +450,7 @@ func (pathEnd *pathEndRuntime) shouldSendPacketMessage(message packetIBCMessage, pathEndForHeight = pathEnd } - if strings.Contains(pathEnd.chainProvider.Type(), common.IconModule) && message.info.Height >= pathEndForHeight.latestBlock.Height { + if message.info.Height >= pathEndForHeight.latestBlock.Height { pathEnd.log.Debug("Waiting to relay packet message until counterparty height has incremented", zap.String("event_type", eventType), zap.Uint64("sequence", sequence), @@ -460,6 +460,18 @@ func (pathEnd *pathEndRuntime) shouldSendPacketMessage(message packetIBCMessage, ) return false } + + // allow to send only counterparty chain has consensusState + if IsBTPLightClient(pathEnd.clientState) && common.EventRequiresClientUpdate[message.eventType] == true { + if pathEnd.clientState.ConsensusHeight.RevisionHeight < message.info.Height { + pathEnd.log.Debug("Waiting to relay packet message until clientState is updated", + zap.Inline(message), + zap.String("event_type", eventType), + ) + return false + } + } + if !pathEnd.channelStateCache[k] { // channel is not open, do not send pathEnd.log.Warn("Refusing to relay packet message because channel is not open", @@ -563,7 +575,7 @@ func (pathEnd *pathEndRuntime) shouldSendConnectionMessage(message connectionIBC k = k.Counterparty() } - if strings.Contains(pathEnd.chainProvider.Type(), common.IconModule) && message.info.Height >= counterparty.latestBlock.Height { + if message.info.Height >= counterparty.latestBlock.Height { pathEnd.log.Debug("Waiting to relay connection message until counterparty height has incremented", zap.Inline(k), zap.String("event_type", eventType), @@ -571,6 +583,17 @@ func (pathEnd *pathEndRuntime) shouldSendConnectionMessage(message connectionIBC return false } + // allow to send only counterparty chain has consensusState + if IsBTPLightClient(pathEnd.clientState) && common.EventRequiresClientUpdate[message.eventType] == true { + if pathEnd.clientState.ConsensusHeight.RevisionHeight < message.info.Height { + pathEnd.log.Debug("Waiting to relay connection message until clientState is updated", + zap.Inline(message), + zap.String("event_type", eventType), + ) + return false + } + } + msgProcessCache, ok := pathEnd.connProcessing[eventType] if !ok { // in progress cache does not exist for this eventType, so can send. @@ -644,13 +667,25 @@ func (pathEnd *pathEndRuntime) shouldSendChannelMessage(message channelIBCMessag channelKey = channelKey.Counterparty() } - if strings.Contains(pathEnd.chainProvider.Type(), common.IconModule) && message.info.Height >= counterparty.latestBlock.Height { + if message.info.Height >= counterparty.latestBlock.Height { pathEnd.log.Debug("Waiting to relay channel message until counterparty height has incremented", zap.Inline(channelKey), zap.String("event_type", eventType), ) return false } + + // allow to send only counterparty chain has consensusState + if IsBTPLightClient(pathEnd.clientState) && common.EventRequiresClientUpdate[message.eventType] == true { + if pathEnd.clientState.ConsensusHeight.RevisionHeight < message.info.Height { + pathEnd.log.Debug("Waiting to relay channel message until clientState is updated", + zap.Inline(message), + zap.String("event_type", eventType), + ) + return false + } + } + msgProcessCache, ok := pathEnd.channelProcessing[eventType] if !ok { // in progress cache does not exist for this eventType, so can send. diff --git a/relayer/processor/path_processor_internal.go b/relayer/processor/path_processor_internal.go index cac76541c..b5c55cd73 100644 --- a/relayer/processor/path_processor_internal.go +++ b/relayer/processor/path_processor_internal.go @@ -52,7 +52,7 @@ func (pp *PathProcessor) getMessagesToSend( if e == chantypes.EventTypeRecvPacket { dstChan, dstPort := m[0].info.DestChannel, m[0].info.DestPort - res, err := dst.chainProvider.QueryNextSeqRecv(ctx, 0, dstChan, dstPort) + res, err := dst.chainProvider.QueryNextSeqRecv(ctx, int64(dst.latestBlock.Height), dstChan, dstPort) if err != nil { dst.log.Error("Failed to query next sequence recv", zap.String("channel_id", dstChan), @@ -673,7 +673,7 @@ func (pp *PathProcessor) updateClientTrustedState(src *pathEndRuntime, dst *path return } - if ClientIsIcon(src.clientState) { + if IsBTPLightClient(src.clientState) { ibcheader, ok := nextIconIBCHeader(dst.ibcHeaderCache.Clone(), src.clientState.ConsensusHeight.RevisionHeight) if !ok { pp.log.Debug("No cached IBC header found for client next trusted height", diff --git a/relayer/processor/utils.go b/relayer/processor/utils.go index 1a004c3f6..21f1d038a 100644 --- a/relayer/processor/utils.go +++ b/relayer/processor/utils.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/relayer/v2/relayer/provider" ) -func ClientIsIcon(cs provider.ClientState) bool { +func IsBTPLightClient(cs provider.ClientState) bool { if strings.Contains(cs.ClientID, common.IconLightClient) { return true } From 81c1701d4e3c2c11c371e30f665c295774b2b69d Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:41:42 +0545 Subject: [PATCH 10/15] feat: log height every hour simulateneously (#126) * feat: log height every hour simulateneously * fix: add missing method * fix: remove should snapshot --------- Co-authored-by: izyak Co-authored-by: izyak Co-authored-by: viveksharmapoudel --- relayer/chains/icon/icon_chain_processor.go | 24 ++++--------- relayer/chains/icon/query.go | 2 +- relayer/chains/wasm/wasm_chain_processor.go | 27 +++++---------- relayer/channel.go | 12 +++---- relayer/connection.go | 4 +-- relayer/strategies.go | 38 ++++++++++++++++++--- 6 files changed, 57 insertions(+), 50 deletions(-) diff --git a/relayer/chains/icon/icon_chain_processor.go b/relayer/chains/icon/icon_chain_processor.go index 079a3c2f4..44fb638a2 100644 --- a/relayer/chains/icon/icon_chain_processor.go +++ b/relayer/chains/icon/icon_chain_processor.go @@ -66,6 +66,8 @@ type IconChainProcessor struct { metrics *processor.PrometheusMetrics verifier *Verifier + + heightSnapshotChan chan struct{} } type Verifier struct { @@ -74,7 +76,7 @@ type Verifier struct { prevNetworkSectionHash []byte } -func NewIconChainProcessor(log *zap.Logger, provider *IconProvider, metrics *processor.PrometheusMetrics) *IconChainProcessor { +func NewIconChainProcessor(log *zap.Logger, provider *IconProvider, metrics *processor.PrometheusMetrics, heightSnapshot chan struct{}) *IconChainProcessor { return &IconChainProcessor{ log: log.With(zap.String("chain_name", provider.ChainName()), zap.String("chain_id", provider.ChainId())), chainProvider: provider, @@ -84,6 +86,7 @@ func NewIconChainProcessor(log *zap.Logger, provider *IconProvider, metrics *pro connectionClients: make(map[string]string), channelConnections: make(map[string]string), metrics: metrics, + heightSnapshotChan: heightSnapshot, } } @@ -306,6 +309,9 @@ loop: case err := <-errCh: return err + case <-icp.heightSnapshotChan: + icp.SnapshotHeight(icp.getHeightToSave(int64(icp.latestBlock.Height))) + case <-reconnectCh: cancelMonitorBlock() ctxMonitorBlock, cancelMonitorBlock = context.WithCancel(ctx) @@ -374,10 +380,6 @@ loop: if br = nil; len(btpBlockRespCh) > 0 { br = <-btpBlockRespCh } - ht, takeSnapshot := icp.shouldSnapshot(int(icp.latestBlock.Height)) - if takeSnapshot { - icp.SnapshotHeight(ht) - } } // remove unprocessed blockResponses for len(btpBlockRespCh) > 0 { @@ -468,18 +470,6 @@ loop: } } -func (icp *IconChainProcessor) shouldSnapshot(height int) (int, bool) { - blockInterval := icp.Provider().ProviderConfig().GetBlockInterval() - snapshotThreshold := rlycommon.ONE_HOUR / int(blockInterval) - - snapshotHeight := icp.getHeightToSave(int64(height)) - - if snapshotHeight%snapshotThreshold == 0 { - return snapshotHeight, true - } - return 0, false -} - func (icp *IconChainProcessor) getHeightToSave(height int64) int { retryAfter := icp.Provider().ProviderConfig().GetFirstRetryBlockAfter() ht := int(height - int64(retryAfter)) diff --git a/relayer/chains/icon/query.go b/relayer/chains/icon/query.go index ae7bc341d..7ace0469c 100644 --- a/relayer/chains/icon/query.go +++ b/relayer/chains/icon/query.go @@ -105,7 +105,7 @@ func (icp *IconProvider) QueryLatestHeight(ctx context.Context) (int64, error) { if block != nil { return block.Height, nil } - return 0, fmt.Errorf("failed to query Block") + return 0, fmt.Errorf("failed to query latest block") } // legacy diff --git a/relayer/chains/wasm/wasm_chain_processor.go b/relayer/chains/wasm/wasm_chain_processor.go index 4b7dc186a..172185bc6 100644 --- a/relayer/chains/wasm/wasm_chain_processor.go +++ b/relayer/chains/wasm/wasm_chain_processor.go @@ -58,13 +58,15 @@ type WasmChainProcessor struct { parsedGasPrices *sdk.DecCoins verifier *Verifier + + heightSnapshotChan chan struct{} } type Verifier struct { Header *types.LightBlock } -func NewWasmChainProcessor(log *zap.Logger, provider *WasmProvider, metrics *processor.PrometheusMetrics) *WasmChainProcessor { +func NewWasmChainProcessor(log *zap.Logger, provider *WasmProvider, metrics *processor.PrometheusMetrics, heightSnapshot chan struct{}) *WasmChainProcessor { return &WasmChainProcessor{ log: log.With(zap.String("chain_name", provider.ChainName()), zap.String("chain_id", provider.ChainId())), chainProvider: provider, @@ -74,6 +76,7 @@ func NewWasmChainProcessor(log *zap.Logger, provider *WasmProvider, metrics *pro connectionClients: make(map[string]string), channelConnections: make(map[string]string), metrics: metrics, + heightSnapshotChan: heightSnapshot, } } @@ -290,6 +293,8 @@ func (ccp *WasmChainProcessor) Run(ctx context.Context, initialBlockHistory uint select { case <-ctx.Done(): return nil + case <-ccp.heightSnapshotChan: + ccp.SnapshotHeight(ccp.getHeightToSave(persistence.latestHeight)) case <-ticker.C: ticker.Reset(persistence.minQueryLoopDuration) } @@ -364,7 +369,8 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer zap.Error(err), ) - ccp.SnapshotHeight(ccp.getHeightToSave(status.SyncInfo.LatestBlockHeight)) + // TODO: Save height when node status is false? + // ccp.SnapshotHeight(ccp.getHeightToSave(status.SyncInfo.LatestBlockHeight)) return nil } @@ -404,11 +410,6 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer chainID := ccp.chainProvider.ChainId() var latestHeader provider.IBCHeader - ht, takeSnapshot := ccp.shouldSnapshot(int(persistence.latestHeight)) - if takeSnapshot { - ccp.SnapshotHeight(ht) - } - for i := persistence.latestQueriedBlock + 1; i <= persistence.latestHeight; i++ { var eg errgroup.Group var blockRes *ctypes.ResultBlockResults @@ -509,18 +510,6 @@ func (ccp *WasmChainProcessor) queryCycle(ctx context.Context, persistence *quer return nil } -func (ccp *WasmChainProcessor) shouldSnapshot(height int) (int, bool) { - blockInterval := ccp.Provider().ProviderConfig().GetBlockInterval() - snapshotThreshold := common.ONE_HOUR / int(blockInterval) - - snapshotHeight := ccp.getHeightToSave(int64(height)) - - if snapshotHeight%snapshotThreshold == 0 { - return snapshotHeight, true - } - return 0, false -} - func (ccp *WasmChainProcessor) getHeightToSave(height int64) int { retryAfter := ccp.Provider().ProviderConfig().GetFirstRetryBlockAfter() ht := int(height - int64(retryAfter)) diff --git a/relayer/channel.go b/relayer/channel.go index 2f3f32b64..184ed599c 100644 --- a/relayer/channel.go +++ b/relayer/channel.go @@ -71,8 +71,8 @@ func (c *Chain) CreateOpenChannels( return processor.NewEventProcessor(). WithChainProcessors( - c.chainProcessor(c.log, nil), - dst.chainProcessor(c.log, nil), + c.chainProcessor(c.log, nil, nil), + dst.chainProcessor(c.log, nil, nil), ). WithPathProcessors(pp). WithInitialBlockHistory(0). @@ -121,8 +121,8 @@ func (c *Chain) CloseChannel( flushProcessor := processor.NewEventProcessor(). WithChainProcessors( - c.chainProcessor(c.log, nil), - dst.chainProcessor(c.log, nil), + c.chainProcessor(c.log, nil, nil), + dst.chainProcessor(c.log, nil, nil), ). WithPathProcessors(processor.NewPathProcessor( c.log, @@ -159,8 +159,8 @@ func (c *Chain) CloseChannel( return processor.NewEventProcessor(). WithChainProcessors( - c.chainProcessor(c.log, nil), - dst.chainProcessor(c.log, nil), + c.chainProcessor(c.log, nil, nil), + dst.chainProcessor(c.log, nil, nil), ). WithPathProcessors(processor.NewPathProcessor( c.log, diff --git a/relayer/connection.go b/relayer/connection.go index df784d504..9c64619cc 100644 --- a/relayer/connection.go +++ b/relayer/connection.go @@ -61,8 +61,8 @@ func (c *Chain) CreateOpenConnections( return connectionSrc, connectionDst, processor.NewEventProcessor(). WithChainProcessors( - c.chainProcessor(c.log, nil), - dst.chainProcessor(c.log, nil), + c.chainProcessor(c.log, nil, nil), + dst.chainProcessor(c.log, nil, nil), ). WithPathProcessors(pp). WithInitialBlockHistory(initialBlockHistory). diff --git a/relayer/strategies.go b/relayer/strategies.go index 920c49686..710589605 100644 --- a/relayer/strategies.go +++ b/relayer/strategies.go @@ -33,6 +33,27 @@ const ( TwoMB = 2 * 1024 * 1024 ) +func timerChannel(ctx context.Context, log *zap.Logger, timerChan map[string]chan struct{}, chains map[string]*Chain) { + ticker := time.NewTicker(time.Hour) + defer ticker.Stop() + for { + NamedLoop: + select { + case <-ticker.C: + for _, c := range chains { + _, err := c.ChainProvider.QueryLatestHeight(ctx) + if err != nil { + log.Warn("Failed getting status of chain", zap.String("chain_id", c.ChainID()), zap.Error(err)) + break NamedLoop + } + } + for _, c := range timerChan { + c <- struct{}{} + } + } + } +} + // StartRelayer starts the main relaying loop and returns a channel that will contain any control-flow related errors. func StartRelayer( ctx context.Context, @@ -49,13 +70,20 @@ func StartRelayer( metrics *processor.PrometheusMetrics, ) chan error { errorChan := make(chan error, 1) + chans := make(map[string]chan struct{}) + + for k := range chains { + chans[k] = make(chan struct{}) + } + + go timerChannel(ctx, log, chans, chains) switch processorType { case ProcessorEvents: chainProcessors := make([]processor.ChainProcessor, 0, len(chains)) - for _, chain := range chains { - chainProcessors = append(chainProcessors, chain.chainProcessor(log, metrics)) + for name, chain := range chains { + chainProcessors = append(chainProcessors, chain.chainProcessor(log, metrics, chans[name])) } ePaths := make([]path, len(paths)) @@ -116,7 +144,7 @@ type path struct { } // chainProcessor returns the corresponding ChainProcessor implementation instance for a pathChain. -func (chain *Chain) chainProcessor(log *zap.Logger, metrics *processor.PrometheusMetrics) processor.ChainProcessor { +func (chain *Chain) chainProcessor(log *zap.Logger, metrics *processor.PrometheusMetrics, timerChan chan struct{}) processor.ChainProcessor { // Handle new ChainProcessor implementations as cases here switch p := chain.ChainProvider.(type) { case *penumbraprocessor.PenumbraProvider: @@ -124,9 +152,9 @@ func (chain *Chain) chainProcessor(log *zap.Logger, metrics *processor.Prometheu case *cosmos.CosmosProvider: return cosmos.NewCosmosChainProcessor(log, p, metrics) case *icon.IconProvider: - return icon.NewIconChainProcessor(log, p, metrics) + return icon.NewIconChainProcessor(log, p, metrics, timerChan) case *wasm.WasmProvider: - return wasm.NewWasmChainProcessor(log, p, metrics) + return wasm.NewWasmChainProcessor(log, p, metrics, timerChan) default: panic(fmt.Errorf("unsupported chain provider type: %T", chain.ChainProvider)) } From 09ee424a2c6f7d6a7d72ea61894efc55e2aa5b07 Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:13:54 +0545 Subject: [PATCH 11/15] fix: timeout not being called on cosmos because of requestTimeout on icon (#134) Co-authored-by: izyak --- relayer/chains/wasm/tx.go | 1 - relayer/processor/path_end_runtime.go | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/relayer/chains/wasm/tx.go b/relayer/chains/wasm/tx.go index fdd7f7581..711fe02ee 100644 --- a/relayer/chains/wasm/tx.go +++ b/relayer/chains/wasm/tx.go @@ -214,7 +214,6 @@ func (ap *WasmProvider) ValidatePacket(msgTransfer provider.PacketInfo, latest p revisionNumber := 0 latestClientTypesHeight := clienttypes.NewHeight(uint64(revisionNumber), latest.Height) if !msgTransfer.TimeoutHeight.IsZero() && latestClientTypesHeight.GTE(msgTransfer.TimeoutHeight) { - fmt.Println("packet timeout failed finally ", msgTransfer.TimeoutHeight) return provider.NewTimeoutHeightError(latest.Height, msgTransfer.TimeoutHeight.RevisionHeight) } diff --git a/relayer/processor/path_end_runtime.go b/relayer/processor/path_end_runtime.go index d4a8445aa..c81e2c19f 100644 --- a/relayer/processor/path_end_runtime.go +++ b/relayer/processor/path_end_runtime.go @@ -450,6 +450,10 @@ func (pathEnd *pathEndRuntime) shouldSendPacketMessage(message packetIBCMessage, pathEndForHeight = pathEnd } + if eventType == chantypes.EventTypeTimeoutPacket && IsBTPLightClient(pathEnd.clientState) { + pathEndForHeight = counterparty + } + if message.info.Height >= pathEndForHeight.latestBlock.Height { pathEnd.log.Debug("Waiting to relay packet message until counterparty height has incremented", zap.String("event_type", eventType), @@ -462,7 +466,7 @@ func (pathEnd *pathEndRuntime) shouldSendPacketMessage(message packetIBCMessage, } // allow to send only counterparty chain has consensusState - if IsBTPLightClient(pathEnd.clientState) && common.EventRequiresClientUpdate[message.eventType] == true { + if IsBTPLightClient(pathEnd.clientState) && common.EventRequiresClientUpdate[eventType] { if pathEnd.clientState.ConsensusHeight.RevisionHeight < message.info.Height { pathEnd.log.Debug("Waiting to relay packet message until clientState is updated", zap.Inline(message), From e3c8645fd3c7e0f70a4d5910bf43d3a7ea620ffd Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Mon, 14 Aug 2023 10:24:42 +0545 Subject: [PATCH 12/15] feat: icon key refactor (#140) * fix: change way of handling keystore on icon * feat: wallet and deletion * fix: handle keys delete on icon * chore: config sample files updated --------- Co-authored-by: izyak --- cmd/keys.go | 9 +- examples/config_IBC_ICON.yaml | 14 +- examples/demo/configs/chains/ibc-icon.json | 30 ++-- examples/demo/configs/chains/ibc-wasm.json | 29 ++-- examples/demo/configs/paths/icon-archway.json | 2 +- relayer/chain.go | 2 +- relayer/chains/cosmos/keys.go | 2 +- relayer/chains/icon/keys.go | 144 ++++++++++++------ relayer/chains/icon/keys_test.go | 57 +++---- relayer/chains/icon/provider.go | 49 +++--- relayer/chains/icon/query.go | 2 +- relayer/chains/icon/tx.go | 10 +- relayer/chains/mock/mock_chain_processor.go | 2 +- relayer/chains/penumbra/keys.go | 2 +- relayer/chains/wasm/keys.go | 2 +- relayer/provider/provider.go | 2 +- 16 files changed, 216 insertions(+), 142 deletions(-) diff --git a/cmd/keys.go b/cmd/keys.go index 0ddd447d4..3f7380141 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -32,6 +32,7 @@ import ( const ( flagCoinType = "coin-type" flagAlgo = "signing-algorithm" + flagPassword = "password" defaultCoinType uint32 = sdk.CoinType ) @@ -97,6 +98,11 @@ $ %s k a cosmoshub testkey`, appName, appName, appName)), return err } + password, err := cmdFlags.GetString(flagPassword) + if err != nil { + return err + } + if algo == "" { if ccp, ok := chain.ChainProvider.(*cosmos.CosmosProvider); ok { algo = ccp.PCfg.SigningAlgorithm @@ -105,7 +111,7 @@ $ %s k a cosmoshub testkey`, appName, appName, appName)), } } - ko, err := chain.ChainProvider.AddKey(keyName, uint32(coinType), algo) + ko, err := chain.ChainProvider.AddKey(keyName, uint32(coinType), algo, password) if err != nil { return fmt.Errorf("failed to add key: %w", err) } @@ -121,6 +127,7 @@ $ %s k a cosmoshub testkey`, appName, appName, appName)), } cmd.Flags().Int32(flagCoinType, -1, "coin type number for HD derivation") cmd.Flags().String(flagAlgo, "", "signing algorithm for key (secp256k1, sr25519)") + cmd.Flags().String(flagPassword, "x", "icon keystore password") return cmd } diff --git a/examples/config_IBC_ICON.yaml b/examples/config_IBC_ICON.yaml index f6a917792..d0f69c19b 100644 --- a/examples/config_IBC_ICON.yaml +++ b/examples/config_IBC_ICON.yaml @@ -7,7 +7,7 @@ chains: archway: type: wasm value: - key-directory: /home/user/.relayer/keys/localnet + key-directory: /home/user/.relayer/keys key: relayWallet chain-id: localnet rpc-addr: http://localhost:26657 @@ -24,24 +24,24 @@ chains: extra-codecs: [] coin-type: 0 broadcast-mode: batch - ibc-handler-address: archway14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sy85n2u + ibc-handler-address: archway1pvrwmjuusn9wh34j7y520g8gumuy9xtl3gvprlljfdpwju3x7ucszwhc7n first-retry-block-after: 0 start-height: 0 - block-interval: 6000 + block-interval: 3000 icon: type: icon value: - key: "" + key-directory: /home/user/.relayer/keys chain-id: ibc-icon rpc-addr: http://localhost:9082/api/v3/ timeout: 30s - keystore: /home/user/keystore/godWallet.json + keystore: godWallet password: gochain icon-network-id: 3 btp-network-id: 1 btp-network-type-id: 1 start-height: 0 - ibc-handler-address: cxa1daa337788364a72644860a5097e3eef7a5d416 + ibc-handler-address: cxbeb5929616e0dbd2fec1e6e950ab09e45e6fb25a first-retry-block-after: 0 block-interval: 2000 paths: @@ -56,4 +56,4 @@ paths: connection-id: connection-0 src-channel-filter: rule: "" - channel-list: [] \ No newline at end of file + channel-list: [] diff --git a/examples/demo/configs/chains/ibc-icon.json b/examples/demo/configs/chains/ibc-icon.json index 851e0a78e..b607006e1 100644 --- a/examples/demo/configs/chains/ibc-icon.json +++ b/examples/demo/configs/chains/ibc-icon.json @@ -1,16 +1,18 @@ { - "type": "icon", - "value": { - "chain-id": "ibc-icon", - "rpc-addr": "http://localhost:9082/api/v3/", - "timeout": "30s", - "keystore":"/Users/viveksharmapoudel/keystore/godWallet.json", - "password":"gochain", - "btp-network-id":2, - "icon-network-id":3, - "start-height":0, - "ibc-handler-address":"cxfffe383e4780084e48e477935099b03193d952fe", - "block-interval":2000, - "first-retry-block-after":8 - } + "type": "icon", + "value": { + "key-directory": "/home/user/.relayer/keys", + "chain-id": "ibc-icon", + "rpc-addr": "http://localhost:9082/api/v3/", + "timeout": "30s", + "keystore": "godWallet", + "password": "gochain", + "icon-network-id": 3, + "btp-network-id": 2, + "btp-network-type-id": 1, + "start-height": 0, + "ibc-handler-address": "cxbeb5929616e0dbd2fec1e6e950ab09e45e6fb25a", + "first-retry-block-after": 0, + "block-interval": 2000 + } } \ No newline at end of file diff --git a/examples/demo/configs/chains/ibc-wasm.json b/examples/demo/configs/chains/ibc-wasm.json index b4f19e508..d0f6bbe3a 100644 --- a/examples/demo/configs/chains/ibc-wasm.json +++ b/examples/demo/configs/chains/ibc-wasm.json @@ -1,25 +1,26 @@ { "type": "wasm", "value": { - "key": "default", - "chain-id": "test-1", - "rpc-addr": "https://rpc.constantine-2.archway.tech:443", - "key-directory":"/Users/viveksharmapoudel/.relayer/keys/test-1", - "grpc-addr": "", - "account-prefix": "neutron", + "key-directory": "/home/user/.relayer/keys", + "key": "relayWallet", + "chain-id": "localnet", + "rpc-addr": "http://localhost:26657", + "account-prefix": "archway", "keyring-backend": "test", "gas-adjustment": 1.5, - "gas-prices": "0.02uconst", + "gas-prices": "0.025stake", + "min-gas-amount": 1000000, "debug": true, "timeout": "20s", + "block-timeout": "", "output-format": "json", "sign-mode": "direct", - "ibc-handler-address":"neutron1fde8lfydxgwg6p7xe9ugx5a6ysj37zfvn9m9z5rxt4mqvdcneczsczq2a4", + "extra-codecs": [], + "coin-type": 0, "broadcast-mode": "batch", - "block-timeout": "", - "start-height":0, - "block-interval": 6000 + "ibc-handler-address": "archway1pvrwmjuusn9wh34j7y520g8gumuy9xtl3gvprlljfdpwju3x7ucszwhc7n", + "first-retry-block-after": 0, + "start-height": 0, + "block-interval": 3000 } -} - - +} \ No newline at end of file diff --git a/examples/demo/configs/paths/icon-archway.json b/examples/demo/configs/paths/icon-archway.json index 51d4d7f0c..9a12e583a 100644 --- a/examples/demo/configs/paths/icon-archway.json +++ b/examples/demo/configs/paths/icon-archway.json @@ -3,7 +3,7 @@ "chain-id": "ibc-icon" }, "dst": { - "chain-id": "constantine-2" + "chain-id": "localnet" }, "src-channel-filter": { "rule": null, diff --git a/relayer/chain.go b/relayer/chain.go index bb0195fb8..d7948786a 100644 --- a/relayer/chain.go +++ b/relayer/chain.go @@ -149,7 +149,7 @@ func (c *Chain) CreateTestKey() error { if c.ChainProvider.KeyExists(c.ChainProvider.Key()) { return fmt.Errorf("key {%s} exists for chain {%s}", c.ChainProvider.Key(), c.ChainID()) } - _, err := c.ChainProvider.AddKey(c.ChainProvider.Key(), defaultCoinType, defaultAlgo) + _, err := c.ChainProvider.AddKey(c.ChainProvider.Key(), defaultCoinType, defaultAlgo, "") return err } diff --git a/relayer/chains/cosmos/keys.go b/relayer/chains/cosmos/keys.go index 0ccdd0938..6cd4a60bf 100644 --- a/relayer/chains/cosmos/keys.go +++ b/relayer/chains/cosmos/keys.go @@ -61,7 +61,7 @@ func (cc *CosmosProvider) KeystoreCreated(path string) bool { // AddKey generates a new mnemonic which is then converted to a private key and BIP-39 HD Path and persists it to the keystore. // It fails if there is an existing key with the same address. -func (cc *CosmosProvider) AddKey(name string, coinType uint32, signingAlgorithm string) (output *provider.KeyOutput, err error) { +func (cc *CosmosProvider) AddKey(name string, coinType uint32, signingAlgorithm string, password string) (output *provider.KeyOutput, err error) { ko, err := cc.KeyAddOrRestore(name, coinType, signingAlgorithm) if err != nil { return nil, err diff --git a/relayer/chains/icon/keys.go b/relayer/chains/icon/keys.go index 9bfda64a6..878422c61 100644 --- a/relayer/chains/icon/keys.go +++ b/relayer/chains/icon/keys.go @@ -1,9 +1,12 @@ package icon import ( + "encoding/json" "fmt" "log" "os" + "path" + "strings" "github.com/cosmos/relayer/v2/relayer/provider" glcrypto "github.com/icon-project/goloop/common/crypto" @@ -12,7 +15,7 @@ import ( ) func (cp *IconProvider) CreateKeystore(path string) error { - _, e := generateKeystoreWithPassword(path, []byte("gochain")) + _, e := cp.generateKeystoreWithPassword(path, "gochain") return e } @@ -20,50 +23,82 @@ func (cp *IconProvider) KeystoreCreated(path string) bool { return false } -func (cp *IconProvider) AddKey(name string, coinType uint32, signingAlgorithm string) (output *provider.KeyOutput, err error) { - return nil, fmt.Errorf("Not implemented on icon") +func (cp *IconProvider) AddKey(name string, coinType uint32, signingAlgorithm string, password string) (output *provider.KeyOutput, err error) { + w, err := cp.generateKeystoreWithPassword(name, password) + if err != nil { + return nil, err + } + return &provider.KeyOutput{ + Address: w.Address().String(), + Mnemonic: "", + }, nil } func (cp *IconProvider) RestoreKey(name, mnemonic string, coinType uint32, signingAlgorithm string) (address string, err error) { - return "", fmt.Errorf("Not implemented on icon") + return "", fmt.Errorf("not implemented on icon") } func (cp *IconProvider) ShowAddress(name string) (address string, err error) { - return cp.wallet.Address().String(), nil + dirPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId(), fmt.Sprintf("%s.json", name)) + return getAddrFromKeystore(dirPath) } func (cp *IconProvider) ListAddresses() (map[string]string, error) { - return nil, fmt.Errorf("Not implemented on icon") + dirPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId()) + dirEntry, err := os.ReadDir(dirPath) + if err != nil { + return nil, err + } + + addrMap := make(map[string]string) + for _, file := range dirEntry { + if !file.IsDir() && strings.HasSuffix(file.Name(), ".json") { + ksFile := path.Join(dirPath, file.Name()) + addr, err := getAddrFromKeystore(ksFile) + if err != nil { + continue + } + addrMap[strings.TrimSuffix(file.Name(), ".json")] = addr + } + } + return addrMap, nil } func (cp *IconProvider) DeleteKey(name string) error { ok := cp.KeyExists(name) if !ok { - return fmt.Errorf("Wallet does not exist") + return fmt.Errorf("wallet does not exist") } - cp.wallet = nil - return nil + + dirPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId(), fmt.Sprintf("%s.json", name)) + _, err := os.Stat(dirPath) + if err == nil { + if err := os.Remove(dirPath); err != nil { + return err + } + return nil + } + return fmt.Errorf("fail to delete wallet") } func (cp *IconProvider) KeyExists(name string) bool { - return cp.wallet != nil + walletPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId(), fmt.Sprintf("%s.json", name)) + _, err := os.ReadFile(walletPath) + if err != nil && os.IsNotExist(err) { + return false + } else if err != nil { + panic("key does not exist") + } + return true } func (cp *IconProvider) ExportPrivKeyArmor(keyName string) (armor string, err error) { - return "", fmt.Errorf("Not implemented on icon") -} - -func (cp *IconProvider) AddIconKey(name string, password []byte) (module.Wallet, error) { - w, err := generateKeystoreWithPassword(name, password) - if err != nil { - return nil, err - } - cp.AddWallet(w) - return w, nil + return "", fmt.Errorf("not implemented on icon") } -func (cp *IconProvider) RestoreIconKeyStore(path string, password []byte) (module.Wallet, error) { - ksByte, err := os.ReadFile(path) +func (cp *IconProvider) RestoreIconKeyStore(name string, password []byte) (module.Wallet, error) { + walletPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId(), fmt.Sprintf("%s.json", name)) + ksByte, err := os.ReadFile(walletPath) if err != nil { return nil, err } @@ -71,11 +106,11 @@ func (cp *IconProvider) RestoreIconKeyStore(path string, password []byte) (modul if err != nil { return nil, err } - cp.AddWallet(w) return w, nil } -func (cp *IconProvider) RestoreIconPrivateKey(pk []byte) (module.Wallet, error) { +// This method does not save keystore +func (cp *IconProvider) RestoreFromPrivateKey(name string, pk []byte) (module.Wallet, error) { pKey, err := glcrypto.ParsePrivateKey(pk) if err != nil { return nil, err @@ -84,37 +119,58 @@ func (cp *IconProvider) RestoreIconPrivateKey(pk []byte) (module.Wallet, error) if err != nil { return nil, err } - cp.AddWallet(w) return w, nil } -func (cp *IconProvider) KeyExistsIcon() bool { - return cp.wallet != nil +func (cp *IconProvider) generateKeystoreWithPassword(name string, password string) (module.Wallet, error) { + w := wallet.New() + ks, err := wallet.KeyStoreFromWallet(w, []byte(password)) + if err != nil { + log.Panicf("Failed to generate keystore. Err %+v", err) + return nil, err + } + + if err := cp.saveWallet(name, ks); err != nil { + return nil, err + } + + return w, nil } -func (cp *IconProvider) DeleteKeyIcon() error { - ok := cp.KeyExistsIcon() - if !ok { - return fmt.Errorf("Wallet does not exist") +func (cp *IconProvider) saveWallet(name string, ks []byte) error { + dirPath := path.Join(cp.PCfg.KeyDirectory, cp.ChainId()) + _, err := os.Stat(dirPath) + if os.IsNotExist(err) { + err := os.MkdirAll(dirPath, 0755) + if err != nil { + panic(err) + } + } else if err != nil { + return err + } + if err := os.WriteFile(fmt.Sprintf("%s/%s.json", dirPath, name), ks, 0600); err != nil { + log.Panicf("Fail to write keystore err=%+v", err) + return err } - cp.wallet = nil return nil } -func (cp *IconProvider) ShowAddressIcon() (address string, err error) { - ok := cp.KeyExistsIcon() - if !ok { - return "", fmt.Errorf("Wallet does not exist") - } - return cp.wallet.Address().String(), nil +type OnlyAddr struct { + Address string `json:"address"` } -func generateKeystoreWithPassword(path string, password []byte) (module.Wallet, error) { - w := wallet.New() - _, err := wallet.KeyStoreFromWallet(w, password) +func getAddrFromKeystore(keystorePath string) (string, error) { + + ksFile, err := os.ReadFile(keystorePath) if err != nil { - log.Panicf("Failed to generate keystore. Err %+v", err) - return nil, err + return "", err } - return w, nil + + var a OnlyAddr + err = json.Unmarshal(ksFile, &a) + if err != nil { + return "", err + } + return a.Address, nil + } diff --git a/relayer/chains/icon/keys_test.go b/relayer/chains/icon/keys_test.go index 9b35e2ee6..108f14242 100644 --- a/relayer/chains/icon/keys_test.go +++ b/relayer/chains/icon/keys_test.go @@ -1,7 +1,6 @@ package icon import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -23,36 +22,40 @@ func TestCreateKeystore(t *testing.T) { } func TestAddIconKeyStore(t *testing.T) { - kwName := "testWallet.json" + kwName := "testWallet" p := &IconProvider{ client: NewClient(ENDPOINT, &zap.Logger{}), + PCfg: &IconProviderConfig{ + KeyDirectory: "../../../env", + ChainID: "ibc-icon", + }, } - w, err := p.AddIconKey(kwName, []byte("gochain")) + w, err := p.AddKey(kwName, 0, "", "gochain") require.NoError(t, err, "err creating keystore with password") - assert.Equal(t, w.Address(), p.wallet.Address()) - assert.Equal(t, w, p.wallet) -} - -func TestRestoreIconKeyStore(t *testing.T) { + addr, err := p.ShowAddress(kwName) + assert.NoError(t, err) - kwName := "../../../env/godWallet.json" - - pcfg := &IconProviderConfig{ - Keystore: kwName, - Password: "gochain", - Timeout: "20s", - ChainName: "icon", - StartHeight: 10, - IbcHandlerAddress: "cxb6b5791be0b5ef67063b3c10b840fb81514db2fd", - BlockInterval: 2000, - } - p, err := pcfg.NewProvider(zap.NewNop(), "not_correct", false, "icon") - require.NoError(t, err) - iconp := p.(*IconProvider) - fmt.Println(iconp) - w, err := iconp.RestoreIconKeyStore(kwName, []byte("gochain")) - require.NoError(t, err) - - assert.Equal(t, w, iconp.wallet) + // assert.Equal(t, w.Address, p.wallet.Address()) + assert.Equal(t, w.Address, addr) } + +// func TestRestoreIconKeyStore(t *testing.T) { + +// pcfg := &IconProviderConfig{ +// KeyDirectory: "../../../env", +// Keystore: "testWallet", +// Password: "gochain", +// Timeout: "20s", +// ChainName: "icon", +// StartHeight: 10, +// IbcHandlerAddress: "cxb6b5791be0b5ef67063b3c10b840fb81514db2fd", +// BlockInterval: 2000, +// } +// p, err := pcfg.NewProvider(zap.NewNop(), "not_correct", false, "icon") +// require.NoError(t, err) +// iconp := p.(*IconProvider) +// _, err = iconp.RestoreIconKeyStore("testWallet", []byte("gochain")) +// require.NoError(t, err) + +// } diff --git a/relayer/chains/icon/provider.go b/relayer/chains/icon/provider.go index 4cec9c55d..9d1ad4508 100644 --- a/relayer/chains/icon/provider.go +++ b/relayer/chains/icon/provider.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "fmt" - "os" "sync" "time" @@ -14,7 +13,6 @@ import ( "github.com/cosmos/relayer/v2/relayer/processor" "github.com/cosmos/relayer/v2/relayer/provider" "github.com/icon-project/IBC-Integration/libraries/go/common/icon" - "github.com/icon-project/goloop/common/wallet" "github.com/icon-project/goloop/module" "go.uber.org/zap" @@ -48,8 +46,12 @@ var ( NOT_IMPLEMENTED = " :: Not implemented for ICON" ) +/* + * The provider assumes the key is in + * KeyDirectory/Keystore.json + */ type IconProviderConfig struct { - Key string `json:"key" yaml:"key"` + KeyDirectory string `json:"key-directory" yaml:"key-directory"` ChainName string `json:"-" yaml:"-"` ChainID string `json:"chain-id" yaml:"chain-id"` RPCAddr string `json:"rpc-addr" yaml:"rpc-addr"` @@ -96,40 +98,22 @@ func (pp *IconProviderConfig) GetFirstRetryBlockAfter() uint64 { func (pp *IconProviderConfig) NewProvider(log *zap.Logger, homepath string, debug bool, chainName string) (provider.ChainProvider, error) { pp.ChainName = chainName - if _, err := os.Stat(pp.Keystore); err != nil { - return nil, err - } if err := pp.Validate(); err != nil { return nil, err } - ksByte, err := os.ReadFile(pp.Keystore) - if err != nil { - return nil, err - } - - wallet, err := wallet.NewFromKeyStore(ksByte, []byte(pp.Password)) - if err != nil { - return nil, err - } - codec := MakeCodec(ModuleBasics, []string{}) return &IconProvider{ log: log.With(zap.String("chain_id", pp.ChainID)), client: NewClient(pp.getRPCAddr(), log), PCfg: pp, - wallet: wallet, StartHeight: uint64(pp.StartHeight), codec: codec, }, nil } -func (icp *IconProvider) AddWallet(w module.Wallet) { - icp.wallet = w -} - func (pp IconProviderConfig) getRPCAddr() string { return pp.RPCAddr } @@ -143,7 +127,6 @@ type IconProvider struct { PCfg *IconProviderConfig txMu sync.Mutex client *Client - wallet module.Wallet metrics *processor.PrometheusMetrics codec Codec StartHeight uint64 @@ -208,6 +191,20 @@ func (h IconIBCHeader) ShouldUpdateWithZeroMessage() bool { //ChainProvider Methods func (icp *IconProvider) Init(ctx context.Context) error { + // if _, err := os.Stat(icp.PCfg.Keystore); err != nil { + // return err + // } + + // ksByte, err := os.ReadFile(icp.PCfg.Keystore) + // if err != nil { + // return err + // } + + // wallet, err := wallet.NewFromKeyStore(ksByte, []byte(icp.PCfg.Password)) + // if err != nil { + // return err + // } + // icp.AddWallet(wallet) return nil } @@ -438,11 +435,15 @@ func (icp *IconProvider) CommitmentPrefix() commitmenttypes.MerklePrefix { } func (icp *IconProvider) Key() string { - return "" + return icp.PCfg.Keystore +} + +func (icp *IconProvider) Wallet() (module.Wallet, error) { + return icp.RestoreIconKeyStore(icp.PCfg.Keystore, []byte(icp.PCfg.Password)) } func (icp *IconProvider) Address() (string, error) { - return icp.wallet.Address().String(), nil + return icp.ShowAddress(icp.PCfg.Keystore) } func (icp *IconProvider) Timeout() string { diff --git a/relayer/chains/icon/query.go b/relayer/chains/icon/query.go index 7ace0469c..ce6b892f1 100644 --- a/relayer/chains/icon/query.go +++ b/relayer/chains/icon/query.go @@ -57,7 +57,7 @@ func (icp *IconProvider) prepareCallParams(methodName string, param map[string]i } callParam := &types.CallParam{ - FromAddress: types.NewAddress(icp.wallet.Address().Bytes()), + FromAddress: types.Address(fmt.Sprintf("hx%s", strings.Repeat("0", 40))), ToAddress: types.Address(icp.PCfg.IbcHandlerAddress), DataType: "call", Data: callData, diff --git a/relayer/chains/icon/tx.go b/relayer/chains/icon/tx.go index 9744bef03..21bd2452c 100644 --- a/relayer/chains/icon/tx.go +++ b/relayer/chains/icon/tx.go @@ -708,9 +708,13 @@ func (icp *IconProvider) SendIconTransaction( asyncCtx context.Context, asyncCallback func(*provider.RelayerTxResponse, error)) error { m := msg.(*IconMessage) + wallet, err := icp.Wallet() + if err != nil { + return err + } txParam := &types.TransactionParam{ Version: types.NewHexInt(types.JsonrpcApiVersion), - FromAddress: types.Address(icp.wallet.Address().String()), + FromAddress: types.Address(wallet.Address().String()), ToAddress: types.Address(icp.PCfg.IbcHandlerAddress), NetworkID: types.NewHexInt(icp.PCfg.ICONNetworkID), StepLimit: types.NewHexInt(int64(defaultStepLimit)), @@ -721,10 +725,10 @@ func (icp *IconProvider) SendIconTransaction( }, } - if err := icp.client.SignTransaction(icp.wallet, txParam); err != nil { + if err := icp.client.SignTransaction(wallet, txParam); err != nil { return err } - _, err := icp.client.SendTransaction(txParam) + _, err = icp.client.SendTransaction(txParam) if err != nil { return err } diff --git a/relayer/chains/mock/mock_chain_processor.go b/relayer/chains/mock/mock_chain_processor.go index 001de0831..b311c687a 100644 --- a/relayer/chains/mock/mock_chain_processor.go +++ b/relayer/chains/mock/mock_chain_processor.go @@ -50,7 +50,7 @@ func NewMockChainProcessor(ctx context.Context, log *zap.Logger, chainID string, } chainProvider, _ := chainProviderCfg.NewProvider(zap.NewNop(), "/tmp", true, "mock-chain-name-"+chainID) _ = chainProvider.Init(ctx) - _, _ = chainProvider.AddKey(chainProvider.Key(), 118, string(hd.Secp256k1Type)) + _, _ = chainProvider.AddKey(chainProvider.Key(), 118, string(hd.Secp256k1Type), "") return &MockChainProcessor{ log: log, chainID: chainID, diff --git a/relayer/chains/penumbra/keys.go b/relayer/chains/penumbra/keys.go index aa09fa7b1..d08efe96d 100644 --- a/relayer/chains/penumbra/keys.go +++ b/relayer/chains/penumbra/keys.go @@ -58,7 +58,7 @@ func (cc *PenumbraProvider) KeystoreCreated(path string) bool { // AddKey generates a new mnemonic which is then converted to a private key and BIP-39 HD Path and persists it to the keystore. // It fails if there is an existing key with the same address. -func (cc *PenumbraProvider) AddKey(name string, coinType uint32, signingAlgorithm string) (output *provider.KeyOutput, err error) { +func (cc *PenumbraProvider) AddKey(name string, coinType uint32, signingAlgorithm string, password string) (output *provider.KeyOutput, err error) { ko, err := cc.KeyAddOrRestore(name, coinType) if err != nil { return nil, err diff --git a/relayer/chains/wasm/keys.go b/relayer/chains/wasm/keys.go index d0f8b4832..c7b8e2991 100644 --- a/relayer/chains/wasm/keys.go +++ b/relayer/chains/wasm/keys.go @@ -58,7 +58,7 @@ func (cc *WasmProvider) KeystoreCreated(path string) bool { // AddKey generates a new mnemonic which is then converted to a private key and BIP-39 HD Path and persists it to the keystore. // It fails if there is an existing key with the same address. -func (cc *WasmProvider) AddKey(name string, coinType uint32, signingAlgorithm string) (output *provider.KeyOutput, err error) { +func (cc *WasmProvider) AddKey(name string, coinType uint32, signingAlgorithm string, password string) (output *provider.KeyOutput, err error) { ko, err := cc.KeyAddOrRestore(name, coinType) if err != nil { return nil, err diff --git a/relayer/provider/provider.go b/relayer/provider/provider.go index 56099ba59..a548eae24 100644 --- a/relayer/provider/provider.go +++ b/relayer/provider/provider.go @@ -220,7 +220,7 @@ func (r RelayerTxResponse) MarshalLogObject(enc zapcore.ObjectEncoder) error { type KeyProvider interface { CreateKeystore(path string) error KeystoreCreated(path string) bool - AddKey(name string, coinType uint32, signingAlgorithm string) (output *KeyOutput, err error) + AddKey(name string, coinType uint32, signingAlgorithm string, password string) (output *KeyOutput, err error) RestoreKey(name, mnemonic string, coinType uint32, signingAlgorithm string) (address string, err error) ShowAddress(name string) (address string, err error) ListAddresses() (map[string]string, error) From 24aa2afc5be038285ebe0b35bed07fe0d7e03b83 Mon Sep 17 00:00:00 2001 From: izyak <76203436+izyak@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:41:58 +0545 Subject: [PATCH 13/15] docs: update readme for ibc-icon-integration (#141) * docs: update readme for ibc-icon-integration * chore: cleanup * docs: add example for icon to edit keys on config * docs: add command for client creating and handshaking * chore: change addresses for security notice * chore: social site --------- Co-authored-by: izyak --- README.md | 150 +++++++++++++-------- examples/demo/configs/chains/ibc-icon.json | 2 +- 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 29aea88af..10c46b32f 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,13 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n --- +## Demo +- The easiest way would be to follow the guide in [this repo](https://github.com/izyak/icon-ibc/tree/master) to setup relay for icon ibc integration. It has all the relevant scripts setup, and you can start the relay using a single command. +- There is E2E tests demo for icon ibc integration [here](https://github.com/icon-project/IBC-Integration/blob/main/docs/e2e_test_setup.md) +--- + ## Table Of Contents -- [Basic Usage - Relaying Across Chains](#Basic-Usage---Relaying-Packets-Across-Chains) +- [Basic Usage - Relaying Across Chains](#basic-usage---relaying-packets-across-chains) - [Create Path Across Chains](./docs/create-path-across-chain.md) - [Advanced Usage](./docs/advanced_usage.md) - [Troubleshooting](./docs/troubleshooting.md) @@ -56,7 +61,7 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n ```shell $ git clone https://github.com/cosmos/relayer.git - $ cd relayer && git checkout v2.3.0 + $ cd relayer $ make install ``` @@ -82,57 +87,63 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n To omit the memo entirely, including the default value of `rly(VERSION)`, use `-` for the memo. 3. **Configure the chains you want to relay between.** - - In our example, we will configure the relayer to operate on the canonical path between the Cosmos Hub and Osmosis.
- The `rly chains add` command fetches chain meta-data from the [chain-registry](https://github.com/cosmos/chain-registry) and adds it to your config file. + In out example, we will configure the relayer to operate between ICON and Archway. +
+ To add the chain config files manually, example config files have been included [here](./examples/demo/configs/chains/) Modify the config file as per your requirements and run the following command: ```shell - $ rly chains add cosmoshub osmosis - ``` - - Adding chains from the chain-registry randomly selects an RPC address from the registry entry. - If you are running your own node, manually go into the config and adjust the `rpc-addr` setting. - - > NOTE: `rly chains add` will check the liveliness of the available RPC endpoints for that chain in the chain-registry. - > It is possible that the command will fail if none of these RPC endpoints are available. In this case, you will want to manually add the chain config. - - To add the chain config files manually, example config files have been included [here](https://github.com/cosmos/relayer/tree/main/docs/example-configs/) - ```shell - $ rly chains add --url https://raw.githubusercontent.com/cosmos/relayer/main/docs/example-configs/cosmoshub-4.json cosmoshub - $ rly chains add --url https://raw.githubusercontent.com/cosmos/relayer/main/docs/example-configs/osmosis-1.json osmosis + $ rly chains add icon --file _path_to_/examples/demo/configs/chains/ibc-icon.json + $ rly chains add archway --file _path_to_/examples/demo/configs/chains/ibc-archway.json ``` 4. **Import OR create new keys for the relayer to use when signing and relaying transactions.** + + - For Cosmos chains: - >`key-name` is an identifier of your choosing. + >`key-name` is an identifier of your choosing. - If you need to generate a new private key you can use the `add` subcommand. + If you need to generate a new private key you can use the `add` subcommand. - ```shell - $ rly keys add cosmoshub [key-name] - $ rly keys add osmosis [key-name] - ``` - - If you already have a private key and want to restore it from your mnemonic you can use the `restore` subcommand. + ```shell + $ rly keys add archway [key-name] + ``` + + If you already have a private key and want to restore it from your mnemonic you can use the `restore` subcommand. - ```shell - $ rly keys restore cosmoshub [key-name] "mnemonic words here" - $ rly keys restore osmosis [key-name] "mnemonic words here" - ``` + ```shell + $ rly keys restore archway [key-name] "mnemonic words here" + ``` + - For Icon chain + To generate a new wallet for icon, you can use `add` subcommmand with password flag. If you do not supply `--password` flag, the default password is `x` + ```shell + $ rly keys add icon [key-name] --password "password" + ``` -5. **Edit the relayer's `key` values in the config file to match the `key-name`'s chosen above.** - >This step is necessary if you chose a `key-name` other than "default" +5. **Edit the relayer's `key` values in the config file to match the `key-name`'s chosen above.** + - *For Archway* + >This step is necessary if you chose a `key-name` other than "default" - Example: + Example: + ```yaml - - type: cosmos - value: - key: YOUR-KEY-NAME-HERE - chain-id: cosmoshub-4 - rpc-addr: http://localhost:26657 + - type: wasm + value: + key: YOUR-KEY-NAME-HERE + chain-id: localnet + rpc-addr: http://localhost:26657 ``` + - *For Icon* + + ```yaml + - type: icon + value: + keystore: YOUR-KEY-NAME-HERE + password: YOUR-KEY-PASSWORD-HERE + chain-id: ibc-icon + ``` + 6. **Ensure the keys associated with the configured chains are funded.** @@ -142,8 +153,8 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n You can query the balance of each configured key by running: ```shell - $ rly q balance cosmoshub - $ rly q balance osmosis + $ rly q balance icon [key-name] + $ rly q balance archway [key-name] ``` 7. **Configure path meta-data in config file.** @@ -151,17 +162,36 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n We have the chain meta-data configured, now we need path meta-data. For more info on `path` terminology visit [here](docs/troubleshooting.md). >NOTE: Thinking of chains in the config as "source" and "destination" can be confusing. Be aware that most path are bi-directional. + To add the chain config files manually, example config files have been included [here](./examples/demo/configs/paths/icon-archway.json) . Modify this file as per your requirements and run the following command.
- `rly paths fetch` will check for IBC path meta data from the [chain-registry](https://github.com/cosmos/chain-registry/tree/master/_IBC) and add these paths to your config file. + ```shell + $ rly paths add [chain-id-1] [chain-id-2] [path-name] --file _path_to/ibc-relay/examples/demo/configs/paths/icon-archway.json + ``` +8. **Client Creation and Handshaking [Optional]** +
+ If you want to create your own client, channels and connection to relay between chains, run the following command: +
- ```shell - $ rly paths fetch - ``` - > **NOTE:** Don't see the path metadata for paths you want to relay on? - > Please open a PR to add this metadata to the GitHub repo! + To create clients between chains + > Ensure that [btp-height] is a valid bto block height. This height will be used to create client for icon's counterparty chain . + ```shell + rly tx clients [path-name] --client-tp "10000000m" --btp-block-height [btp-height] + ``` + + To create connection + ```shell + rly tx conn [path-name] + ``` -8. #### **Configure the channel filter.** + To create channels + ```sh + rly tx chan [path-name] --src-port=[src-port] --dst-port=[dst-port] + ``` + + This step can entirely be skipped if connection and channel exists between 2 chains you want to relay. Ensure that client-id and connection-id are provided in the paths for this. + +9. #### **Configure the channel filter [Optional]** By default, the relayer will relay packets over all channels on a given connection.
@@ -177,18 +207,18 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n
Example: ```yaml - hubosmo: + icon-archway: src: - chain-id: cosmoshub-4 - client-id: 07-tendermint-259 - connection-id: connection-257 + chain-id: ibc-icon + client-id: 07-tendermint-0 + connection-id: connection-0 dst: - chain-id: osmosis-1 - client-id: 07-tendermint-1 - connection-id: connection-1 + chain-id: localnet + client-id: iconclient-0 + connection-id: connection-0 src-channel-filter: - rule: allowlist - channel-list: [channel-141] + rule: allowlist + channel-list: [] ``` >Because two channels between chains are tightly coupled, there is no need to specify the dst channels. @@ -211,10 +241,16 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n [[TROUBLESHOOTING](docs/troubleshooting.md)] --- +## Build Docker Image of Relayer +To build the docker image of the relayer, use the following command: +```sh +docker build -t . +``` + ## Security Notice If you would like to report a security critical bug related to the relayer repo, -please reach out @jackzampolin or @Ethereal0ne on telegram. +please reach out @applexxx or @astra#2705 on discord. ## Code of Conduct diff --git a/examples/demo/configs/chains/ibc-icon.json b/examples/demo/configs/chains/ibc-icon.json index b607006e1..466f153ee 100644 --- a/examples/demo/configs/chains/ibc-icon.json +++ b/examples/demo/configs/chains/ibc-icon.json @@ -5,7 +5,7 @@ "chain-id": "ibc-icon", "rpc-addr": "http://localhost:9082/api/v3/", "timeout": "30s", - "keystore": "godWallet", + "keystore": "relayerWallet", "password": "gochain", "icon-network-id": 3, "btp-network-id": 2, From d7e3493970661aba10bda4953a92eb524e61cb44 Mon Sep 17 00:00:00 2001 From: viveksharmapoudel Date: Tue, 15 Aug 2023 08:26:02 +0545 Subject: [PATCH 14/15] fix: sdk prefix change handle for neutron and archway (#119) * fix: sdk prefix change handle for neutron and archway * fix: wasm prefix change optimize * fix: add config during successlogtx as well due to feepayer address --- relayer/chains/icon/icon_chain_processor.go | 3 +++ relayer/chains/wasm/provider.go | 2 -- relayer/chains/wasm/query.go | 3 ++- relayer/chains/wasm/tx.go | 5 +++++ relayer/chains/wasm/wasm_prefix.go | 15 ++++++++------- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/relayer/chains/icon/icon_chain_processor.go b/relayer/chains/icon/icon_chain_processor.go index 44fb638a2..514e201f9 100644 --- a/relayer/chains/icon/icon_chain_processor.go +++ b/relayer/chains/icon/icon_chain_processor.go @@ -353,6 +353,9 @@ loop: break } + icp.log.Debug("Verified block ", + zap.Int64("height", int64(processedheight))) + icp.latestBlock = provider.LatestBlock{ Height: uint64(processedheight), } diff --git a/relayer/chains/wasm/provider.go b/relayer/chains/wasm/provider.go index fcb77454d..c3ad74207 100644 --- a/relayer/chains/wasm/provider.go +++ b/relayer/chains/wasm/provider.go @@ -346,8 +346,6 @@ func (ap *WasmProvider) Init(ctx context.Context) error { } ap.LightProvider = lightprovider - ap.SetSDKContext() - clientCtx := client.Context{}. WithClient(rpcClient). WithFromName(ap.PCfg.Key). diff --git a/relayer/chains/wasm/query.go b/relayer/chains/wasm/query.go index 053fcfadd..44176237f 100644 --- a/relayer/chains/wasm/query.go +++ b/relayer/chains/wasm/query.go @@ -334,7 +334,8 @@ func (ap *WasmProvider) QueryClientConsensusState(ctx context.Context, chainHeig } func (ap *WasmProvider) QueryIBCHandlerContract(ctx context.Context, param wasmtypes.RawContractMessage) (*wasmtypes.QuerySmartContractStateResponse, error) { - + done := ap.SetSDKContext() + defer done() return ap.QueryClient.SmartContractState(ctx, &wasmtypes.QuerySmartContractStateRequest{ Address: ap.PCfg.IbcHandlerAddress, QueryData: param, diff --git a/relayer/chains/wasm/tx.go b/relayer/chains/wasm/tx.go index 711fe02ee..00692f19b 100644 --- a/relayer/chains/wasm/tx.go +++ b/relayer/chains/wasm/tx.go @@ -807,6 +807,8 @@ func (ap *WasmProvider) LogFailedTx(res *provider.RelayerTxResponse, err error, func (ap *WasmProvider) LogSuccessTx(res *sdk.TxResponse, msgs []provider.RelayerMessage) { // Include the chain_id fields := []zapcore.Field{zap.String("chain_id", ap.ChainId())} + done := ap.SetSDKContext() + defer done() // Include the gas used fields = append(fields, zap.Int64("gas_used", res.GasUsed)) @@ -879,6 +881,9 @@ func (ap *WasmProvider) sdkError(codespace string, code uint32) error { } func (ap *WasmProvider) buildMessages(clientCtx client.Context, txf tx.Factory, msgs ...sdk.Msg) ([]byte, uint64, error) { + done := ap.SetSDKContext() + defer done() + for _, msg := range msgs { if err := msg.ValidateBasic(); err != nil { return nil, 0, err diff --git a/relayer/chains/wasm/wasm_prefix.go b/relayer/chains/wasm/wasm_prefix.go index ed04aa8e6..1a77d255f 100644 --- a/relayer/chains/wasm/wasm_prefix.go +++ b/relayer/chains/wasm/wasm_prefix.go @@ -16,12 +16,13 @@ var sdkConfigMutex sync.Mutex // SetSDKContext sets the SDK config to the proper bech32 prefixes for wasm. // Don't use this unless you know what you're doing. // TODO: :dagger: :knife: :chainsaw: remove this function -func (ap *WasmProvider) SetSDKContext() { +func (ap *WasmProvider) SetSDKContext() func() { + sdkConfigMutex.Lock() - cfg := sdk.GetConfig() - cfg.SetBech32PrefixForAccount(ap.PCfg.AccountPrefix, app.Bech32PrefixAccPub) - cfg.SetBech32PrefixForValidator(ap.PCfg.AccountPrefix, app.Bech32PrefixValPub) - cfg.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) - cfg.SetAddressVerifier(wasmtypes.VerifyAddressLen()) - sdkConfigMutex.Unlock() + cfg_update := sdk.GetConfig() + cfg_update.SetBech32PrefixForAccount(ap.PCfg.AccountPrefix, app.Bech32PrefixAccPub) + cfg_update.SetBech32PrefixForValidator(ap.PCfg.AccountPrefix, app.Bech32PrefixValPub) + cfg_update.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) + cfg_update.SetAddressVerifier(wasmtypes.VerifyAddressLen()) + return sdkConfigMutex.Unlock } From 1b5e17fd4ab28868146c5e938df592b5950e2338 Mon Sep 17 00:00:00 2001 From: DeepakBomjan <44976635+DeepakBomjan@users.noreply.github.com> Date: Tue, 15 Aug 2023 08:29:19 +0545 Subject: [PATCH 15/15] docs: add local relay deployment guide (#139) * docs: add README for relay setup on localnet and testnet * docs: running relayer locally * docs: update reference * chore: remove demo/dev environmtn --------- Co-authored-by: izyak --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10c46b32f..1a3daffce 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Additional information on how IBC works can be found [here](https://ibc.cosmos.n - [Relayer Terminology](./docs/terminology.md) - [New Chain Implementation](./docs/chain_implementation.md) - [Recommended Pruning Settings](./docs/node_pruning.md) -- [Demo/Dev-Environmnet](./examples/README.md) +- [Running Relayer Locally](https://github.com/izyak/icon-ibc/blob/master/README.md) --- ## Basic Usage - Relaying Packets Across Chains