From 195c492c3b2ed6144e0aa38117783ca3b095d168 Mon Sep 17 00:00:00 2001 From: Masanori Yoshida Date: Mon, 20 Nov 2023 16:39:09 +0900 Subject: [PATCH 1/3] replace `LightClient::CreateMsgCreateClient` with `CreateInitialLightClientState`, and remove `GetFinalizedHeader` with restoring `GetLatestFinalizedHeader` Signed-off-by: Masanori Yoshida --- chains/tendermint/client-tx.go | 17 ++--------- chains/tendermint/cmd/light.go | 2 +- chains/tendermint/prover.go | 32 ++++++++++++-------- chains/tendermint/query.go | 4 +-- cmd/tx.go | 19 +++++++++--- core/client.go | 55 +++++++++++----------------------- core/headers.go | 4 +-- core/provers.go | 14 ++++----- core/send.go | 2 +- provers/mock/prover.go | 39 ++++++++++++------------ 10 files changed, 86 insertions(+), 102 deletions(-) diff --git a/chains/tendermint/client-tx.go b/chains/tendermint/client-tx.go index 008b59ad..d81c019c 100644 --- a/chains/tendermint/client-tx.go +++ b/chains/tendermint/client-tx.go @@ -4,7 +4,6 @@ import ( "time" "github.com/cometbft/cometbft/light" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" tmclient "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -13,7 +12,7 @@ import ( func createClient( dstHeader *tmclient.Header, trustingPeriod, unbondingPeriod time.Duration, - signer sdk.AccAddress) *clienttypes.MsgCreateClient { +) *tmclient.ClientState { if err := dstHeader.ValidateBasic(); err != nil { panic(err) } @@ -30,17 +29,5 @@ func createClient( []string{"upgrade", "upgradedIBCState"}, ) - msg, err := clienttypes.NewMsgCreateClient( - clientState, - dstHeader.ConsensusState(), - signer.String(), - ) - - if err != nil { - panic(err) - } - if err = msg.ValidateBasic(); err != nil { - panic(err) - } - return msg + return clientState } diff --git a/chains/tendermint/cmd/light.go b/chains/tendermint/cmd/light.go index cea04263..46648e18 100644 --- a/chains/tendermint/cmd/light.go +++ b/chains/tendermint/cmd/light.go @@ -108,7 +108,7 @@ func updateLightCmd(ctx *config.Context) *cobra.Command { return err } - fmt.Printf("Updated light client for %s from height %d -> height %d\n", args[0], bh.Header.Height, ah.(*tmclient.Header).Header.Height) + fmt.Printf("Updated light client for %s from height %d -> height %d\n", args[0], bh.Header.Height, ah.Header.Height) return nil }, } diff --git a/chains/tendermint/prover.go b/chains/tendermint/prover.go index a47f2931..fec8e215 100644 --- a/chains/tendermint/prover.go +++ b/chains/tendermint/prover.go @@ -10,7 +10,6 @@ import ( "github.com/cometbft/cometbft/types" tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcclient "github.com/cosmos/ibc-go/v7/modules/core/client" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -57,18 +56,27 @@ func (pr *Prover) ProveState(ctx core.QueryContext, path string, value []byte) ( /* LightClient implementation */ -// CreateMsgCreateClient creates a MsgCreateClient for the counterparty chain -func (pr *Prover) CreateMsgCreateClient(selfHeader core.Header, signer sdk.AccAddress) (*clienttypes.MsgCreateClient, error) { +// CreateInitialLightClientState creates a pair of ClientState and ConsensusState submitted to the counterparty chain as MsgCreateClient +func (pr *Prover) CreateInitialLightClientState(height ibcexported.Height) (ibcexported.ClientState, ibcexported.ConsensusState, error) { + selfHeader, err := pr.UpdateLightClient(int64(height.GetRevisionHeight())) + if err != nil { + return nil, nil, fmt.Errorf("failed to update the local light client and get the header@%d: %v", height, err) + } + ubdPeriod, err := pr.chain.QueryUnbondingPeriod() if err != nil { - return nil, err + return nil, nil, fmt.Errorf("failed to query for the unbonding period: %v", err) } - return createClient( - selfHeader.(*tmclient.Header), + + cs := createClient( + selfHeader, pr.getTrustingPeriod(), ubdPeriod, - signer, - ), nil + ) + + cons := selfHeader.ConsensusState() + + return cs, cons, nil } // SetupHeadersForUpdate returns the finalized header and any intermediate headers needed to apply it to the client on the counterpaty chain @@ -108,9 +116,9 @@ func (pr *Prover) SetupHeadersForUpdate(counterparty core.FinalityAwareChain, la return []core.Header{&h}, nil } -// GetFinalizedHeader returns the finalized header at `height` -func (pr *Prover) GetFinalizedHeader(height uint64) (core.Header, error) { - return pr.UpdateLightClient(int64(height)) +// GetLatestFinalizedHeader returns the latest finalized header +func (pr *Prover) GetLatestFinalizedHeader() (core.Header, error) { + return pr.UpdateLightClient(0) } func (pr *Prover) CheckRefreshRequired(counterparty core.ChainInfoICS02Querier) (bool, error) { @@ -180,7 +188,7 @@ func (pr *Prover) GetLatestLightHeight() (int64, error) { return client.LastTrustedHeight() } -func (pr *Prover) UpdateLightClient(height int64) (core.Header, error) { +func (pr *Prover) UpdateLightClient(height int64) (*tmclient.Header, error) { // create database connection db, df, err := pr.NewLightDB() if err != nil { diff --git a/chains/tendermint/query.go b/chains/tendermint/query.go index c7a95ae6..110384db 100644 --- a/chains/tendermint/query.go +++ b/chains/tendermint/query.go @@ -209,7 +209,7 @@ func (c *Chain) QueryUnfinalizedRelayPackets(ctx core.QueryContext, counterparty } var counterpartyCtx core.QueryContext - if counterpartyH, err := counterparty.GetFinalizedHeader(0); err != nil { + if counterpartyH, err := counterparty.GetLatestFinalizedHeader(); err != nil { return nil, err } else { counterpartyCtx = core.NewQueryContext(context.TODO(), counterpartyH.GetHeight()) @@ -262,7 +262,7 @@ func (c *Chain) QueryUnfinalizedRelayAcknowledgements(ctx core.QueryContext, cou } var counterpartyCtx core.QueryContext - if counterpartyH, err := counterparty.GetFinalizedHeader(0); err != nil { + if counterpartyH, err := counterparty.GetLatestFinalizedHeader(); err != nil { return nil, err } else { counterpartyCtx = core.NewQueryContext(context.TODO(), counterpartyH.GetHeight()) diff --git a/cmd/tx.go b/cmd/tx.go index e318899a..1e04d159 100644 --- a/cmd/tx.go +++ b/cmd/tx.go @@ -2,9 +2,12 @@ package cmd import ( "context" + "fmt" "strings" "github.com/cosmos/cosmos-sdk/client/flags" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/hyperledger-labs/yui-relayer/config" "github.com/hyperledger-labs/yui-relayer/core" "github.com/spf13/cobra" @@ -66,14 +69,22 @@ func createClientsCmd(ctx *config.Context) *cobra.Command { return err } - srcHeight, err := cmd.Flags().GetUint64(flagSrcHeight) - if err != nil { + var srcHeight exported.Height + if height, err := cmd.Flags().GetUint64(flagSrcHeight); err != nil { return err + } else if latestHeight, err := c[src].LatestHeight(); err != nil { + return fmt.Errorf("failed to get the latest height of src chain: %v", err) + } else { + srcHeight = clienttypes.NewHeight(latestHeight.GetRevisionNumber(), height) } - dstHeight, err := cmd.Flags().GetUint64(flagDstHeight) - if err != nil { + var dstHeight exported.Height + if height, err := cmd.Flags().GetUint64(flagDstHeight); err != nil { return err + } else if latestHeight, err := c[dst].LatestHeight(); err != nil { + return fmt.Errorf("failed to get the latest height of dst chain: %v", err) + } else { + dstHeight = clienttypes.NewHeight(latestHeight.GetRevisionNumber(), height) } return core.CreateClients(c[src], c[dst], srcHeight, dstHeight) diff --git a/core/client.go b/core/client.go index 8c8ede57..9d9d4e81 100644 --- a/core/client.go +++ b/core/client.go @@ -1,29 +1,22 @@ package core import ( + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/hyperledger-labs/yui-relayer/log" - "golang.org/x/sync/errgroup" ) -func CreateClients(src, dst *ProvableChain, srcHeight, dstHeight uint64) error { +func CreateClients(src, dst *ProvableChain, srcHeight, dstHeight exported.Height) error { logger := GetChainPairLogger(src, dst) defer logger.TimeTrack(time.Now(), "CreateClients") var ( clients = &RelayMsgs{Src: []sdk.Msg{}, Dst: []sdk.Msg{}} ) - srcH, dstH, err := getHeadersForCreateClient(src, dst, srcHeight, dstHeight) - if err != nil { - logger.Error( - "failed to get headers for create client", - err, - ) - return err - } - srcAddr, err := src.GetAddress() if err != nil { logger.Error( @@ -42,24 +35,27 @@ func CreateClients(src, dst *ProvableChain, srcHeight, dstHeight uint64) error { } { - msg, err := dst.CreateMsgCreateClient(dstH, srcAddr) + cs, cons, err := dst.CreateInitialLightClientState(dstHeight) if err != nil { - logger.Error( - "failed to create client", - err, - ) + logger.Error("failed to create initial light client state", err) return err } + msg, err := clienttypes.NewMsgCreateClient(cs, cons, srcAddr.String()) + if err != nil { + return fmt.Errorf("failed to create MsgCreateClient: %v", err) + } clients.Src = append(clients.Src, msg) } { - msg, err := src.CreateMsgCreateClient(srcH, dstAddr) + cs, cons, err := src.CreateInitialLightClientState(srcHeight) if err != nil { - logger.Error( - "failed to create client", - err, - ) + logger.Error("failed to create initial light client state", err) + return err + } + msg, err := clienttypes.NewMsgCreateClient(cs, cons, dstAddr.String()) + if err != nil { + logger.Error("failed to create MsgCreateClient: %v", err) return err } clients.Dst = append(clients.Dst, msg) @@ -117,23 +113,6 @@ func UpdateClients(src, dst *ProvableChain) error { return nil } -// getHeadersForCreateClient calls UpdateLightWithHeader on the passed chains concurrently -func getHeadersForCreateClient(src, dst LightClient, srcHeight, dstHeight uint64) (srch, dsth Header, err error) { - var eg = new(errgroup.Group) - eg.Go(func() error { - srch, err = src.GetFinalizedHeader(srcHeight) - return err - }) - eg.Go(func() error { - dsth, err = dst.GetFinalizedHeader(dstHeight) - return err - }) - if err := eg.Wait(); err != nil { - return nil, nil, err - } - return srch, dsth, nil -} - func GetClientPairLogger(src, dst Chain) *log.RelayLogger { return log.GetLogger(). WithClientPair( diff --git a/core/headers.go b/core/headers.go index 1638f4b7..11de7198 100644 --- a/core/headers.go +++ b/core/headers.go @@ -77,12 +77,12 @@ func (sh *syncHeaders) Updates(src, dst ChainInfoLightClient) error { return err } - srcHeader, err := src.GetFinalizedHeader(0) + srcHeader, err := src.GetLatestFinalizedHeader() if err != nil { logger.Error("error getting latest finalized header of src", err) return err } - dstHeader, err := dst.GetFinalizedHeader(0) + dstHeader, err := dst.GetLatestFinalizedHeader() if err != nil { logger.Error("error getting latest finalized header of dst", err) return err diff --git a/core/provers.go b/core/provers.go index 2ed556f1..c8e50ac8 100644 --- a/core/provers.go +++ b/core/provers.go @@ -5,8 +5,8 @@ import ( "time" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" ) // Prover represents a prover that supports generating a commitment proof @@ -33,8 +33,9 @@ type StateProver interface { type LightClient interface { FinalityAware - // CreateMsgCreateClient creates a MsgCreateClient for the counterparty chain - CreateMsgCreateClient(selfHeader Header, signer sdk.AccAddress) (*clienttypes.MsgCreateClient, error) + // CreateInitialLightClientState returns a pair of ClientState and ConsensusState based on the state of the self chain at `height`. + // These states will be submitted to the counterparty chain as MsgCreateClient. + CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) // SetupHeadersForUpdate returns the finalized header and any intermediate headers needed to apply it to the client on the counterpaty chain // The order of the returned header slice should be as: [..., ] @@ -48,10 +49,9 @@ type LightClient interface { // FinalityAware provides the capability to determine the finality of the chain type FinalityAware interface { - // GetFinalizedHeader returns the finalized header on this chain corresponding to `height`. - // If `height` is zero, this function returns the latest finalized header. - // If the header at `height` isn't finalized yet, this function returns an error. - GetFinalizedHeader(height uint64) (Header, error) + // GetLatestFinalizedHeader returns the latest finalized header on this chain + // The returned header is expected to be the latest one of headers that can be verified by the light client + GetLatestFinalizedHeader() (latestFinalizedHeader Header, err error) } // FinalityAwareChain is FinalityAware + Chain diff --git a/core/send.go b/core/send.go index 83eac72f..33c7cbbd 100644 --- a/core/send.go +++ b/core/send.go @@ -28,7 +28,7 @@ func GetFinalizedMsgResult(chain ProvableChain, msgID MsgID) (MsgResult, error) if err := retry.Do(func() error { // query LFH for each retry because it can proceed. - lfHeader, err := chain.GetFinalizedHeader(0) + lfHeader, err := chain.GetLatestFinalizedHeader() if err != nil { return fmt.Errorf("failed to get latest finalized header: %v", err) } diff --git a/provers/mock/prover.go b/provers/mock/prover.go index c248c2ca..a756d8e1 100644 --- a/provers/mock/prover.go +++ b/provers/mock/prover.go @@ -7,7 +7,6 @@ import ( "time" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -39,20 +38,25 @@ func (pr *Prover) SetupForRelay(ctx context.Context) error { return nil } -// CreateMsgCreateClient creates a MsgCreateClient for the counterparty chain -func (pr *Prover) CreateMsgCreateClient(selfHeader core.Header, signer sdk.AccAddress) (*clienttypes.MsgCreateClient, error) { - h := selfHeader.(*mocktypes.Header) +// CreateInitialLightClientState creates a pair of ClientState and ConsensusState for building MsgCreateClient submitted to the counterparty chain +func (pr *Prover) CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) { clientState := &mocktypes.ClientState{ - LatestHeight: h.Height, + LatestHeight: clienttypes.NewHeight( + height.GetRevisionNumber(), + height.GetRevisionHeight(), + ), } - consensusState := &mocktypes.ConsensusState{ - Timestamp: h.Timestamp, + + var consensusState exported.ConsensusState + if timestamp, err := pr.chain.Timestamp(height); err != nil { + return nil, nil, fmt.Errorf("get timestamp at height@%v: %v", height, err) + } else { + consensusState = &mocktypes.ConsensusState{ + Timestamp: uint64(timestamp.UnixNano()), + } } - return clienttypes.NewMsgCreateClient( - clientState, - consensusState, - signer.String(), - ) + + return clientState, consensusState, nil } // SetupHeadersForUpdate returns the finalized header and any intermediate headers needed to apply it to the client on the counterpaty chain @@ -89,17 +93,12 @@ func (pr *Prover) getDelayedLatestFinalizedHeight() (exported.Height, error) { return height, nil } -// GetFinalizedHeader returns the finalized header at `height` -func (pr *Prover) GetFinalizedHeader(height uint64) (core.Header, error) { +// GetLatestFinalizedHeader returns the latest finalized header +func (pr *Prover) GetLatestFinalizedHeader() (core.Header, error) { if latestFinalizedHeight, err := pr.getDelayedLatestFinalizedHeight(); err != nil { return nil, err - } else if height == 0 { - return pr.createMockHeader(latestFinalizedHeight) - } else if height > latestFinalizedHeight.GetRevisionHeight() { - return nil, fmt.Errorf("the requested height is greater than the latest finalized height: %v > %v", height, latestFinalizedHeight) } else { - ics02Height := clienttypes.NewHeight(latestFinalizedHeight.GetRevisionNumber(), height) - return pr.createMockHeader(ics02Height) + return pr.createMockHeader(latestFinalizedHeight) } } From 27cfff3b78b76cd8a29d13a077c0dd3b3b55324d Mon Sep 17 00:00:00 2001 From: Masanori Yoshida Date: Mon, 20 Nov 2023 20:01:14 +0900 Subject: [PATCH 2/3] fix `CreateInitialLightClientState` to select the latest finalized height if the given height is nil Signed-off-by: Masanori Yoshida --- chains/tendermint/prover.go | 8 ++++++-- cmd/tx.go | 6 ++++++ core/provers.go | 1 + provers/mock/prover.go | 8 ++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/chains/tendermint/prover.go b/chains/tendermint/prover.go index fec8e215..30904770 100644 --- a/chains/tendermint/prover.go +++ b/chains/tendermint/prover.go @@ -58,9 +58,13 @@ func (pr *Prover) ProveState(ctx core.QueryContext, path string, value []byte) ( // CreateInitialLightClientState creates a pair of ClientState and ConsensusState submitted to the counterparty chain as MsgCreateClient func (pr *Prover) CreateInitialLightClientState(height ibcexported.Height) (ibcexported.ClientState, ibcexported.ConsensusState, error) { - selfHeader, err := pr.UpdateLightClient(int64(height.GetRevisionHeight())) + var tmHeight int64 + if height != nil { + tmHeight = int64(height.GetRevisionHeight()) + } + selfHeader, err := pr.UpdateLightClient(tmHeight) if err != nil { - return nil, nil, fmt.Errorf("failed to update the local light client and get the header@%d: %v", height, err) + return nil, nil, fmt.Errorf("failed to update the local light client and get the header@%d: %v", tmHeight, err) } ubdPeriod, err := pr.chain.QueryUnbondingPeriod() diff --git a/cmd/tx.go b/cmd/tx.go index 1e04d159..60a6c78d 100644 --- a/cmd/tx.go +++ b/cmd/tx.go @@ -69,18 +69,24 @@ func createClientsCmd(ctx *config.Context) *cobra.Command { return err } + // if the option "src-height" is not set or is set zero, the latest finalized height is used. var srcHeight exported.Height if height, err := cmd.Flags().GetUint64(flagSrcHeight); err != nil { return err + } else if height == 0 { + srcHeight = nil } else if latestHeight, err := c[src].LatestHeight(); err != nil { return fmt.Errorf("failed to get the latest height of src chain: %v", err) } else { srcHeight = clienttypes.NewHeight(latestHeight.GetRevisionNumber(), height) } + // if the option "dst-height" is not set or is set zero, the latest finalized height is used. var dstHeight exported.Height if height, err := cmd.Flags().GetUint64(flagDstHeight); err != nil { return err + } else if height == 0 { + dstHeight = nil } else if latestHeight, err := c[dst].LatestHeight(); err != nil { return fmt.Errorf("failed to get the latest height of dst chain: %v", err) } else { diff --git a/core/provers.go b/core/provers.go index c8e50ac8..7f584883 100644 --- a/core/provers.go +++ b/core/provers.go @@ -35,6 +35,7 @@ type LightClient interface { // CreateInitialLightClientState returns a pair of ClientState and ConsensusState based on the state of the self chain at `height`. // These states will be submitted to the counterparty chain as MsgCreateClient. + // If `height` is nil, the latest finalized height is selected automatically. CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) // SetupHeadersForUpdate returns the finalized header and any intermediate headers needed to apply it to the client on the counterpaty chain diff --git a/provers/mock/prover.go b/provers/mock/prover.go index a756d8e1..4d7a5363 100644 --- a/provers/mock/prover.go +++ b/provers/mock/prover.go @@ -40,6 +40,14 @@ func (pr *Prover) SetupForRelay(ctx context.Context) error { // CreateInitialLightClientState creates a pair of ClientState and ConsensusState for building MsgCreateClient submitted to the counterparty chain func (pr *Prover) CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) { + if height == nil { + if head, err := pr.GetLatestFinalizedHeader(); err != nil { + return nil, nil, fmt.Errorf("failed to get the latest finalized header: %v", err) + } else { + height = head.GetHeight() + } + } + clientState := &mocktypes.ClientState{ LatestHeight: clienttypes.NewHeight( height.GetRevisionNumber(), From 523478c5e7aa667f7f1c24ffbe4a89e0ae807ca1 Mon Sep 17 00:00:00 2001 From: Masanori Yoshida Date: Tue, 21 Nov 2023 07:17:24 +0900 Subject: [PATCH 3/3] fix the mock prover's `CreateInitialLightClientState` to check that the given height <= the latest finalized height Signed-off-by: Masanori Yoshida --- provers/mock/prover.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/provers/mock/prover.go b/provers/mock/prover.go index 4d7a5363..ee53bf95 100644 --- a/provers/mock/prover.go +++ b/provers/mock/prover.go @@ -40,12 +40,12 @@ func (pr *Prover) SetupForRelay(ctx context.Context) error { // CreateInitialLightClientState creates a pair of ClientState and ConsensusState for building MsgCreateClient submitted to the counterparty chain func (pr *Prover) CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) { - if height == nil { - if head, err := pr.GetLatestFinalizedHeader(); err != nil { - return nil, nil, fmt.Errorf("failed to get the latest finalized header: %v", err) - } else { - height = head.GetHeight() - } + if head, err := pr.GetLatestFinalizedHeader(); err != nil { + return nil, nil, fmt.Errorf("failed to get the latest finalized header: %v", err) + } else if height == nil { + height = head.GetHeight() + } else if height.GT(head.GetHeight()) { + return nil, nil, fmt.Errorf("the given height is greater than the latest finalized height: %v > %v", height, head) } clientState := &mocktypes.ClientState{