From 6186b8042d07aa39cc8e0781b7466533dfd1cc50 Mon Sep 17 00:00:00 2001 From: Jun Kimura Date: Tue, 25 Jan 2022 17:28:56 +0900 Subject: [PATCH] proxyClient tracks the upstream's block and uses its timestamp as a validation of packet timeout Signed-off-by: Jun Kimura --- .../xx-proxy/types/client_state.go | 36 +- .../light-clients/xx-proxy/types/header.go | 37 + .../light-clients/xx-proxy/types/proxy.pb.go | 858 +++++++++++++++++- modules/light-clients/xx-proxy/types/store.go | 19 + .../xx-proxy/types/tendermint/proxy_client.go | 97 ++ modules/light-clients/xx-proxy/types/types.go | 166 ++++ .../light-clients/xx-proxy/types/update.go | 27 +- modules/proxy/keeper/client_keeper.go | 26 - modules/proxy/keeper/commitment.go | 25 +- modules/proxy/keeper/core_extension.go | 66 ++ modules/proxy/keeper/proxy.go | 28 +- modules/proxy/keeper/proxy_test.go | 8 +- modules/proxy/types/codec.go | 1 + modules/proxy/types/expected_keepers.go | 1 + proto/ibc/lightclients/proxy/v1/proxy.proto | 29 + testing/chain.go | 12 +- testing/coordinator.go | 16 +- testing/multiv.go | 8 +- testing/proxy.go | 67 +- testing/simapp/app.go | 10 +- 20 files changed, 1431 insertions(+), 106 deletions(-) create mode 100644 modules/light-clients/xx-proxy/types/header.go create mode 100644 modules/light-clients/xx-proxy/types/tendermint/proxy_client.go delete mode 100644 modules/proxy/keeper/client_keeper.go create mode 100644 modules/proxy/keeper/core_extension.go diff --git a/modules/light-clients/xx-proxy/types/client_state.go b/modules/light-clients/xx-proxy/types/client_state.go index 0565fee..427b635 100644 --- a/modules/light-clients/xx-proxy/types/client_state.go +++ b/modules/light-clients/xx-proxy/types/client_state.go @@ -20,20 +20,19 @@ func NewClientState(upstreamClientID string) *ClientState { return &ClientState{UpstreamClientId: upstreamClientID} } -func (cs *ClientState) IsInitialized() bool { - return cs.ProxyClientState != nil -} - func (cs *ClientState) ClientType() string { return ProxyClientType } func (cs *ClientState) GetProxyClientState() exported.ClientState { - state, err := clienttypes.UnpackClientState(cs.ProxyClientState) + if cs.ProxyClientState == nil { + return nil + } + clientState, err := clienttypes.UnpackClientState(cs.ProxyClientState) if err != nil { panic(err) } - return state + return clientState } // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces @@ -44,11 +43,9 @@ func (cs *ClientState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { return nil } +// GetLatestHeight returns the latest height of the upstream instead of the proxy func (cs *ClientState) GetLatestHeight() exported.Height { - if cs.ProxyClientState == nil { - return clienttypes.NewHeight(0, 0) - } - return cs.GetProxyClientState().GetLatestHeight() + return cs.UpstreamHeight } func (cs *ClientState) Status( @@ -60,6 +57,24 @@ func (cs *ClientState) Status( } func (cs *ClientState) Validate() error { + if cs.ProxyClientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "ProxyClientState must be non-empty") + } + if cs.UpstreamClientId == "" { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "UpstreamClientId must be non-empty") + } + if cs.ProxyPrefix == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "ProxyPrefix must be non-empty") + } + if cs.IbcPrefix == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "IbcPrefix must be non-empty") + } + if cs.UpstreamHeight.IsZero() { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "UpstreamHeight must be non-empty") + } + if cs.UpstreamTimestamp == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "UpstreamTimestamp must be non-zero") + } return cs.GetProxyClientState().Validate() } @@ -87,6 +102,7 @@ func (cs *ClientState) Initialize(ctx sdk.Context, cdc codec.BinaryCodec, client if _, err := clienttypes.UnpackConsensusState(cons.ProxyConsensusState); err != nil { return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "failed to unpack client state: %v", err) } + SetUpstreamBlockTime(clientStore, cs.UpstreamHeight, cs.UpstreamTimestamp) return nil } diff --git a/modules/light-clients/xx-proxy/types/header.go b/modules/light-clients/xx-proxy/types/header.go new file mode 100644 index 0000000..500fe3c --- /dev/null +++ b/modules/light-clients/xx-proxy/types/header.go @@ -0,0 +1,37 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + "github.com/cosmos/ibc-go/modules/core/exported" +) + +var _ exported.Header = (*Header)(nil) + +func (h *Header) ClientType() string { + return ProxyClientType +} + +func (h *Header) GetHeight() exported.Height { + return h.GetProxyHeader().GetHeight() +} + +func (h *Header) ValidateBasic() error { + return nil +} + +func (h *Header) GetProxyHeader() exported.Header { + header, err := clienttypes.UnpackHeader(h.ProxyHeader) + if err != nil { + panic(err) + } + return header +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (h *Header) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + if err := unpacker.UnpackAny(h.ProxyHeader, new(exported.Header)); err != nil { + return err + } + return nil +} diff --git a/modules/light-clients/xx-proxy/types/proxy.pb.go b/modules/light-clients/xx-proxy/types/proxy.pb.go index 672d3b1..32ee866 100644 --- a/modules/light-clients/xx-proxy/types/proxy.pb.go +++ b/modules/light-clients/xx-proxy/types/proxy.pb.go @@ -6,6 +6,7 @@ package types import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" + types2 "github.com/cosmos/ibc-go/modules/core/02-client/types" types1 "github.com/cosmos/ibc-go/modules/core/23-commitment/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" @@ -35,6 +36,10 @@ type ClientState struct { ProxyPrefix *types1.MerklePrefix `protobuf:"bytes,3,opt,name=proxy_prefix,json=proxyPrefix,proto3" json:"proxy_prefix,omitempty"` // the ibc commitment prefix of the proxy chain IbcPrefix *types1.MerklePrefix `protobuf:"bytes,4,opt,name=ibc_prefix,json=ibcPrefix,proto3" json:"ibc_prefix,omitempty"` + // latest height on the upstream chain + UpstreamHeight UpstreamHeight `protobuf:"bytes,5,opt,name=upstream_height,json=upstreamHeight,proto3" json:"upstream_height"` + // latest timestamp on the upstream chain + UpstreamTimestamp uint64 `protobuf:"varint,6,opt,name=upstream_timestamp,json=upstreamTimestamp,proto3" json:"upstream_timestamp,omitempty"` } func (m *ClientState) Reset() { *m = ClientState{} } @@ -109,9 +114,157 @@ func (m *ConsensusState) XXX_DiscardUnknown() { var xxx_messageInfo_ConsensusState proto.InternalMessageInfo +type Header struct { + ProxyHeader *types.Any `protobuf:"bytes,1,opt,name=proxy_header,json=proxyHeader,proto3" json:"proxy_header,omitempty"` + UpstreamBlockProof *UpstreamBlockProof `protobuf:"bytes,2,opt,name=upstream_block_proof,json=upstreamBlockProof,proto3" json:"upstream_block_proof,omitempty"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_7b548f5864814422, []int{2} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +type UpstreamBlockProof struct { + Proof []byte `protobuf:"bytes,1,opt,name=proof,proto3" json:"proof,omitempty"` + ProofHeight types2.Height `protobuf:"bytes,2,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` + UpstreamHeight types2.Height `protobuf:"bytes,3,opt,name=upstream_height,json=upstreamHeight,proto3" json:"upstream_height"` + UpstreamTimestamp uint64 `protobuf:"varint,4,opt,name=upstream_timestamp,json=upstreamTimestamp,proto3" json:"upstream_timestamp,omitempty"` +} + +func (m *UpstreamBlockProof) Reset() { *m = UpstreamBlockProof{} } +func (m *UpstreamBlockProof) String() string { return proto.CompactTextString(m) } +func (*UpstreamBlockProof) ProtoMessage() {} +func (*UpstreamBlockProof) Descriptor() ([]byte, []int) { + return fileDescriptor_7b548f5864814422, []int{3} +} +func (m *UpstreamBlockProof) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpstreamBlockProof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpstreamBlockProof.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpstreamBlockProof) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpstreamBlockProof.Merge(m, src) +} +func (m *UpstreamBlockProof) XXX_Size() int { + return m.Size() +} +func (m *UpstreamBlockProof) XXX_DiscardUnknown() { + xxx_messageInfo_UpstreamBlockProof.DiscardUnknown(m) +} + +var xxx_messageInfo_UpstreamBlockProof proto.InternalMessageInfo + +func (m *UpstreamBlockProof) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *UpstreamBlockProof) GetProofHeight() types2.Height { + if m != nil { + return m.ProofHeight + } + return types2.Height{} +} + +func (m *UpstreamBlockProof) GetUpstreamHeight() types2.Height { + if m != nil { + return m.UpstreamHeight + } + return types2.Height{} +} + +func (m *UpstreamBlockProof) GetUpstreamTimestamp() uint64 { + if m != nil { + return m.UpstreamTimestamp + } + return 0 +} + +type UpstreamHeight struct { + // the revision that the client is currently on + RevisionNumber uint64 `protobuf:"varint,1,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty" yaml:"revision_number"` + // the height within the given revision + RevisionHeight uint64 `protobuf:"varint,2,opt,name=revision_height,json=revisionHeight,proto3" json:"revision_height,omitempty" yaml:"revision_height"` +} + +func (m *UpstreamHeight) Reset() { *m = UpstreamHeight{} } +func (*UpstreamHeight) ProtoMessage() {} +func (*UpstreamHeight) Descriptor() ([]byte, []int) { + return fileDescriptor_7b548f5864814422, []int{4} +} +func (m *UpstreamHeight) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpstreamHeight) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpstreamHeight.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpstreamHeight) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpstreamHeight.Merge(m, src) +} +func (m *UpstreamHeight) XXX_Size() int { + return m.Size() +} +func (m *UpstreamHeight) XXX_DiscardUnknown() { + xxx_messageInfo_UpstreamHeight.DiscardUnknown(m) +} + +var xxx_messageInfo_UpstreamHeight proto.InternalMessageInfo + func init() { proto.RegisterType((*ClientState)(nil), "ibc.lightclients.proxy.v1.ClientState") proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.proxy.v1.ConsensusState") + proto.RegisterType((*Header)(nil), "ibc.lightclients.proxy.v1.Header") + proto.RegisterType((*UpstreamBlockProof)(nil), "ibc.lightclients.proxy.v1.UpstreamBlockProof") + proto.RegisterType((*UpstreamHeight)(nil), "ibc.lightclients.proxy.v1.UpstreamHeight") } func init() { @@ -119,32 +272,47 @@ func init() { } var fileDescriptor_7b548f5864814422 = []byte{ - // 386 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xbf, 0x6e, 0xe2, 0x40, - 0x10, 0xc6, 0x6d, 0x0e, 0x9d, 0xc4, 0x72, 0x3a, 0x21, 0x1f, 0x27, 0x01, 0x85, 0x0f, 0xa1, 0x8b, - 0x42, 0x11, 0x76, 0x45, 0xd2, 0xa5, 0x0b, 0x48, 0xf9, 0x53, 0x44, 0x8a, 0x48, 0x97, 0x06, 0xbc, - 0xeb, 0xc5, 0xac, 0x62, 0x7b, 0x2d, 0xef, 0x1a, 0x99, 0x37, 0x48, 0x19, 0x29, 0x2f, 0x90, 0xc7, - 0x49, 0x49, 0x99, 0x32, 0x82, 0x17, 0x89, 0xbc, 0x83, 0x15, 0xd2, 0x25, 0xdd, 0xcc, 0xce, 0x37, - 0xdf, 0x6f, 0x76, 0x34, 0xe8, 0x40, 0x50, 0x46, 0x42, 0x11, 0x2c, 0x34, 0x0b, 0x05, 0x8f, 0xb5, - 0x22, 0x49, 0x2a, 0xf3, 0x15, 0x59, 0x0e, 0x21, 0xc0, 0x49, 0x2a, 0xb5, 0x74, 0xda, 0x82, 0x32, - 0xbc, 0x2f, 0xc3, 0x50, 0x5d, 0x0e, 0x3b, 0xcd, 0x40, 0x06, 0xd2, 0xa8, 0x48, 0x11, 0x41, 0x43, - 0xa7, 0x1d, 0x48, 0x19, 0x84, 0x9c, 0x98, 0x8c, 0x66, 0x73, 0xe2, 0xc5, 0x3b, 0xaf, 0xce, 0x61, - 0x81, 0x64, 0x32, 0xe5, 0x84, 0xc9, 0x28, 0x12, 0x3a, 0xe2, 0xb1, 0x2e, 0x78, 0x1f, 0x19, 0x08, - 0x7b, 0x4f, 0x15, 0x54, 0x1f, 0x1b, 0xdc, 0xad, 0xf6, 0x34, 0x77, 0x46, 0xc8, 0x31, 0xd4, 0x29, - 0xcc, 0x30, 0x55, 0xc5, 0x6b, 0xcb, 0xee, 0xda, 0xfd, 0xfa, 0x71, 0x13, 0x03, 0x10, 0x97, 0x40, - 0x7c, 0x16, 0xaf, 0x26, 0x0d, 0xa3, 0xdf, 0xf7, 0x38, 0x42, 0x4e, 0x96, 0x28, 0x9d, 0x72, 0x2f, - 0x2a, 0x6d, 0x84, 0xdf, 0xaa, 0x74, 0xed, 0x7e, 0x6d, 0xd2, 0x28, 0x2b, 0xd0, 0x70, 0xe5, 0x3b, - 0x17, 0xe8, 0x17, 0x10, 0x93, 0x94, 0xcf, 0x45, 0xde, 0xfa, 0x61, 0x58, 0xff, 0x71, 0xb1, 0x8d, - 0xe2, 0x07, 0x78, 0x6f, 0xe6, 0xe5, 0x10, 0x5f, 0xf3, 0xf4, 0x3e, 0xe4, 0x37, 0x46, 0x3b, 0xa9, - 0x9b, 0x4e, 0x48, 0x9c, 0x31, 0x42, 0x82, 0xb2, 0xd2, 0xa6, 0xfa, 0x0d, 0x9b, 0x9a, 0xa0, 0x0c, - 0xc2, 0xd3, 0xea, 0xc3, 0xf3, 0x3f, 0xab, 0x37, 0x43, 0xbf, 0xc7, 0x32, 0x56, 0x3c, 0x56, 0x99, - 0x82, 0x3f, 0x5d, 0xa2, 0xbf, 0xbb, 0xbd, 0x94, 0xef, 0x5f, 0x58, 0xcd, 0x1f, 0x58, 0xcd, 0x27, - 0x27, 0x20, 0x8c, 0x66, 0x2f, 0x1b, 0xd7, 0x5e, 0x6f, 0x5c, 0xfb, 0x6d, 0xe3, 0xda, 0x8f, 0x5b, - 0xd7, 0x5a, 0x6f, 0x5d, 0xeb, 0x75, 0xeb, 0x5a, 0x77, 0xe7, 0x81, 0xd0, 0x8b, 0x8c, 0x16, 0xf3, - 0x12, 0xdf, 0xd3, 0x1e, 0x5b, 0x78, 0x22, 0x0e, 0x3d, 0x4a, 0x04, 0x65, 0x03, 0x38, 0x9c, 0x48, - 0xfa, 0x59, 0xc8, 0x15, 0xdc, 0xd4, 0xa0, 0x3c, 0xaa, 0x3c, 0xdf, 0x95, 0xf5, 0x2a, 0xe1, 0x8a, - 0xfe, 0x34, 0xa3, 0x9c, 0xbc, 0x07, 0x00, 0x00, 0xff, 0xff, 0x24, 0xc9, 0x0d, 0x02, 0x7e, 0x02, - 0x00, 0x00, + // 628 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xb6, 0xa9, 0x5b, 0xd1, 0x4b, 0x95, 0x96, 0x23, 0xa0, 0x34, 0x83, 0x53, 0x45, 0x20, 0x82, + 0x44, 0xce, 0x4a, 0x19, 0x90, 0xb2, 0x91, 0x48, 0x90, 0x0e, 0xa0, 0xca, 0x80, 0x84, 0x58, 0x52, + 0xdb, 0xb9, 0x38, 0xa7, 0xda, 0x3e, 0xcb, 0x3e, 0x47, 0xc9, 0x3f, 0x60, 0x64, 0x64, 0x60, 0xe8, + 0xc0, 0xc0, 0x4f, 0xe9, 0xd8, 0x91, 0xa9, 0x42, 0xc9, 0xc4, 0xca, 0x2f, 0x40, 0x77, 0xe7, 0x4b, + 0x1d, 0x4a, 0x51, 0xd8, 0xfc, 0xde, 0xfb, 0xbe, 0xef, 0xbd, 0x7b, 0xdf, 0xf9, 0xc0, 0x43, 0xe2, + 0x7a, 0x56, 0x40, 0xfc, 0x31, 0xf3, 0x02, 0x82, 0x23, 0x96, 0x5a, 0x71, 0x42, 0xa7, 0x33, 0x6b, + 0xd2, 0x96, 0x1f, 0x28, 0x4e, 0x28, 0xa3, 0x70, 0x9f, 0xb8, 0x1e, 0x2a, 0xc2, 0x90, 0xac, 0x4e, + 0xda, 0xb5, 0x8a, 0x4f, 0x7d, 0x2a, 0x50, 0x16, 0xff, 0x92, 0x84, 0xda, 0xbe, 0x4f, 0xa9, 0x1f, + 0x60, 0x4b, 0x44, 0x6e, 0x36, 0xb2, 0x9c, 0x28, 0xd7, 0xaa, 0xd5, 0x79, 0x4b, 0x8f, 0x26, 0xd8, + 0x92, 0x5a, 0xbc, 0x97, 0xfc, 0xca, 0x01, 0x8f, 0xae, 0x00, 0x34, 0x0c, 0x09, 0x0b, 0x15, 0x68, + 0x19, 0x49, 0x60, 0xe3, 0xcb, 0x06, 0x28, 0xf5, 0x04, 0xf3, 0x0d, 0x73, 0x18, 0x86, 0x5d, 0x00, + 0xc5, 0x58, 0x03, 0x29, 0x37, 0x48, 0x79, 0xb6, 0xaa, 0x1f, 0xe8, 0xcd, 0xd2, 0x61, 0x05, 0xc9, + 0x89, 0x90, 0x9a, 0x08, 0x3d, 0x8f, 0x66, 0xf6, 0x9e, 0xc0, 0x17, 0x35, 0x9e, 0x00, 0x98, 0xc5, + 0x29, 0x4b, 0xb0, 0x13, 0x2a, 0x19, 0x32, 0xac, 0xde, 0x3a, 0xd0, 0x9b, 0xdb, 0xf6, 0x9e, 0xaa, + 0x48, 0xc2, 0xd1, 0x10, 0xbe, 0x04, 0x3b, 0xb2, 0x63, 0x9c, 0xe0, 0x11, 0x99, 0x56, 0x37, 0x44, + 0xaf, 0x07, 0x88, 0xaf, 0x8b, 0x9f, 0x00, 0x15, 0x66, 0x9e, 0xb4, 0xd1, 0x2b, 0x9c, 0x9c, 0x06, + 0xf8, 0x58, 0x60, 0xed, 0x92, 0x60, 0xca, 0x00, 0xf6, 0x00, 0x20, 0xae, 0xa7, 0x64, 0x8c, 0xff, + 0x90, 0xd9, 0x26, 0xae, 0x97, 0x8b, 0xbc, 0x07, 0xbb, 0xcb, 0xd9, 0xc7, 0x98, 0xbb, 0x55, 0xdd, + 0x14, 0x4a, 0x8f, 0xd1, 0x8d, 0xfe, 0xa1, 0x77, 0x39, 0xa3, 0x2f, 0x08, 0x5d, 0xe3, 0xfc, 0xb2, + 0xae, 0xd9, 0xe5, 0x6c, 0x25, 0x0b, 0x5b, 0x85, 0xad, 0x30, 0x12, 0xe2, 0x94, 0x39, 0x61, 0x5c, + 0xdd, 0x3a, 0xd0, 0x9b, 0x86, 0x7d, 0x47, 0x55, 0xde, 0xaa, 0x42, 0xc7, 0xf8, 0x78, 0x56, 0xd7, + 0x1a, 0x27, 0xa0, 0xdc, 0xa3, 0x51, 0x8a, 0xa3, 0x34, 0x4b, 0xe5, 0x72, 0xfb, 0xe0, 0x5e, 0x6e, + 0x90, 0xca, 0xaf, 0xe1, 0xd1, 0x5d, 0xe9, 0xd1, 0x8a, 0x52, 0xde, 0xe1, 0x9b, 0x0e, 0xb6, 0xfa, + 0xd8, 0x19, 0xe2, 0x04, 0x3e, 0x53, 0x4e, 0x8c, 0x45, 0xfc, 0x4f, 0x45, 0xb9, 0xf9, 0x9c, 0x38, + 0x00, 0x95, 0xe5, 0xd1, 0xdc, 0x80, 0x7a, 0xa7, 0x83, 0x38, 0xa1, 0x74, 0x24, 0x2c, 0x2f, 0x1d, + 0xb6, 0xd6, 0xd8, 0x5c, 0x97, 0xb3, 0x8e, 0x39, 0xc9, 0x5e, 0x6e, 0xe9, 0x2a, 0x97, 0x8f, 0xfa, + 0x53, 0x07, 0xf0, 0x3a, 0x01, 0x56, 0xc0, 0xa6, 0x6c, 0xc7, 0xe7, 0xdd, 0xb1, 0x65, 0x00, 0x7b, + 0xe2, 0x30, 0x74, 0xa4, 0x5c, 0x94, 0xb3, 0xd4, 0x0a, 0xf7, 0x41, 0xfe, 0x2f, 0x93, 0x36, 0x5a, + 0xb1, 0xad, 0x24, 0x58, 0xb9, 0x67, 0x47, 0xd7, 0x6f, 0xc3, 0xc6, 0x9a, 0x3a, 0xeb, 0xd9, 0x6f, + 0xdc, 0x60, 0x7f, 0xe3, 0xab, 0x0e, 0xca, 0xab, 0xd7, 0x0a, 0xf6, 0xc0, 0x6e, 0x82, 0x27, 0x24, + 0x25, 0x34, 0x1a, 0x44, 0x59, 0xe8, 0xe6, 0x0e, 0x19, 0xdd, 0xda, 0xaf, 0xcb, 0xfa, 0xfd, 0x99, + 0x13, 0x06, 0x9d, 0xc6, 0x1f, 0x80, 0x86, 0x5d, 0x56, 0x99, 0xd7, 0x22, 0xb1, 0x22, 0x52, 0xd8, + 0xcc, 0xdf, 0x45, 0x24, 0xa0, 0x20, 0x22, 0x27, 0xe9, 0xdc, 0xe6, 0x76, 0x7c, 0x3e, 0xab, 0x6b, + 0xdd, 0x93, 0xf3, 0xb9, 0xa9, 0x5f, 0xcc, 0x4d, 0xfd, 0xc7, 0xdc, 0xd4, 0x3f, 0x2d, 0x4c, 0xed, + 0x62, 0x61, 0x6a, 0xdf, 0x17, 0xa6, 0xf6, 0xe1, 0x85, 0x4f, 0xd8, 0x38, 0x73, 0xf9, 0x6f, 0x67, + 0x0d, 0x1d, 0xe6, 0x78, 0x63, 0x87, 0x44, 0x81, 0xe3, 0x5a, 0xc4, 0xf5, 0x5a, 0xf2, 0x81, 0x0c, + 0xe9, 0x30, 0x0b, 0x70, 0x2a, 0xdf, 0xce, 0x96, 0x7a, 0x3c, 0xa7, 0xd3, 0xbc, 0xcc, 0x66, 0x31, + 0x4e, 0xdd, 0x2d, 0x71, 0xed, 0x9e, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x61, 0xae, 0x0e, 0xf9, + 0x66, 0x05, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -167,6 +335,21 @@ func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.UpstreamTimestamp != 0 { + i = encodeVarintProxy(dAtA, i, uint64(m.UpstreamTimestamp)) + i-- + dAtA[i] = 0x30 + } + { + size, err := m.UpstreamHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProxy(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a if m.IbcPrefix != nil { { size, err := m.IbcPrefix.MarshalToSizedBuffer(dAtA[:i]) @@ -248,6 +431,141 @@ func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Header) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UpstreamBlockProof != nil { + { + size, err := m.UpstreamBlockProof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProxy(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ProxyHeader != nil { + { + size, err := m.ProxyHeader.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProxy(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpstreamBlockProof) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpstreamBlockProof) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpstreamBlockProof) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UpstreamTimestamp != 0 { + i = encodeVarintProxy(dAtA, i, uint64(m.UpstreamTimestamp)) + i-- + dAtA[i] = 0x20 + } + { + size, err := m.UpstreamHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProxy(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProxy(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintProxy(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpstreamHeight) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpstreamHeight) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpstreamHeight) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RevisionHeight != 0 { + i = encodeVarintProxy(dAtA, i, uint64(m.RevisionHeight)) + i-- + dAtA[i] = 0x10 + } + if m.RevisionNumber != 0 { + i = encodeVarintProxy(dAtA, i, uint64(m.RevisionNumber)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintProxy(dAtA []byte, offset int, v uint64) int { offset -= sovProxy(v) base := offset @@ -281,6 +599,11 @@ func (m *ClientState) Size() (n int) { l = m.IbcPrefix.Size() n += 1 + l + sovProxy(uint64(l)) } + l = m.UpstreamHeight.Size() + n += 1 + l + sovProxy(uint64(l)) + if m.UpstreamTimestamp != 0 { + n += 1 + sovProxy(uint64(m.UpstreamTimestamp)) + } return n } @@ -297,6 +620,58 @@ func (m *ConsensusState) Size() (n int) { return n } +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProxyHeader != nil { + l = m.ProxyHeader.Size() + n += 1 + l + sovProxy(uint64(l)) + } + if m.UpstreamBlockProof != nil { + l = m.UpstreamBlockProof.Size() + n += 1 + l + sovProxy(uint64(l)) + } + return n +} + +func (m *UpstreamBlockProof) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovProxy(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovProxy(uint64(l)) + l = m.UpstreamHeight.Size() + n += 1 + l + sovProxy(uint64(l)) + if m.UpstreamTimestamp != 0 { + n += 1 + sovProxy(uint64(m.UpstreamTimestamp)) + } + return n +} + +func (m *UpstreamHeight) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.RevisionNumber != 0 { + n += 1 + sovProxy(uint64(m.RevisionNumber)) + } + if m.RevisionHeight != 0 { + n += 1 + sovProxy(uint64(m.RevisionHeight)) + } + return n +} + func sovProxy(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -472,6 +847,58 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpstreamHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UpstreamHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpstreamTimestamp", wireType) + } + m.UpstreamTimestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UpstreamTimestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProxy(dAtA[iNdEx:]) @@ -579,6 +1006,385 @@ func (m *ConsensusState) Unmarshal(dAtA []byte) error { } return nil } +func (m *Header) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProxyHeader", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProxyHeader == nil { + m.ProxyHeader = &types.Any{} + } + if err := m.ProxyHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpstreamBlockProof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpstreamBlockProof == nil { + m.UpstreamBlockProof = &UpstreamBlockProof{} + } + if err := m.UpstreamBlockProof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProxy(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProxy + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpstreamBlockProof) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpstreamBlockProof: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpstreamBlockProof: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpstreamHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProxy + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProxy + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UpstreamHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpstreamTimestamp", wireType) + } + m.UpstreamTimestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UpstreamTimestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProxy(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProxy + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpstreamHeight) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpstreamHeight: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpstreamHeight: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionNumber", wireType) + } + m.RevisionNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHeight", wireType) + } + m.RevisionHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProxy + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProxy(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProxy + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipProxy(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/light-clients/xx-proxy/types/store.go b/modules/light-clients/xx-proxy/types/store.go index a77ea88..16a2142 100644 --- a/modules/light-clients/xx-proxy/types/store.go +++ b/modules/light-clients/xx-proxy/types/store.go @@ -1,6 +1,9 @@ package types import ( + "encoding/binary" + "fmt" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -35,3 +38,19 @@ func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported return consensusState, nil } + +func SetUpstreamBlockTime(clientStore sdk.KVStore, height exported.Height, timestamp uint64) { + var bz [8]byte + binary.BigEndian.PutUint64(bz[:], timestamp) + clientStore.Set([]byte(fmt.Sprintf("/upstreamBlockTimes/%s", height.String())), bz[:]) +} + +func GetUpstreamBlockTime(clientStore sdk.KVStore, height exported.Height) (uint64, bool) { + bz := clientStore.Get([]byte(fmt.Sprintf("/upstreamBlockTimes/%s", height.String()))) + if l := len(bz); l == 0 { + return 0, false + } else if l != 8 { + panic("the state is corrupted") + } + return binary.BigEndian.Uint64(bz), true +} diff --git a/modules/light-clients/xx-proxy/types/tendermint/proxy_client.go b/modules/light-clients/xx-proxy/types/tendermint/proxy_client.go new file mode 100644 index 0000000..73cc413 --- /dev/null +++ b/modules/light-clients/xx-proxy/types/tendermint/proxy_client.go @@ -0,0 +1,97 @@ +package tendermint + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/modules/core/exported" + tmtypes "github.com/cosmos/ibc-go/modules/light-clients/07-tendermint/types" + "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-proxy/types" +) + +type ProxyClientBuilder struct{} + +var _ types.ProxyClientBuilder = (*ProxyClientBuilder)(nil) + +func (ProxyClientBuilder) ClientType() string { + return exported.Tendermint +} + +func (pb ProxyClientBuilder) Build(clientState exported.ClientState) (types.ProxyClientI, error) { + cs, ok := clientState.(*tmtypes.ClientState) + if !ok { + return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", &tmtypes.ClientState{}, clientState) + } + return ProxyClient{clientState: *cs}, nil +} + +type ProxyClient struct { + clientState tmtypes.ClientState +} + +var _ types.ProxyClientI = (*ProxyClient)(nil) + +func (pc ProxyClient) VerifyBlockTime( + store sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + prefix exported.Prefix, + upstreamHeight exported.Height, + proof []byte, + timestamp uint64, +) error { + merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, pc.clientState, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := commitmenttypes.NewMerklePath(fmt.Sprintf("block/%s", upstreamHeight.String())) + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + return merkleProof.VerifyMembership(pc.clientState.ProofSpecs, provingConsensusState.GetRoot(), path, sdk.Uint64ToBigEndian(timestamp)) +} + +// produceVerificationArgs perfoms the basic checks on the arguments that are +// shared between the verification functions and returns the unmarshalled +// merkle proof, the consensus state and an error if one occurred. +func produceVerificationArgs( + store sdk.KVStore, + cdc codec.BinaryCodec, + cs tmtypes.ClientState, + height exported.Height, + prefix exported.Prefix, + proof []byte, +) (merkleProof commitmenttypes.MerkleProof, consensusState *tmtypes.ConsensusState, err error) { + if cs.GetLatestHeight().LT(height) { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height, + ) + } + + if prefix == nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") + } + + if proof == nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty") + } + + if err = cdc.Unmarshal(proof, &merkleProof); err != nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") + } + + consensusState, err = tmtypes.GetConsensusState(store, cdc, height) + if err != nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(err, "please ensure the proof was constructed against a height that exists on the client") + } + + return merkleProof, consensusState, nil +} diff --git a/modules/light-clients/xx-proxy/types/types.go b/modules/light-clients/xx-proxy/types/types.go index ab1254f..6d5aa92 100644 --- a/modules/light-clients/xx-proxy/types/types.go +++ b/modules/light-clients/xx-proxy/types/types.go @@ -1 +1,167 @@ package types + +import ( + "fmt" + "math/big" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/modules/core/exported" +) + +type ProxyClientI interface { + VerifyBlockTime( + store sdk.KVStore, + cdc codec.BinaryCodec, + height exported.Height, + prefix exported.Prefix, + upstreamHeight exported.Height, + proof []byte, + timestamp uint64, + ) error +} + +var GlobalProxyClientRegistry = NewProxyClientRegistry() + +type ProxyClientRegistry struct { + builders map[string]ProxyClientBuilder + sealed bool +} + +func NewProxyClientRegistry() *ProxyClientRegistry { + return &ProxyClientRegistry{builders: make(map[string]ProxyClientBuilder)} +} + +func (pr *ProxyClientRegistry) RegisterBuilder(builder ProxyClientBuilder) { + if pr.sealed { + panic(fmt.Errorf("the registry is already sealed")) + } + clientType := builder.ClientType() + builder, ok := pr.builders[clientType] + if !ok { + panic(fmt.Errorf("the clientType '%v' already exists", clientType)) + } + pr.builders[clientType] = builder +} + +func (pr *ProxyClientRegistry) Seal() { + if pr.sealed { + panic(fmt.Errorf("the registry is already sealed")) + } + pr.sealed = true +} + +func (pr ProxyClientRegistry) MustGet(clientType string) ProxyClientBuilder { + builder, ok := pr.builders[clientType] + if !ok { + panic(fmt.Errorf("the clientType '%v' not found", clientType)) + } + return builder +} + +type ProxyClientBuilder interface { + ClientType() string + Build(exported.ClientState) (ProxyClientI, error) +} + +var _ exported.Height = (*UpstreamHeight)(nil) + +// ZeroHeight is a helper function which returns an uninitialized height. +func ZeroHeight() UpstreamHeight { + return UpstreamHeight{} +} + +// NewHeight is a constructor for the IBC height type +func NewHeight(revisionNumber, revisionHeight uint64) UpstreamHeight { + return UpstreamHeight{ + RevisionNumber: revisionNumber, + RevisionHeight: revisionHeight, + } +} + +// GetRevisionNumber returns the revision-number of the height +func (h UpstreamHeight) GetRevisionNumber() uint64 { + return h.RevisionNumber +} + +// GetRevisionHeight returns the revision-height of the height +func (h UpstreamHeight) GetRevisionHeight() uint64 { + return h.RevisionHeight +} + +// Compare implements a method to compare two heights. When comparing two heights a, b +// we can call a.Compare(b) which will return +// -1 if a < b +// 0 if a = b +// 1 if a > b +// +// It first compares based on revision numbers, whichever has the higher revision number is the higher height +// If revision number is the same, then the revision height is compared +func (h UpstreamHeight) Compare(other exported.Height) int64 { + // height, ok := other.(UpstreamHeight) + // if !ok { + // panic(fmt.Sprintf("cannot compare against invalid height type: %T. expected height type: %T", other, h)) + // } + height := NewHeight(other.GetRevisionNumber(), other.GetRevisionHeight()) + var a, b big.Int + if h.RevisionNumber != height.RevisionNumber { + a.SetUint64(h.RevisionNumber) + b.SetUint64(height.RevisionNumber) + } else { + a.SetUint64(h.RevisionHeight) + b.SetUint64(height.RevisionHeight) + } + return int64(a.Cmp(&b)) +} + +// LT Helper comparison function returns true if h < other +func (h UpstreamHeight) LT(other exported.Height) bool { + return h.Compare(other) == -1 +} + +// LTE Helper comparison function returns true if h <= other +func (h UpstreamHeight) LTE(other exported.Height) bool { + cmp := h.Compare(other) + return cmp <= 0 +} + +// GT Helper comparison function returns true if h > other +func (h UpstreamHeight) GT(other exported.Height) bool { + return h.Compare(other) == 1 +} + +// GTE Helper comparison function returns true if h >= other +func (h UpstreamHeight) GTE(other exported.Height) bool { + cmp := h.Compare(other) + return cmp >= 0 +} + +// EQ Helper comparison function returns true if h == other +func (h UpstreamHeight) EQ(other exported.Height) bool { + return h.Compare(other) == 0 +} + +// String returns a string representation of UpstreamHeight +func (h UpstreamHeight) String() string { + return fmt.Sprintf("%d-%d", h.RevisionNumber, h.RevisionHeight) +} + +// Decrement will return a new height with the RevisionHeight decremented +// If the RevisionHeight is already at lowest value (1), then false success flag is returend +func (h UpstreamHeight) Decrement() (decremented exported.Height, success bool) { + if h.RevisionHeight == 0 { + return UpstreamHeight{}, false + } + return NewHeight(h.RevisionNumber, h.RevisionHeight-1), true +} + +// Increment will return a height with the same revision number but an +// incremented revision height +func (h UpstreamHeight) Increment() exported.Height { + return NewHeight(h.RevisionNumber, h.RevisionHeight+1) +} + +// IsZero returns true if height revision and revision-height are both 0 +func (h UpstreamHeight) IsZero() bool { + return h.RevisionNumber == 0 && h.RevisionHeight == 0 +} diff --git a/modules/light-clients/xx-proxy/types/update.go b/modules/light-clients/xx-proxy/types/update.go index 3feb51c..067f7da 100644 --- a/modules/light-clients/xx-proxy/types/update.go +++ b/modules/light-clients/xx-proxy/types/update.go @@ -7,13 +7,15 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/modules/core/24-host" "github.com/cosmos/ibc-go/modules/core/exported" ) // Update and Misbehaviour functions func (cs ClientState) CheckHeaderAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, header exported.Header) (exported.ClientState, exported.ConsensusState, error) { - clientState, consensusState, err := cs.GetProxyClientState().CheckHeaderAndUpdateState(ctx, cdc, NewProxyExtractorStore(cdc, clientStore), header) + h := header.(*Header) + clientState, consensusState, err := cs.GetProxyClientState().CheckHeaderAndUpdateState(ctx, cdc, NewProxyExtractorStore(cdc, clientStore), h.GetProxyHeader()) if err != nil { return nil, nil, err } @@ -27,6 +29,29 @@ func (cs ClientState) CheckHeaderAndUpdateState(ctx sdk.Context, cdc codec.Binar } cs.ProxyClientState = anyClientState proxyConsensusState := &ConsensusState{ProxyConsensusState: anyConsensusState} + + if ubp := h.UpstreamBlockProof; ubp != nil { + pc, err := GlobalProxyClientRegistry.MustGet(cs.GetProxyClientState().ClientType()).Build(cs.GetProxyClientState()) + if err != nil { + return nil, nil, err + } + prefix := commitmenttypes.MultiPrefix{ + Prefix: cs.ProxyPrefix, + PathPrefix: []byte(cs.UpstreamClientId + "/"), + } + if err := pc.VerifyBlockTime( + NewProxyExtractorStore(cdc, clientStore), + cdc, ubp.ProofHeight, prefix, ubp.UpstreamHeight, ubp.Proof, ubp.UpstreamTimestamp, + ); err != nil { + return nil, nil, err + } + if ubp.UpstreamHeight.GT(cs.UpstreamHeight) { + // NOTE: should we check if proofHeight is also advanced? + cs.UpstreamHeight = UpstreamHeight(ubp.UpstreamHeight) + cs.UpstreamTimestamp = ubp.UpstreamTimestamp + } + SetUpstreamBlockTime(clientStore, ubp.UpstreamHeight, ubp.UpstreamTimestamp) + } return &cs, proxyConsensusState, nil } diff --git a/modules/proxy/keeper/client_keeper.go b/modules/proxy/keeper/client_keeper.go deleted file mode 100644 index 66819d7..0000000 --- a/modules/proxy/keeper/client_keeper.go +++ /dev/null @@ -1,26 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" - "github.com/cosmos/ibc-go/modules/core/exported" - multivtypes "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-multiv/types" -) - -// ClientKeeper override `ValidateSelfClient` in the keeper of ibc-client -// Original method doesn't yet support a consensus state for a general client -type ClientKeeper struct { - connectiontypes.ClientKeeper -} - -func NewClientKeeper(k connectiontypes.ClientKeeper) ClientKeeper { - return ClientKeeper{ClientKeeper: k} -} - -func (k ClientKeeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { - cs, ok := clientState.(*multivtypes.ClientState) - if !ok { - return k.ClientKeeper.ValidateSelfClient(ctx, clientState) - } - return k.ClientKeeper.ValidateSelfClient(ctx, cs.GetUnderlyingClientState()) -} diff --git a/modules/proxy/keeper/commitment.go b/modules/proxy/keeper/commitment.go index 08cba2e..7436ad0 100644 --- a/modules/proxy/keeper/commitment.go +++ b/modules/proxy/keeper/commitment.go @@ -1,8 +1,11 @@ package keeper import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "encoding/binary" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" @@ -72,6 +75,26 @@ func (k Keeper) GetProxyChannel( return channel, true } +func (k Keeper) SetProxyUpstreamBlockTime( + ctx sdk.Context, + upstreamPrefix exported.Prefix, // upstream's prefix + upstreamClientID string, // client id corresponding to upstream on proxy + height exported.Height, +) error { + consensusState, found := k.clientKeeper.GetClientConsensusState(ctx, upstreamClientID, height) + if !found { + return sdkerrors.Wrapf( + clienttypes.ErrConsensusStateNotFound, + "clientID (%s), height (%s)", upstreamClientID, height, + ) + } + var bz [8]byte + binary.BigEndian.PutUint64(bz[:], consensusState.GetTimestamp()) + store := k.ProxyStore(ctx, upstreamPrefix, upstreamClientID) + store.Set([]byte(fmt.Sprintf("block/%s", height.String())), bz[:]) + return nil +} + func (k Keeper) SetProxyClientState( ctx sdk.Context, upstreamPrefix exported.Prefix, // upstream's prefix diff --git a/modules/proxy/keeper/core_extension.go b/modules/proxy/keeper/core_extension.go new file mode 100644 index 0000000..7ced8ee --- /dev/null +++ b/modules/proxy/keeper/core_extension.go @@ -0,0 +1,66 @@ +package keeper + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/modules/core/exported" + multivtypes "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-multiv/types" + proxyclienttypes "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-proxy/types" +) + +// ClientKeeper override `ValidateSelfClient` in the keeper of ibc-client +// Original method doesn't yet support a consensus state for a general client +type ClientKeeper struct { + connectiontypes.ClientKeeper +} + +func NewClientKeeper(k connectiontypes.ClientKeeper) ClientKeeper { + return ClientKeeper{ClientKeeper: k} +} + +func (k ClientKeeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { + cs, ok := clientState.(*multivtypes.ClientState) + if !ok { + return k.ClientKeeper.ValidateSelfClient(ctx, clientState) + } + return k.ClientKeeper.ValidateSelfClient(ctx, cs.GetUnderlyingClientState()) +} + +type ConnectionKeeper struct { + clientKeeper connectiontypes.ClientKeeper + channeltypes.ConnectionKeeper +} + +func NewConnectionKeeper(clientKeeper connectiontypes.ClientKeeper, base channeltypes.ConnectionKeeper) ConnectionKeeper { + return ConnectionKeeper{clientKeeper: clientKeeper, ConnectionKeeper: base} +} + +func (k ConnectionKeeper) GetTimestampAtHeight(ctx sdk.Context, connection connectiontypes.ConnectionEnd, height exported.Height) (uint64, error) { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return 0, sdkerrors.Wrapf( + clienttypes.ErrClientNotFound, + "clientID (%s)", connection.GetClientID(), + ) + } + if clientState.ClientType() == proxyclienttypes.ProxyClientType { + blockTime, found := proxyclienttypes.GetUpstreamBlockTime(k.clientKeeper.ClientStore(ctx, connection.GetClientID()), height) + if !found { + return 0, sdkerrors.Wrapf(errors.New("ErrUpstreamBlockTimeNotFound"), "clientID (%s), height (%s)", connection.GetClientID(), height) + } + return blockTime, nil + } + consensusState, found := k.clientKeeper.GetClientConsensusState(ctx, connection.GetClientID(), height) + if !found { + return 0, sdkerrors.Wrapf( + clienttypes.ErrConsensusStateNotFound, + "clientID (%s), height (%s)", connection.GetClientID(), height, + ) + } + return consensusState.GetTimestamp(), nil +} diff --git a/modules/proxy/keeper/proxy.go b/modules/proxy/keeper/proxy.go index 8051662..b000922 100644 --- a/modules/proxy/keeper/proxy.go +++ b/modules/proxy/keeper/proxy.go @@ -41,6 +41,9 @@ func (k Keeper) VerifyAndProxyClientState( if err := k.VerifyClientState(ctx, upstreamClientID, upstreamPrefix, counterpartyClientID, height, proof, clientState); err != nil { return err } + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyClientState( ctx, upstreamPrefix, @@ -86,6 +89,9 @@ func (k Keeper) VerifyAndProxyClientConsensusState( if err := k.VerifyClientConsensusState(ctx, upstreamClientID, upstreamPrefix, counterpartyClientID, height, consensusHeight, proof, consensusState); err != nil { return err } + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyClientConsensusState( ctx, upstreamPrefix, @@ -131,6 +137,9 @@ func (k Keeper) VerifyAndProxyConnectionState( if err := k.VerifyConnectionState(ctx, upstreamClientID, upstreamPrefix, connection, height, proof, connectionID); err != nil { return err } + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyConnection( ctx, upstreamPrefix, @@ -179,6 +188,9 @@ func (k Keeper) VerifyAndProxyChannelState( if err := k.VerifyChannelState(ctx, upstreamClientID, upstreamPrefix, height, proof, portID, channelID, channel); err != nil { return err } + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyChannel( ctx, upstreamPrefix, @@ -235,7 +247,9 @@ func (k Keeper) VerifyAndProxyPacketCommitment( if err := k.VerifyPacketCommitment(ctx, upstreamClientID, upstreamPrefix, connection, height, proof, portID, channelID, sequence, commitmentBytes); err != nil { return err } - + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyPacketCommitment( ctx, upstreamPrefix, @@ -293,7 +307,9 @@ func (k Keeper) VerifyAndProxyPacketAcknowledgement( if err := k.VerifyPacketAcknowledgement(ctx, upstreamClientID, upstreamPrefix, connection, height, proof, portID, channelID, sequence, acknowledgement); err != nil { return err } - + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyPacketAcknowledgement( ctx, upstreamPrefix, @@ -350,7 +366,9 @@ func (k Keeper) VerifyAndProxyPacketReceiptAbsence( if err := k.VerifyPacketReceiptAbsence(ctx, upstreamClientID, upstreamPrefix, connection, height, proof, portID, channelID, sequence); err != nil { return err } - + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyPacketReceiptAbsence( ctx, upstreamPrefix, @@ -405,7 +423,9 @@ func (k Keeper) VerifyAndProxyNextSequenceRecv( if err := k.VerifyNextSequenceRecv(ctx, upstreamClientID, upstreamPrefix, connection, height, proof, portID, channelID, nextSequenceRecv); err != nil { return err } - + if err := k.SetProxyUpstreamBlockTime(ctx, upstreamPrefix, upstreamClientID, height); err != nil { + return err + } return k.SetProxyNextSequenceRecv( ctx, upstreamPrefix, diff --git a/modules/proxy/keeper/proxy_test.go b/modules/proxy/keeper/proxy_test.go index 5d17eab..766cb47 100644 --- a/modules/proxy/keeper/proxy_test.go +++ b/modules/proxy/keeper/proxy_test.go @@ -28,7 +28,7 @@ func (suite *KeeperTestSuite) TestOneSideProxy1() { clientBA, err := suite.coordinator.CreateMultiVClient(suite.chainB, suite.chainA, exported.Tendermint, 0) suite.Require().NoError(err) - clientAC, err := suite.coordinator.CreateProxyClient(suite.chainA, suite.chainC, exported.Tendermint, clientCB) + clientAC, err := suite.coordinator.CreateProxyClient(suite.chainA, suite.chainC, suite.chainB, exported.Tendermint, clientCB) suite.Require().NoError(err) ppair := ibctesting.ProxyPair{{suite.chainC, clientAC, clientCB, suite.chainB.GetPrefix()}, nil} @@ -51,7 +51,7 @@ func (suite *KeeperTestSuite) TestOneSideProxy2() { suite.Require().NoError(err) // downstream creates a proxy client - clientBC, err := suite.coordinator.CreateProxyClient(suite.chainB, suite.chainC, exported.Tendermint, clientCA) + clientBC, err := suite.coordinator.CreateProxyClient(suite.chainB, suite.chainC, suite.chainA, exported.Tendermint, clientCA) suite.Require().NoError(err) ppair := ibctesting.ProxyPair{nil, {suite.chainC, clientBC, clientCA, suite.chainA.GetPrefix()}} @@ -74,10 +74,10 @@ func (suite *KeeperTestSuite) TestBothSideProxy() { clientDA, err := suite.coordinator.CreateClient2(suite.chainD, suite.chainA, exported.Tendermint, true, 0) suite.Require().NoError(err) - clientAC, err := suite.coordinator.CreateProxyClient(suite.chainA, suite.chainC, exported.Tendermint, clientCB) + clientAC, err := suite.coordinator.CreateProxyClient(suite.chainA, suite.chainC, suite.chainB, exported.Tendermint, clientCB) suite.Require().NoError(err) - clientBD, err := suite.coordinator.CreateProxyClient(suite.chainB, suite.chainD, exported.Tendermint, clientDA) + clientBD, err := suite.coordinator.CreateProxyClient(suite.chainB, suite.chainD, suite.chainA, exported.Tendermint, clientDA) suite.Require().NoError(err) ppair := ibctesting.ProxyPair{{suite.chainC, clientAC, clientCB, suite.chainB.GetPrefix()}, {suite.chainD, clientBD, clientDA, suite.chainA.GetPrefix()}} diff --git a/modules/proxy/types/codec.go b/modules/proxy/types/codec.go index 196489e..a5f37f7 100644 --- a/modules/proxy/types/codec.go +++ b/modules/proxy/types/codec.go @@ -26,6 +26,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &MsgProxyRecvPacket{}, &MsgProxyAcknowledgePacket{}, ) + registry.RegisterImplementations((*exported.Header)(nil), &proxytypes.Header{}) registry.RegisterImplementations((*exported.ClientState)(nil), &proxytypes.ClientState{}) registry.RegisterImplementations((*exported.ConsensusState)(nil), &proxytypes.ConsensusState{}) multivtypes.RegisterInterfaces(registry) diff --git a/modules/proxy/types/expected_keepers.go b/modules/proxy/types/expected_keepers.go index 2d0fd53..c1dcc4d 100644 --- a/modules/proxy/types/expected_keepers.go +++ b/modules/proxy/types/expected_keepers.go @@ -8,6 +8,7 @@ import ( type ClientKeeper interface { ClientStore(ctx sdk.Context, clientID string) sdk.KVStore GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error } diff --git a/proto/ibc/lightclients/proxy/v1/proxy.proto b/proto/ibc/lightclients/proxy/v1/proxy.proto index 6f117c8..4c1b539 100644 --- a/proto/ibc/lightclients/proxy/v1/proxy.proto +++ b/proto/ibc/lightclients/proxy/v1/proxy.proto @@ -5,6 +5,7 @@ option go_package = "github.com/datachainlab/ibc-proxy/modules/light-clients/xx- import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; +import "ibc/core/client/v1/client.proto"; import "ibc/core/commitment/v1/commitment.proto"; message ClientState { @@ -19,6 +20,10 @@ message ClientState { ibc.core.commitment.v1.MerklePrefix proxy_prefix = 3; // the ibc commitment prefix of the proxy chain ibc.core.commitment.v1.MerklePrefix ibc_prefix = 4; + // latest height on the upstream chain + UpstreamHeight upstream_height = 5 [(gogoproto.nullable) = false]; + // latest timestamp on the upstream chain + uint64 upstream_timestamp = 6; } message ConsensusState { @@ -28,3 +33,27 @@ message ConsensusState { // the type must implements ConsensusState interface google.protobuf.Any proxy_consensus_state = 1; } + +message Header { + option (gogoproto.goproto_getters) = false; + + google.protobuf.Any proxy_header = 1; + UpstreamBlockProof upstream_block_proof = 2; +} + +message UpstreamBlockProof { + bytes proof = 1; + ibc.core.client.v1.Height proof_height = 2 [(gogoproto.nullable) = false]; + ibc.core.client.v1.Height upstream_height = 3 [(gogoproto.nullable) = false]; + uint64 upstream_timestamp = 4; +} + +message UpstreamHeight { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // the revision that the client is currently on + uint64 revision_number = 1 [(gogoproto.moretags) = "yaml:\"revision_number\""]; + // the height within the given revision + uint64 revision_height = 2 [(gogoproto.moretags) = "yaml:\"revision_height\""]; +} diff --git a/testing/chain.go b/testing/chain.go index af593bd..95989e0 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -37,6 +37,7 @@ import ( "github.com/cosmos/ibc-go/modules/core/types" ibctmtypes "github.com/cosmos/ibc-go/modules/light-clients/07-tendermint/types" + proxyclienttypes "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-proxy/types" "github.com/datachainlab/ibc-proxy/testing/mock" "github.com/datachainlab/ibc-proxy/testing/simapp" ) @@ -232,12 +233,19 @@ func (chain *TestChain) QueryClientStateProof(clientID string) (exported.ClientS return clientState, proofClient } +func GetClientLatestHeight(clientState exported.ClientState) clienttypes.Height { + if clientState.ClientType() != proxyclienttypes.ProxyClientType { + return clientState.GetLatestHeight().(clienttypes.Height) + } + return clientState.(*proxyclienttypes.ClientState).GetProxyClientState().GetLatestHeight().(clienttypes.Height) +} + // QueryConsensusStateProof performs an abci query for a consensus state // stored on the given clientID. The proof and consensusHeight are returned. func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, clienttypes.Height) { clientState := chain.GetClientState(clientID) - consensusHeight := clientState.GetLatestHeight().(clienttypes.Height) + consensusHeight := GetClientLatestHeight(clientState) consensusKey := host.FullConsensusStateKey(clientID, consensusHeight) proofConsensus, _ := chain.QueryProof(consensusKey) @@ -495,7 +503,7 @@ func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string) func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctmtypes.Header, error) { header := counterparty.LastHeader // Relayer must query for LatestHeight on client to get TrustedHeight - trustedHeight := chain.GetClientState(clientID).GetLatestHeight().(clienttypes.Height) + trustedHeight := GetClientLatestHeight(chain.GetClientState(clientID)) var ( tmTrustedVals *tmtypes.ValidatorSet ok bool diff --git a/testing/coordinator.go b/testing/coordinator.go index fcd3d81..45f6243 100644 --- a/testing/coordinator.go +++ b/testing/coordinator.go @@ -6,6 +6,7 @@ import ( "testing" "time" + proxyclienttypes "github.com/datachainlab/ibc-proxy/modules/light-clients/xx-proxy/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -126,7 +127,8 @@ func (coord *Coordinator) UpdateClient( switch clientType { case exported.Tendermint: err = source.UpdateTMClient(counterparty, clientID) - + case proxyclienttypes.ProxyClientType: + err = source.UpdateProxyClient(counterparty, clientID) default: err = fmt.Errorf("client type %s is not supported", clientType) } @@ -140,22 +142,22 @@ func (coord *Coordinator) UpdateClient( return nil } -func (coord *Coordinator) UpdateClients(chains []*TestChain, clientIDs []string, clientType string) error { +func (coord *Coordinator) UpdateClients(chains []*TestChain, clients [][2]string) error { if len(chains) == 0 { return fmt.Errorf("chains should be non-empty") - } else if len(chains) != len(clientIDs)+1 { + } else if len(chains) != len(clients)+1 { return fmt.Errorf("length of items mismatch") } rChain := make([]*TestChain, len(chains)) - rClientIDs := make([]string, len(clientIDs)) + rClients := make([][2]string, len(clients)) for i := 0; i < len(chains); i++ { rChain[i] = chains[len(chains)-1-i] } - for i := 0; i < len(clientIDs); i++ { - rClientIDs[i] = clientIDs[len(clientIDs)-1-i] + for i := 0; i < len(clients); i++ { + rClients[i] = clients[len(clients)-1-i] } for i, chain := range rChain[:len(rChain)-1] { - if err := coord.UpdateClient(rChain[i+1], chain, rClientIDs[i], clientType); err != nil { + if err := coord.UpdateClient(rChain[i+1], chain, rClients[i][1], rClients[i][0]); err != nil { return err } } diff --git a/testing/multiv.go b/testing/multiv.go index 7ec2293..831611f 100644 --- a/testing/multiv.go +++ b/testing/multiv.go @@ -159,8 +159,7 @@ func (chain *TestChain) QueryMultiVLeafClientProof(head *multivtypes.Proof, upst if err != nil { panic(err) } - h := cs.GetLatestHeight() - upstreamClientState, upstreamClientProof, upstreamProofHeight := chain.queryClientStateProof(upstreamClientID, int64(h.GetRevisionHeight())-1) + upstreamClientState, upstreamClientProof, upstreamProofHeight := chain.queryClientStateProof(upstreamClientID, int64(GetClientLatestHeight(cs).GetRevisionHeight())-1) leafClient := &multivtypes.LeafProof{ Proof: upstreamClientProof, ProofHeight: upstreamProofHeight, @@ -174,8 +173,7 @@ func (chain *TestChain) QueryMultiVLeafConsensusProof(head *multivtypes.Proof, u if err != nil { panic(err) } - h := cs.GetLatestHeight() - upstreamConsensusProof, upstreamConsensusHeight, upstreamProofHeight := chain.queryConsensusStateProof(upstreamClientID, int64(h.GetRevisionHeight())-1) + upstreamConsensusProof, upstreamConsensusHeight, upstreamProofHeight := chain.queryConsensusStateProof(upstreamClientID, int64(GetClientLatestHeight(cs).GetRevisionHeight())-1) leafConsensus := &multivtypes.LeafProof{ Proof: upstreamConsensusProof, ProofHeight: upstreamProofHeight, @@ -228,7 +226,7 @@ func (chain *TestChain) queryClientStateProof(clientID string, height int64) (ex func (chain *TestChain) queryConsensusStateProof(clientID string, height int64) ([]byte, clienttypes.Height, clienttypes.Height) { clientState := chain.GetClientState(clientID) - consensusHeight := clientState.GetLatestHeight().(clienttypes.Height) + consensusHeight := GetClientLatestHeight(clientState) consensusKey := host.FullConsensusStateKey(clientID, consensusHeight) proofConsensus, proofHeight := chain.queryProof(consensusKey, height) diff --git a/testing/proxy.go b/testing/proxy.go index aa8204e..cc4b0c9 100644 --- a/testing/proxy.go +++ b/testing/proxy.go @@ -33,24 +33,39 @@ func (coord *Coordinator) CreateClient2( return clientID, err } -func (coord *Coordinator) CreateProxyClient(downstream, proxy *TestChain, clientType string, upstreamClientID string) (string, error) { +func (coord *Coordinator) CreateProxyClient( + downstream, proxy, upstream *TestChain, + clientType string, upstreamClientID string) (string, error) { clientID := downstream.NewClientID(proxyclienttypes.ProxyClientType) - if err := downstream.CreateProxyClient(proxy, clientType, clientID, upstreamClientID); err != nil { + if err := downstream.CreateProxyClient(proxy, clientType, clientID, upstreamClientID, upstream.LastHeader.GetHeight(), uint64(upstream.LastHeader.GetTime().UnixNano())); err != nil { return "", err } return clientID, nil } -func (chain *TestChain) CreateProxyClient(proxy *TestChain, clientType string, clientID string, upstreamClientID string) error { +func (chain *TestChain) CreateProxyClient( + proxy *TestChain, + clientType string, clientID string, upstreamClientID string, + upstreamHeight exported.Height, upstreamTimestamp uint64) error { msg := chain.ConstructMsgCreateClient(proxy, clientID, clientType) ibcPrefix := commitmenttypes.NewMerklePrefix([]byte(host.StoreKey)) proxyPrefix := commitmenttypes.NewMerklePrefix([]byte(proxytypes.StoreKey)) + h := msg.ClientState.GetCachedValue().(exported.ClientState).GetLatestHeight() clientState := &proxyclienttypes.ClientState{ ProxyClientState: msg.ClientState, UpstreamClientId: upstreamClientID, - IbcPrefix: &ibcPrefix, ProxyPrefix: &proxyPrefix, + IbcPrefix: &ibcPrefix, + // TODO For now, UpstreamHeight and UpstreamTimestamp indicate the proxy's block in the initialization + // after fixed https://github.com/cosmos/ibc-go/issues/284, we should apply a fix to use the upstream's height instead + UpstreamHeight: proxyclienttypes.NewHeight(h.GetRevisionNumber(), h.GetRevisionHeight()), + UpstreamTimestamp: msg.ConsensusState.GetCachedValue().(exported.ConsensusState).GetTimestamp(), + // UpstreamHeight: proxyclienttypes.NewHeight(upstreamHeight.GetRevisionNumber(), upstreamHeight.GetRevisionHeight()), + // UpstreamTimestamp: upstreamTimestamp, + } + if err := clientState.Validate(); err != nil { + return err } anyClientState, err := clienttypes.PackClientState(clientState) if err != nil { @@ -73,7 +88,12 @@ func (chain *TestChain) UpdateProxyClient(proxy *TestChain, proxyClientID string if err != nil { return err } - msg, err := clienttypes.NewMsgUpdateClient(proxyClientID, header, chain.SenderAccount.GetAddress().String()) + anyHeader, err := clienttypes.PackHeader(header) + if err != nil { + return err + } + h := proxyclienttypes.Header{ProxyHeader: anyHeader} + msg, err := clienttypes.NewMsgUpdateClient(proxyClientID, &h, chain.SenderAccount.GetAddress().String()) if err != nil { return err } @@ -201,8 +221,10 @@ func (coord *Coordinator) ConnOpenTryWithProxy( if proxies[1] == nil { if err := coord.UpdateClients( []*TestChain{proxy, counterparty, source}, - []string{proxies[0].UpstreamClientID, counterpartyConnection.ClientID}, - exported.Tendermint); err != nil { + [][2]string{ + {exported.Tendermint, proxies[0].UpstreamClientID}, + {exported.Tendermint, counterpartyConnection.ClientID}, + }); err != nil { return err } var found bool @@ -215,8 +237,11 @@ func (coord *Coordinator) ConnOpenTryWithProxy( } else { if err := coord.UpdateClients( []*TestChain{proxy, counterparty, proxies[1].Chain, source}, - []string{proxies[0].UpstreamClientID, proxies[1].ClientID, proxies[1].UpstreamClientID}, - exported.Tendermint); err != nil { + [][2]string{ + {exported.Tendermint, proxies[0].UpstreamClientID}, + {proxyclienttypes.ProxyClientType, proxies[1].ClientID}, + {exported.Tendermint, proxies[1].UpstreamClientID}, + }); err != nil { return err } head := counterparty.QueryMultiVBranchProof(counterpartyConnection.ClientID) @@ -226,8 +251,8 @@ func (coord *Coordinator) ConnOpenTryWithProxy( } proofInit, proofHeight := counterparty.QueryProof(host.ConnectionKey(counterpartyConnection.ID)) - proxyClientState, proofProxyClient, proofProxyHeight := source.queryClientStateProof(sourceConnection.ClientID, int64(counterpartyClient.GetLatestHeight().GetRevisionHeight()-1)) - proofProxyConsensus, proxyConsensusHeight, _ := source.queryConsensusStateProof(sourceConnection.ClientID, int64(counterpartyClient.GetLatestHeight().GetRevisionHeight()-1)) + proxyClientState, proofProxyClient, proofProxyHeight := source.queryClientStateProof(sourceConnection.ClientID, int64(GetClientLatestHeight(counterpartyClient).GetRevisionHeight()-1)) + proofProxyConsensus, proxyConsensusHeight, _ := source.queryConsensusStateProof(sourceConnection.ClientID, int64(GetClientLatestHeight(counterpartyClient).GetRevisionHeight()-1)) msg, err := proxytypes.NewMsgProxyConnectionOpenTry( counterpartyConnection.ID, @@ -310,8 +335,10 @@ func (coord *Coordinator) ConnOpenAckWithProxy( if proxies[1] == nil { if err := coord.UpdateClients( []*TestChain{proxy, counterparty, source}, - []string{proxies[0].UpstreamClientID, counterpartyConnection.ClientID}, - exported.Tendermint); err != nil { + [][2]string{ + {exported.Tendermint, proxies[0].UpstreamClientID}, + {exported.Tendermint, counterpartyConnection.ClientID}, + }); err != nil { return err } var found bool @@ -324,8 +351,12 @@ func (coord *Coordinator) ConnOpenAckWithProxy( } else { if err := coord.UpdateClients( []*TestChain{proxy, counterparty, proxies[1].Chain, source}, - []string{proxies[0].UpstreamClientID, proxies[1].ClientID, proxies[1].UpstreamClientID}, - exported.Tendermint); err != nil { + [][2]string{ + {exported.Tendermint, proxies[0].UpstreamClientID}, + {proxyclienttypes.ProxyClientType, proxies[1].ClientID}, + {exported.Tendermint, proxies[1].UpstreamClientID}, + }, + ); err != nil { return err } head := counterparty.QueryMultiVBranchProof(counterpartyConnection.ClientID) @@ -335,8 +366,8 @@ func (coord *Coordinator) ConnOpenAckWithProxy( } proofTry, proofHeight := counterparty.QueryProof(host.ConnectionKey(counterpartyConnection.ID)) - proxyClientState, proofProxyClient, proofProxyHeight := source.queryClientStateProof(sourceConnection.ClientID, int64(counterpartyClient.GetLatestHeight().GetRevisionHeight()-1)) - proofProxyConsensus, proxyConsensusHeight, _ := source.queryConsensusStateProof(sourceConnection.ClientID, int64(counterpartyClient.GetLatestHeight().GetRevisionHeight()-1)) + proxyClientState, proofProxyClient, proofProxyHeight := source.queryClientStateProof(sourceConnection.ClientID, int64(GetClientLatestHeight(counterpartyClient).GetRevisionHeight()-1)) + proofProxyConsensus, proxyConsensusHeight, _ := source.queryConsensusStateProof(sourceConnection.ClientID, int64(GetClientLatestHeight(counterpartyClient).GetRevisionHeight()-1)) msg, err := proxytypes.NewMsgProxyConnectionOpenAck( counterpartyConnection.ID, @@ -988,7 +1019,7 @@ func (chain *TestChain) QueryProxyConsensusStateProof(clientID string, upstreamP upstreamClientID, ) require.True(chain.t, found) - consensusHeight := clientState.GetLatestHeight().(clienttypes.Height) + consensusHeight := GetClientLatestHeight(clientState) proofConsensus, _ := chain.QueryProxyProof(proxytypes.ProxyConsensusStateKey(upstreamPrefix, upstreamClientID, clientID, consensusHeight)) return proofConsensus, consensusHeight } diff --git a/testing/simapp/app.go b/testing/simapp/app.go index c6f2ff6..6e6bd22 100644 --- a/testing/simapp/app.go +++ b/testing/simapp/app.go @@ -89,6 +89,8 @@ import ( ibcclientclient "github.com/cosmos/ibc-go/modules/core/02-client/client" ibcclienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" ibcconnectionkeeper "github.com/cosmos/ibc-go/modules/core/03-connection/keeper" + ibcchannelkeeper "github.com/cosmos/ibc-go/modules/core/04-channel/keeper" + ibcchanneltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/modules/core/05-port/types" ibchost "github.com/cosmos/ibc-go/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/modules/core/keeper" @@ -306,7 +308,7 @@ func NewSimApp( ibcKeeper := ibckeeper.NewKeeper( appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, ) - app.IBCKeeper = applyPatchToIBCKeeper(*ibcKeeper, appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName)) + app.IBCKeeper = applyPatchToIBCKeeper(*ibcKeeper, appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), ibcKeeper.PortKeeper, scopedIBCKeeper) app.IBCProxyKeeper = ibcproxykeeper.NewKeeper( appCodec, keys[ibcproxytypes.StoreKey], keys[ibchost.StoreKey], app.IBCKeeper.ClientKeeper, @@ -490,9 +492,13 @@ func NewSimApp( return app } -func applyPatchToIBCKeeper(k ibckeeper.Keeper, cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace) *ibckeeper.Keeper { +func applyPatchToIBCKeeper(k ibckeeper.Keeper, cdc codec.BinaryCodec, key sdk.StoreKey, + paramSpace paramtypes.Subspace, portKeeper ibcchanneltypes.PortKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, +) *ibckeeper.Keeper { clientKeeper := ibcproxykeeper.NewClientKeeper(k.ClientKeeper) k.ConnectionKeeper = ibcconnectionkeeper.NewKeeper(cdc, key, paramSpace, clientKeeper) + connectionKeeper := ibcproxykeeper.NewConnectionKeeper(clientKeeper, k.ConnectionKeeper) + k.ChannelKeeper = ibcchannelkeeper.NewKeeper(cdc, key, clientKeeper, connectionKeeper, portKeeper, scopedKeeper) return &k }