diff --git a/cmd/root.go b/cmd/root.go index fc940377f..6c3e7907b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -77,7 +77,7 @@ func NewRootCmd(log *zap.Logger) *cobra.Command { rootCmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error { // Inside persistent pre-run because this takes effect after flags are parsed. if log == nil { - log, err := newRootLogger(a.viper.GetString("log-format"), a.viper.GetBool("debug")) + log, err := newRootLogger(a.viper.GetString(flagHome), a.viper.GetString("log-format"), a.viper.GetBool("debug")) if err != nil { return err } @@ -180,7 +180,7 @@ type lumberjackSink struct { func (lumberjackSink) Sync() error { return nil } -func newRootLogger(format string, debug bool) (*zap.Logger, error) { +func newRootLogger(homepath string, format string, debug bool) (*zap.Logger, error) { config := zap.NewProductionEncoderConfig() config.EncodeTime = func(ts time.Time, encoder zapcore.PrimitiveArrayEncoder) { encoder.AppendString(ts.UTC().Format("2006-01-02T15:04:05.000000Z07:00")) @@ -188,8 +188,8 @@ func newRootLogger(format string, debug bool) (*zap.Logger, error) { config.LevelKey = "lvl" ll := lumberjack.Logger{ - Filename: path.Join(defaultHome, "relay.log"), - MaxSize: 10, //MB + Filename: path.Join(homepath, "relay.log"), + MaxSize: 100, //MB MaxBackups: 30, MaxAge: 28, //days Compress: false, diff --git a/cmd/tx.go b/cmd/tx.go index 44ff0af77..f0de65cda 100644 --- a/cmd/tx.go +++ b/cmd/tx.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "time" @@ -44,6 +45,7 @@ Most of these commands take a [path] argument. Make sure: lineBreakCommand(), createClientsCmd(a), createClientCmd(a), + updateClientCmd(a), updateClientsCmd(a), upgradeClientsCmd(a), createConnectionCmd(a), @@ -265,6 +267,53 @@ func createClientCmd(a *appState) *cobra.Command { return cmd } +func updateClientCmd(a *appState) *cobra.Command { + cmd := &cobra.Command{ + Use: "update-client src_chain_name dst_chain_name path_name [heights]", + Short: "update block [heights] of src_chain on dst_chain", + Args: withUsage(cobra.ExactArgs(4)), + Example: strings.TrimSpace(fmt.Sprintf(`$ %s transact update-client icon archway icon-archway 75830974,75830975`, appName)), + RunE: func(cmd *cobra.Command, args []string) error { + src, ok := a.config.Chains[args[0]] + if !ok { + return errChainNotFound(args[0]) + } + dst, ok := a.config.Chains[args[1]] + if !ok { + return errChainNotFound(args[1]) + } + _, _, _, err := a.config.ChainsFromPath(args[2]) + if err != nil { + return err + } + + // ensure that keys exist + if exists := src.ChainProvider.KeyExists(src.ChainProvider.Key()); !exists { + return fmt.Errorf("key %s not found on src chain %s", src.ChainProvider.Key(), src.ChainID()) + } + if exists := dst.ChainProvider.KeyExists(dst.ChainProvider.Key()); !exists { + return fmt.Errorf("key %s not found on dst chain %s", dst.ChainProvider.Key(), dst.ChainID()) + } + + var heights []int64 + + numStr := strings.Split(args[3], ",") + + for _, s := range numStr { + num, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return fmt.Errorf("error converting string to int64: %w", err) + + } + heights = append(heights, num) + } + + return relayer.UpdateClient(cmd.Context(), src, dst, a.config.memo(cmd), heights) + }, + } + return memoFlag(a.viper, cmd) +} + func updateClientsCmd(a *appState) *cobra.Command { cmd := &cobra.Command{ Use: "update-clients path_name", diff --git a/relayer/chains/wasm/tx.go b/relayer/chains/wasm/tx.go index 455407bec..8ca73c962 100644 --- a/relayer/chains/wasm/tx.go +++ b/relayer/chains/wasm/tx.go @@ -736,12 +736,16 @@ func (ap *WasmProvider) SendMessagesToMempool( wasmMsg, ok := msg.(*WasmContractMessage) if !ok { - return fmt.Errorf("Wasm Message is not valid %s", wasmMsg.Type()) + return fmt.Errorf("wasm Message is not valid %s", wasmMsg.Type()) } txBytes, sequence, err := ap.buildMessages(cliCtx, factory, wasmMsg.Msg) if err != nil { - return err + if strings.Contains(err.Error(), sdkerrors.ErrWrongSequence.Error()) { + ap.handleAccountSequenceMismatchError(err) + } else { + return err + } } if msg.Type() == MethodUpdateClient { @@ -997,7 +1001,7 @@ func (ap *WasmProvider) BroadcastTx( if isFailed { err = ap.sdkError(res.Codespace, res.Code) if err == nil { - err = fmt.Errorf("transaction failed to execute") + err = fmt.Errorf("transaction failed to execute: %s", res.RawLog) } } ap.LogFailedTx(rlyResp, err, msgs) diff --git a/relayer/client.go b/relayer/client.go index 1972885bf..dcd85079f 100644 --- a/relayer/client.go +++ b/relayer/client.go @@ -357,6 +357,101 @@ func MsgUpdateClient( return dst.ChainProvider.MsgUpdateClient(dst.ClientID(), updateHeader) } +func msgUpdateClientOneWay(ctx context.Context, src, dst *Chain, height int64) (provider.RelayerMessage, error) { + + var updateHeader ibcexported.ClientMessage + if err := retry.Do(func() error { + var err error + + dstHeight, err := dst.ChainProvider.QueryLatestHeight(ctx) + if err != nil { + return err + } + + dstClientState, err := dst.ChainProvider.QueryClientState(ctx, dstHeight, dst.ClientID()) + if err != nil { + return err + } + + trustedHdr, err := src.ChainProvider.QueryIBCHeader(ctx, int64(dstClientState.GetLatestHeight().GetRevisionHeight())) + if err != nil { + return err + } + + latestHdr, err := src.ChainProvider.QueryIBCHeader(ctx, height) + if err != nil { + return err + } + + trustedHeight := clienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: trustedHdr.Height(), + } + + updateHeader, err = src.ChainProvider.MsgUpdateClientHeader(latestHdr, trustedHeight, trustedHdr) + return err + }, retry.Context(ctx), RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.log.Info( + "Failed to build update message", + zap.String("client_id", dst.ClientID()), + zap.Uint("attempt", n+1), + zap.Uint("max_attempts", RtyAttNum), + zap.Error(err), + ) + })); err != nil { + return nil, err + } + + return dst.ChainProvider.MsgUpdateClient(dst.ClientID(), updateHeader) +} + +func UpdateClient(ctx context.Context, src, dst *Chain, memo string, heights []int64) error { + eg, egCtx := errgroup.WithContext(ctx) + for _, height := range heights { + + var dstMsgUpdateClient provider.RelayerMessage + eg.Go(func() error { + var err error + dstMsgUpdateClient, err = msgUpdateClientOneWay(egCtx, src, dst, height) + return err + }) + + if err := eg.Wait(); err != nil { + return err + } + + clients := &RelayMsgs{ + Src: []provider.RelayerMessage{}, + Dst: []provider.RelayerMessage{dstMsgUpdateClient}, + } + + result := clients.Send(ctx, src.log, AsRelayMsgSender(src), AsRelayMsgSender(dst), memo) + + if err := result.Error(); err != nil { + if result.PartiallySent() { + src.log.Info( + "Partial success when updating clients", + zap.String("src_chain_id", src.ChainID()), + zap.String("dst_chain_id", dst.ChainID()), + zap.Object("send_result", result), + ) + } + return err + } + + src.log.Info( + "Client updated", + zap.String("src_chain_id", src.ChainID()), + zap.String("src_client", src.PathEnd.ClientID), + + zap.String("dst_chain_id", dst.ChainID()), + zap.String("dst_client", dst.PathEnd.ClientID), + ) + } + + return nil +} + // UpdateClients updates clients for src on dst and dst on src given the configured paths. func UpdateClients( ctx context.Context,