From 7a914daf57c61f63130c053409063c51017e881c Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 12 May 2026 15:40:46 +0900 Subject: [PATCH 01/29] lcp: update speculative batch stream bindings --- lcp | 2 +- relay/elc/tx.pb.go | 6009 ++++++++++++++++++++++++++++++++------------ 2 files changed, 4365 insertions(+), 1646 deletions(-) diff --git a/lcp b/lcp index a6006913..3832bdeb 160000 --- a/lcp +++ b/lcp @@ -1 +1 @@ -Subproject commit a6006913123c973d4b1e8b57020df103551f122e +Subproject commit 3832bdebd4998e0d8b90d65544a428ba536d3f63 diff --git a/relay/elc/tx.pb.go b/relay/elc/tx.pb.go index f0473a4e..741ff088 100644 --- a/relay/elc/tx.pb.go +++ b/relay/elc/tx.pb.go @@ -601,929 +601,1024 @@ func (m *UpdateClientStreamHeaderChunk) XXX_DiscardUnknown() { var xxx_messageInfo_UpdateClientStreamHeaderChunk proto.InternalMessageInfo -func init() { - proto.RegisterType((*MsgCreateClient)(nil), "lcp.service.elc.v1.MsgCreateClient") - proto.RegisterType((*MsgCreateClientResponse)(nil), "lcp.service.elc.v1.MsgCreateClientResponse") - proto.RegisterType((*MsgUpdateClient)(nil), "lcp.service.elc.v1.MsgUpdateClient") - proto.RegisterType((*MsgUpdateClientResponse)(nil), "lcp.service.elc.v1.MsgUpdateClientResponse") - proto.RegisterType((*MsgAggregateMessages)(nil), "lcp.service.elc.v1.MsgAggregateMessages") - proto.RegisterType((*MsgAggregateMessagesResponse)(nil), "lcp.service.elc.v1.MsgAggregateMessagesResponse") - proto.RegisterType((*MsgVerifyMembership)(nil), "lcp.service.elc.v1.MsgVerifyMembership") - proto.RegisterType((*MsgVerifyMembershipResponse)(nil), "lcp.service.elc.v1.MsgVerifyMembershipResponse") - proto.RegisterType((*MsgVerifyNonMembership)(nil), "lcp.service.elc.v1.MsgVerifyNonMembership") - proto.RegisterType((*MsgVerifyNonMembershipResponse)(nil), "lcp.service.elc.v1.MsgVerifyNonMembershipResponse") - proto.RegisterType((*MsgUpdateClientStreamChunk)(nil), "lcp.service.elc.v1.MsgUpdateClientStreamChunk") - proto.RegisterType((*UpdateClientStreamInit)(nil), "lcp.service.elc.v1.UpdateClientStreamInit") - proto.RegisterType((*UpdateClientStreamHeaderChunk)(nil), "lcp.service.elc.v1.UpdateClientStreamHeaderChunk") +type ExplicitStateRef struct { + PrevHeight *types1.Height `protobuf:"bytes,1,opt,name=prev_height,json=prevHeight,proto3" json:"prev_height,omitempty"` + PrevStateId []byte `protobuf:"bytes,2,opt,name=prev_state_id,json=prevStateId,proto3" json:"prev_state_id,omitempty"` + ClientState *types.Any `protobuf:"bytes,3,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty"` + ConsensusState *types.Any `protobuf:"bytes,4,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty"` } -func init() { proto.RegisterFile("lcp/service/elc/v1/tx.proto", fileDescriptor_28db3542707b11e8) } - -var fileDescriptor_28db3542707b11e8 = []byte{ - // 896 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0x26, 0xad, 0x1f, 0x4b, 0x23, 0xb6, 0x49, 0xd7, 0x82, 0xa2, 0xd0, 0x29, 0x65, 0x28, 0x87, - 0x0a, 0x69, 0x4b, 0x56, 0xce, 0x2d, 0xa7, 0x46, 0x06, 0x0a, 0xe7, 0xa0, 0xa2, 0x60, 0x90, 0x00, - 0x6d, 0x0f, 0x32, 0x45, 0xad, 0x97, 0x8b, 0x52, 0x24, 0xb1, 0xbb, 0x14, 0xa2, 0x37, 0xe8, 0xa5, - 0x40, 0x81, 0xf6, 0x01, 0xf2, 0x1a, 0x3d, 0xf5, 0xea, 0x63, 0x8e, 0x3d, 0x09, 0xad, 0x7d, 0xe9, - 0xb5, 0x7e, 0x82, 0x82, 0x4b, 0x5a, 0xa1, 0x7e, 0xa1, 0x20, 0xce, 0x6d, 0x67, 0xf7, 0x9b, 0x6f, - 0x66, 0xbf, 0x1d, 0xce, 0x10, 0x0e, 0x7d, 0x37, 0xb2, 0x38, 0x66, 0x13, 0xea, 0x62, 0x0b, 0xfb, - 0xae, 0x35, 0xe9, 0x5a, 0xe2, 0x95, 0x19, 0xb1, 0x50, 0x84, 0x08, 0xf9, 0x6e, 0x64, 0x66, 0x87, - 0x26, 0xf6, 0x5d, 0x73, 0xd2, 0xd5, 0xeb, 0x24, 0x24, 0xa1, 0x3c, 0xb6, 0x92, 0x55, 0x8a, 0xd4, - 0xef, 0x93, 0x30, 0x24, 0x3e, 0xb6, 0xa4, 0x35, 0x8c, 0xcf, 0x2d, 0x27, 0x98, 0x66, 0x47, 0x2d, - 0x3a, 0x74, 0x2d, 0x37, 0x64, 0xd8, 0x72, 0x7d, 0x8a, 0x03, 0x91, 0x44, 0x48, 0x57, 0x29, 0xa0, - 0xfd, 0xdb, 0x1e, 0xdc, 0xe9, 0x73, 0x72, 0xc2, 0xb0, 0x23, 0xf0, 0x89, 0x3c, 0x41, 0x5d, 0xa8, - 0xa6, 0x98, 0x01, 0x1d, 0x35, 0xd5, 0x23, 0xb5, 0x53, 0xed, 0xd5, 0xaf, 0x67, 0xad, 0xbb, 0x53, - 0x67, 0xec, 0x3f, 0x69, 0xcf, 0x8f, 0xda, 0x76, 0x25, 0x5d, 0x3f, 0x1b, 0xa1, 0xef, 0x40, 0xcb, - 0xf6, 0xb9, 0x70, 0x04, 0x6e, 0xee, 0x1d, 0xa9, 0x9d, 0xda, 0x71, 0xdd, 0x4c, 0x33, 0x33, 0x6f, - 0x32, 0x33, 0x9f, 0x06, 0xd3, 0xde, 0xbd, 0xeb, 0x59, 0xeb, 0x60, 0x81, 0x4b, 0xfa, 0xb4, 0xed, - 0x5a, 0x6a, 0x3e, 0x4f, 0x2c, 0xf4, 0x3d, 0xdc, 0x71, 0xc3, 0x80, 0xe3, 0x80, 0xc7, 0x3c, 0x23, - 0x2d, 0x6c, 0x21, 0xd5, 0xaf, 0x67, 0xad, 0x46, 0x46, 0xba, 0xe8, 0xd6, 0xb6, 0x3f, 0x9e, 0xef, - 0xa4, 0xd4, 0x0d, 0x28, 0x73, 0x4a, 0x02, 0xcc, 0x9a, 0xc5, 0x23, 0xb5, 0xa3, 0xd9, 0x99, 0xf5, - 0xa4, 0xf2, 0xf3, 0xeb, 0x96, 0xf2, 0xef, 0xeb, 0x96, 0xd2, 0xfe, 0x11, 0xee, 0x2d, 0x89, 0x62, - 0x63, 0x1e, 0x25, 0x2c, 0xa8, 0x09, 0xfb, 0x63, 0xcc, 0xb9, 0x43, 0xb0, 0x94, 0x46, 0xb3, 0x6f, - 0x4c, 0xf4, 0x00, 0xaa, 0x09, 0x91, 0x23, 0x62, 0x96, 0x0a, 0xa0, 0xd9, 0x6f, 0x37, 0x72, 0xe4, - 0x7f, 0xa8, 0x52, 0xf2, 0x17, 0xd1, 0xe8, 0xbd, 0x24, 0xff, 0x02, 0xca, 0x1e, 0x76, 0x46, 0x98, - 0x6d, 0x13, 0xdb, 0xce, 0x30, 0xe8, 0x21, 0x7c, 0x44, 0x03, 0xd7, 0x8f, 0x47, 0x38, 0x27, 0x66, - 0xc5, 0xd6, 0xb2, 0xcd, 0x77, 0x13, 0x26, 0x9f, 0xfa, 0x2d, 0x0a, 0x23, 0xa0, 0xde, 0xe7, 0xe4, - 0x29, 0x21, 0x0c, 0x13, 0x47, 0xe0, 0x7e, 0xea, 0xce, 0x73, 0x69, 0xa9, 0xf9, 0xb4, 0x90, 0x0e, - 0x95, 0x2c, 0x04, 0x6f, 0xee, 0x1d, 0x15, 0x3a, 0x9a, 0x3d, 0xb7, 0x91, 0x01, 0x30, 0x0f, 0xc1, - 0x9b, 0x05, 0x79, 0x9a, 0xdb, 0xc9, 0x45, 0x3d, 0x83, 0x07, 0xeb, 0xa2, 0xde, 0xe2, 0xbd, 0xfe, - 0x53, 0xe1, 0xa0, 0xcf, 0xc9, 0x4b, 0xcc, 0xe8, 0xf9, 0xb4, 0x8f, 0xc7, 0x43, 0xcc, 0xb8, 0x47, - 0x23, 0x74, 0xb8, 0xf2, 0xe8, 0xb9, 0xe7, 0x6d, 0x40, 0x39, 0x62, 0xf8, 0x9c, 0xbe, 0xca, 0x98, - 0x33, 0x0b, 0x21, 0x28, 0x46, 0x8e, 0xf0, 0xe4, 0xfb, 0x55, 0x6d, 0xb9, 0x46, 0x75, 0x28, 0x4d, - 0x1c, 0x3f, 0xc6, 0xd9, 0xb3, 0xa5, 0x06, 0x3a, 0x01, 0x2d, 0x62, 0x61, 0x78, 0x3e, 0xf0, 0x30, - 0x25, 0x9e, 0x68, 0x96, 0x64, 0x99, 0xe8, 0x26, 0x1d, 0xba, 0x66, 0xd2, 0x12, 0xcc, 0xac, 0x11, - 0x4c, 0xba, 0xe6, 0xa9, 0x44, 0xf4, 0x8a, 0x17, 0xb3, 0x96, 0x62, 0xd7, 0xa4, 0x57, 0xba, 0x95, - 0x50, 0x4b, 0xb3, 0x59, 0x4e, 0xa9, 0xa5, 0x91, 0x7b, 0x91, 0xfd, 0x0d, 0x85, 0x32, 0x80, 0xc3, - 0x35, 0x57, 0xbe, 0x45, 0x51, 0x67, 0x2a, 0x34, 0xe6, 0x11, 0xbe, 0x0d, 0x83, 0x0f, 0xa1, 0xeb, - 0xb2, 0x82, 0xc5, 0xf7, 0x52, 0xb0, 0xb4, 0x5e, 0xc1, 0xf2, 0x06, 0x05, 0x87, 0x60, 0xac, 0xbf, - 0xdf, 0x2d, 0x8a, 0xf8, 0xa7, 0x0a, 0xfa, 0xd2, 0xf7, 0xfc, 0x5c, 0x30, 0xec, 0x8c, 0x4f, 0xbc, - 0x38, 0xf8, 0x09, 0x7d, 0x0d, 0x45, 0x1a, 0x50, 0x21, 0xd9, 0x6b, 0xc7, 0x8f, 0xcc, 0xd5, 0x89, - 0x64, 0xae, 0xba, 0x3e, 0x0b, 0xa8, 0x38, 0x55, 0x6c, 0xe9, 0x89, 0x5e, 0x82, 0x96, 0x36, 0xa0, - 0x81, 0x9b, 0x30, 0x66, 0xad, 0xaa, 0xbb, 0x1b, 0xd3, 0xa9, 0xf4, 0x94, 0xa9, 0x9c, 0x2a, 0x76, - 0xcd, 0x7b, 0x6b, 0xf6, 0xf6, 0xa1, 0x24, 0x09, 0xdb, 0xbf, 0xa8, 0xd0, 0x58, 0x9f, 0xc3, 0xf6, - 0x32, 0x58, 0xe9, 0x87, 0x7b, 0x5b, 0xfb, 0x61, 0x61, 0xa1, 0xf1, 0xdc, 0x87, 0x8a, 0x98, 0x46, - 0x78, 0x10, 0x33, 0x5f, 0xd6, 0x44, 0xd5, 0xde, 0x4f, 0xec, 0x17, 0xcc, 0x6f, 0x3f, 0x86, 0x4f, - 0xb7, 0x5e, 0x24, 0xa9, 0xb3, 0x91, 0x23, 0x9c, 0xec, 0xc5, 0xe4, 0xfa, 0xf8, 0xf7, 0x12, 0x14, - 0xfa, 0x9c, 0xa0, 0x33, 0xd0, 0x16, 0x06, 0xf1, 0xc3, 0x75, 0x3a, 0x2d, 0x0d, 0x26, 0xfd, 0xf3, - 0x1d, 0x40, 0xf3, 0x92, 0x39, 0x03, 0x6d, 0x61, 0xee, 0x6c, 0x8a, 0x90, 0x07, 0x6d, 0x8c, 0xb0, - 0x76, 0x0c, 0x70, 0x40, 0xab, 0x02, 0x20, 0x73, 0x07, 0x8a, 0x5c, 0xe5, 0xbd, 0x53, 0xc8, 0x8e, - 0x8a, 0x42, 0xf8, 0x64, 0x75, 0x6c, 0x74, 0x36, 0x70, 0xac, 0x20, 0xf5, 0xaf, 0x76, 0x45, 0xce, - 0x6f, 0xe9, 0xc3, 0xdd, 0x95, 0x76, 0xfe, 0xd9, 0x06, 0x96, 0x65, 0xa0, 0x6e, 0xed, 0x08, 0x9c, - 0x47, 0x8b, 0xe1, 0x60, 0x5d, 0x9f, 0x7b, 0xb4, 0x95, 0x67, 0x01, 0xab, 0x1f, 0xef, 0x8e, 0xbd, - 0x09, 0xdb, 0xfb, 0xe6, 0xe2, 0x1f, 0x43, 0xb9, 0xb8, 0x34, 0xd4, 0x37, 0x97, 0x86, 0xfa, 0xf7, - 0xa5, 0xa1, 0xfe, 0x7a, 0x65, 0x28, 0x6f, 0xae, 0x0c, 0xe5, 0xaf, 0x2b, 0x43, 0xf9, 0xa1, 0x43, - 0xa8, 0xf0, 0xe2, 0xa1, 0xe9, 0x86, 0x63, 0x2b, 0xa9, 0x62, 0xd7, 0x73, 0x68, 0xe0, 0x3b, 0x43, - 0xcb, 0x77, 0xa3, 0x2f, 0x49, 0x68, 0x31, 0xec, 0x3b, 0xd3, 0xe4, 0xaf, 0x76, 0x58, 0x96, 0x7f, - 0x24, 0x8f, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x13, 0xf1, 0x6a, 0x04, 0xef, 0x0a, 0x00, 0x00, +func (m *ExplicitStateRef) Reset() { *m = ExplicitStateRef{} } +func (m *ExplicitStateRef) String() string { return proto.CompactTextString(m) } +func (*ExplicitStateRef) ProtoMessage() {} +func (*ExplicitStateRef) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{13} +} +func (m *ExplicitStateRef) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExplicitStateRef) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExplicitStateRef.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 *ExplicitStateRef) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExplicitStateRef.Merge(m, src) +} +func (m *ExplicitStateRef) XXX_Size() int { + return m.Size() +} +func (m *ExplicitStateRef) XXX_DiscardUnknown() { + xxx_messageInfo_ExplicitStateRef.DiscardUnknown(m) } -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 +var xxx_messageInfo_ExplicitStateRef proto.InternalMessageInfo -// MsgClient is the client API for Msg service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type MsgClient interface { - // CreateClient defines a rpc handler method for MsgCreateClient. - CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) - // UpdateClient defines a rpc handler method for MsgUpdateClient. - UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) - // UpdateClientStream defines a rpc handler method for MsgUpdateClient. - UpdateClientStream(ctx context.Context, opts ...grpc.CallOption) (Msg_UpdateClientStreamClient, error) - // AggregateMessages defines a rpc handler method for MsgAggregateMessages - AggregateMessages(ctx context.Context, in *MsgAggregateMessages, opts ...grpc.CallOption) (*MsgAggregateMessagesResponse, error) - // VerifyMembership defines a rpc handler method for MsgVerifyMembership - VerifyMembership(ctx context.Context, in *MsgVerifyMembership, opts ...grpc.CallOption) (*MsgVerifyMembershipResponse, error) - // VerifyNonMembership defines a rpc handler method for MsgVerifyNonMembership - VerifyNonMembership(ctx context.Context, in *MsgVerifyNonMembership, opts ...grpc.CallOption) (*MsgVerifyNonMembershipResponse, error) +type MsgSpeculativeUpdateClientBatchStreamChunk struct { + // Types that are valid to be assigned to Chunk: + // *MsgSpeculativeUpdateClientBatchStreamChunk_Init + // *MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit + // *MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk + // *MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd + // *MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd + Chunk isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk `protobuf_oneof:"chunk"` } -type msgClient struct { - cc grpc1.ClientConn +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) Reset() { + *m = MsgSpeculativeUpdateClientBatchStreamChunk{} } - -func NewMsgClient(cc grpc1.ClientConn) MsgClient { - return &msgClient{cc} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) String() string { + return proto.CompactTextString(m) } - -func (c *msgClient) CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) { - out := new(MsgCreateClientResponse) - err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/CreateClient", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +func (*MsgSpeculativeUpdateClientBatchStreamChunk) ProtoMessage() {} +func (*MsgSpeculativeUpdateClientBatchStreamChunk) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{14} } - -func (c *msgClient) UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) { - out := new(MsgUpdateClientResponse) - err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/UpdateClient", in, out, opts...) - if err != nil { - return nil, err +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSpeculativeUpdateClientBatchStreamChunk.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return out, nil +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSpeculativeUpdateClientBatchStreamChunk.Merge(m, src) +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) XXX_Size() int { + return m.Size() +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSpeculativeUpdateClientBatchStreamChunk.DiscardUnknown(m) } -func (c *msgClient) UpdateClientStream(ctx context.Context, opts ...grpc.CallOption) (Msg_UpdateClientStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Msg_serviceDesc.Streams[0], "/lcp.service.elc.v1.Msg/UpdateClientStream", opts...) - if err != nil { - return nil, err - } - x := &msgUpdateClientStreamClient{stream} - return x, nil +var xxx_messageInfo_MsgSpeculativeUpdateClientBatchStreamChunk proto.InternalMessageInfo + +type isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk interface { + isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() + MarshalTo([]byte) (int, error) + Size() int } -type Msg_UpdateClientStreamClient interface { - Send(*MsgUpdateClientStreamChunk) error - CloseAndRecv() (*MsgUpdateClientResponse, error) - grpc.ClientStream +type MsgSpeculativeUpdateClientBatchStreamChunk_Init struct { + Init *SpeculativeUpdateClientBatchStreamInit `protobuf:"bytes,1,opt,name=init,proto3,oneof" json:"init,omitempty"` +} +type MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit struct { + UnitInit *SpeculativeUpdateClientUnitInit `protobuf:"bytes,2,opt,name=unit_init,json=unitInit,proto3,oneof" json:"unit_init,omitempty"` +} +type MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk struct { + UnitHeaderChunk *SpeculativeUpdateClientUnitHeaderChunk `protobuf:"bytes,3,opt,name=unit_header_chunk,json=unitHeaderChunk,proto3,oneof" json:"unit_header_chunk,omitempty"` +} +type MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd struct { + UnitEnd *SpeculativeUpdateClientUnitEnd `protobuf:"bytes,4,opt,name=unit_end,json=unitEnd,proto3,oneof" json:"unit_end,omitempty"` +} +type MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd struct { + BatchEnd *SpeculativeUpdateClientBatchEnd `protobuf:"bytes,5,opt,name=batch_end,json=batchEnd,proto3,oneof" json:"batch_end,omitempty"` } -type msgUpdateClientStreamClient struct { - grpc.ClientStream +func (*MsgSpeculativeUpdateClientBatchStreamChunk_Init) isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() { +} +func (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit) isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() { +} +func (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk) isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() { +} +func (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd) isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() { +} +func (*MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd) isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk() { } -func (x *msgUpdateClientStreamClient) Send(m *MsgUpdateClientStreamChunk) error { - return x.ClientStream.SendMsg(m) +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetChunk() isMsgSpeculativeUpdateClientBatchStreamChunk_Chunk { + if m != nil { + return m.Chunk + } + return nil } -func (x *msgUpdateClientStreamClient) CloseAndRecv() (*MsgUpdateClientResponse, error) { - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetInit() *SpeculativeUpdateClientBatchStreamInit { + if x, ok := m.GetChunk().(*MsgSpeculativeUpdateClientBatchStreamChunk_Init); ok { + return x.Init } - m := new(MsgUpdateClientResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err + return nil +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetUnitInit() *SpeculativeUpdateClientUnitInit { + if x, ok := m.GetChunk().(*MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit); ok { + return x.UnitInit } - return m, nil + return nil } -func (c *msgClient) AggregateMessages(ctx context.Context, in *MsgAggregateMessages, opts ...grpc.CallOption) (*MsgAggregateMessagesResponse, error) { - out := new(MsgAggregateMessagesResponse) - err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/AggregateMessages", in, out, opts...) - if err != nil { - return nil, err +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetUnitHeaderChunk() *SpeculativeUpdateClientUnitHeaderChunk { + if x, ok := m.GetChunk().(*MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk); ok { + return x.UnitHeaderChunk } - return out, nil + return nil } -func (c *msgClient) VerifyMembership(ctx context.Context, in *MsgVerifyMembership, opts ...grpc.CallOption) (*MsgVerifyMembershipResponse, error) { - out := new(MsgVerifyMembershipResponse) - err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/VerifyMembership", in, out, opts...) - if err != nil { - return nil, err +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetUnitEnd() *SpeculativeUpdateClientUnitEnd { + if x, ok := m.GetChunk().(*MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd); ok { + return x.UnitEnd } - return out, nil + return nil } -func (c *msgClient) VerifyNonMembership(ctx context.Context, in *MsgVerifyNonMembership, opts ...grpc.CallOption) (*MsgVerifyNonMembershipResponse, error) { - out := new(MsgVerifyNonMembershipResponse) - err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/VerifyNonMembership", in, out, opts...) - if err != nil { - return nil, err +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) GetBatchEnd() *SpeculativeUpdateClientBatchEnd { + if x, ok := m.GetChunk().(*MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd); ok { + return x.BatchEnd } - return out, nil + return nil } -// MsgServer is the server API for Msg service. -type MsgServer interface { - // CreateClient defines a rpc handler method for MsgCreateClient. - CreateClient(context.Context, *MsgCreateClient) (*MsgCreateClientResponse, error) - // UpdateClient defines a rpc handler method for MsgUpdateClient. - UpdateClient(context.Context, *MsgUpdateClient) (*MsgUpdateClientResponse, error) - // UpdateClientStream defines a rpc handler method for MsgUpdateClient. - UpdateClientStream(Msg_UpdateClientStreamServer) error - // AggregateMessages defines a rpc handler method for MsgAggregateMessages - AggregateMessages(context.Context, *MsgAggregateMessages) (*MsgAggregateMessagesResponse, error) - // VerifyMembership defines a rpc handler method for MsgVerifyMembership - VerifyMembership(context.Context, *MsgVerifyMembership) (*MsgVerifyMembershipResponse, error) - // VerifyNonMembership defines a rpc handler method for MsgVerifyNonMembership - VerifyNonMembership(context.Context, *MsgVerifyNonMembership) (*MsgVerifyNonMembershipResponse, error) +// XXX_OneofWrappers is for the internal use of the proto package. +func (*MsgSpeculativeUpdateClientBatchStreamChunk) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*MsgSpeculativeUpdateClientBatchStreamChunk_Init)(nil), + (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit)(nil), + (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk)(nil), + (*MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd)(nil), + (*MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd)(nil), + } } -// UnimplementedMsgServer can be embedded to have forward compatible implementations. -type UnimplementedMsgServer struct { +type SpeculativeUpdateClientBatchStreamInit struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` } -func (*UnimplementedMsgServer) CreateClient(ctx context.Context, req *MsgCreateClient) (*MsgCreateClientResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) Reset() { + *m = SpeculativeUpdateClientBatchStreamInit{} } -func (*UnimplementedMsgServer) UpdateClient(ctx context.Context, req *MsgUpdateClient) (*MsgUpdateClientResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateClient not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) String() string { return proto.CompactTextString(m) } +func (*SpeculativeUpdateClientBatchStreamInit) ProtoMessage() {} +func (*SpeculativeUpdateClientBatchStreamInit) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{15} } -func (*UnimplementedMsgServer) UpdateClientStream(srv Msg_UpdateClientStreamServer) error { - return status.Errorf(codes.Unimplemented, "method UpdateClientStream not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } -func (*UnimplementedMsgServer) AggregateMessages(ctx context.Context, req *MsgAggregateMessages) (*MsgAggregateMessagesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AggregateMessages not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpeculativeUpdateClientBatchStreamInit.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 (*UnimplementedMsgServer) VerifyMembership(ctx context.Context, req *MsgVerifyMembership) (*MsgVerifyMembershipResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method VerifyMembership not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpeculativeUpdateClientBatchStreamInit.Merge(m, src) } -func (*UnimplementedMsgServer) VerifyNonMembership(ctx context.Context, req *MsgVerifyNonMembership) (*MsgVerifyNonMembershipResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method VerifyNonMembership not implemented") +func (m *SpeculativeUpdateClientBatchStreamInit) XXX_Size() int { + return m.Size() } - -func RegisterMsgServer(s grpc1.Server, srv MsgServer) { - s.RegisterService(&_Msg_serviceDesc, srv) +func (m *SpeculativeUpdateClientBatchStreamInit) XXX_DiscardUnknown() { + xxx_messageInfo_SpeculativeUpdateClientBatchStreamInit.DiscardUnknown(m) } -func _Msg_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgCreateClient) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).CreateClient(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/lcp.service.elc.v1.Msg/CreateClient", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).CreateClient(ctx, req.(*MsgCreateClient)) - } - return interceptor(ctx, in, info, handler) +var xxx_messageInfo_SpeculativeUpdateClientBatchStreamInit proto.InternalMessageInfo + +type SpeculativeUpdateClientUnitInit struct { + UnitId string `protobuf:"bytes,1,opt,name=unit_id,json=unitId,proto3" json:"unit_id,omitempty"` + TypeUrl string `protobuf:"bytes,2,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + IncludeState bool `protobuf:"varint,3,opt,name=include_state,json=includeState,proto3" json:"include_state,omitempty"` + Signer []byte `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty"` + BaseState ExplicitStateRef `protobuf:"bytes,5,opt,name=base_state,json=baseState,proto3" json:"base_state"` } -func _Msg_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgUpdateClient) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).UpdateClient(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/lcp.service.elc.v1.Msg/UpdateClient", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).UpdateClient(ctx, req.(*MsgUpdateClient)) +func (m *SpeculativeUpdateClientUnitInit) Reset() { *m = SpeculativeUpdateClientUnitInit{} } +func (m *SpeculativeUpdateClientUnitInit) String() string { return proto.CompactTextString(m) } +func (*SpeculativeUpdateClientUnitInit) ProtoMessage() {} +func (*SpeculativeUpdateClientUnitInit) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{16} +} +func (m *SpeculativeUpdateClientUnitInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SpeculativeUpdateClientUnitInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpeculativeUpdateClientUnitInit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return interceptor(ctx, in, info, handler) } - -func _Msg_UpdateClientStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(MsgServer).UpdateClientStream(&msgUpdateClientStreamServer{stream}) +func (m *SpeculativeUpdateClientUnitInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpeculativeUpdateClientUnitInit.Merge(m, src) } - -type Msg_UpdateClientStreamServer interface { - SendAndClose(*MsgUpdateClientResponse) error - Recv() (*MsgUpdateClientStreamChunk, error) - grpc.ServerStream +func (m *SpeculativeUpdateClientUnitInit) XXX_Size() int { + return m.Size() +} +func (m *SpeculativeUpdateClientUnitInit) XXX_DiscardUnknown() { + xxx_messageInfo_SpeculativeUpdateClientUnitInit.DiscardUnknown(m) } -type msgUpdateClientStreamServer struct { - grpc.ServerStream +var xxx_messageInfo_SpeculativeUpdateClientUnitInit proto.InternalMessageInfo + +type SpeculativeUpdateClientUnitHeaderChunk struct { + UnitId string `protobuf:"bytes,1,opt,name=unit_id,json=unitId,proto3" json:"unit_id,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` } -func (x *msgUpdateClientStreamServer) SendAndClose(m *MsgUpdateClientResponse) error { - return x.ServerStream.SendMsg(m) +func (m *SpeculativeUpdateClientUnitHeaderChunk) Reset() { + *m = SpeculativeUpdateClientUnitHeaderChunk{} } - -func (x *msgUpdateClientStreamServer) Recv() (*MsgUpdateClientStreamChunk, error) { - m := new(MsgUpdateClientStreamChunk) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil +func (m *SpeculativeUpdateClientUnitHeaderChunk) String() string { return proto.CompactTextString(m) } +func (*SpeculativeUpdateClientUnitHeaderChunk) ProtoMessage() {} +func (*SpeculativeUpdateClientUnitHeaderChunk) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{17} } - -func _Msg_AggregateMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgAggregateMessages) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).AggregateMessages(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/lcp.service.elc.v1.Msg/AggregateMessages", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).AggregateMessages(ctx, req.(*MsgAggregateMessages)) - } - return interceptor(ctx, in, info, handler) +func (m *SpeculativeUpdateClientUnitHeaderChunk) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } - -func _Msg_VerifyMembership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgVerifyMembership) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).VerifyMembership(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/lcp.service.elc.v1.Msg/VerifyMembership", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).VerifyMembership(ctx, req.(*MsgVerifyMembership)) +func (m *SpeculativeUpdateClientUnitHeaderChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpeculativeUpdateClientUnitHeaderChunk.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return interceptor(ctx, in, info, handler) } - -func _Msg_VerifyNonMembership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgVerifyNonMembership) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).VerifyNonMembership(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/lcp.service.elc.v1.Msg/VerifyNonMembership", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).VerifyNonMembership(ctx, req.(*MsgVerifyNonMembership)) - } - return interceptor(ctx, in, info, handler) +func (m *SpeculativeUpdateClientUnitHeaderChunk) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpeculativeUpdateClientUnitHeaderChunk.Merge(m, src) +} +func (m *SpeculativeUpdateClientUnitHeaderChunk) XXX_Size() int { + return m.Size() +} +func (m *SpeculativeUpdateClientUnitHeaderChunk) XXX_DiscardUnknown() { + xxx_messageInfo_SpeculativeUpdateClientUnitHeaderChunk.DiscardUnknown(m) } -var _Msg_serviceDesc = grpc.ServiceDesc{ - ServiceName: "lcp.service.elc.v1.Msg", - HandlerType: (*MsgServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateClient", - Handler: _Msg_CreateClient_Handler, - }, - { - MethodName: "UpdateClient", - Handler: _Msg_UpdateClient_Handler, - }, - { - MethodName: "AggregateMessages", - Handler: _Msg_AggregateMessages_Handler, - }, - { - MethodName: "VerifyMembership", - Handler: _Msg_VerifyMembership_Handler, - }, - { - MethodName: "VerifyNonMembership", - Handler: _Msg_VerifyNonMembership_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "UpdateClientStream", - Handler: _Msg_UpdateClientStream_Handler, - ClientStreams: true, - }, - }, - Metadata: "lcp/service/elc/v1/tx.proto", +var xxx_messageInfo_SpeculativeUpdateClientUnitHeaderChunk proto.InternalMessageInfo + +type SpeculativeUpdateClientUnitEnd struct { + UnitId string `protobuf:"bytes,1,opt,name=unit_id,json=unitId,proto3" json:"unit_id,omitempty"` } -func (m *MsgCreateClient) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func (m *SpeculativeUpdateClientUnitEnd) Reset() { *m = SpeculativeUpdateClientUnitEnd{} } +func (m *SpeculativeUpdateClientUnitEnd) String() string { return proto.CompactTextString(m) } +func (*SpeculativeUpdateClientUnitEnd) ProtoMessage() {} +func (*SpeculativeUpdateClientUnitEnd) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{18} +} +func (m *SpeculativeUpdateClientUnitEnd) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SpeculativeUpdateClientUnitEnd) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpeculativeUpdateClientUnitEnd.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return dAtA[:n], nil +} +func (m *SpeculativeUpdateClientUnitEnd) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpeculativeUpdateClientUnitEnd.Merge(m, src) +} +func (m *SpeculativeUpdateClientUnitEnd) XXX_Size() int { + return m.Size() +} +func (m *SpeculativeUpdateClientUnitEnd) XXX_DiscardUnknown() { + xxx_messageInfo_SpeculativeUpdateClientUnitEnd.DiscardUnknown(m) } -func (m *MsgCreateClient) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +var xxx_messageInfo_SpeculativeUpdateClientUnitEnd proto.InternalMessageInfo + +type SpeculativeUpdateClientBatchEnd struct { } -func (m *MsgCreateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0x22 - } - if m.ConsensusState != nil { - { - size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if m.ClientState != nil { - { - size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) +func (m *SpeculativeUpdateClientBatchEnd) Reset() { *m = SpeculativeUpdateClientBatchEnd{} } +func (m *SpeculativeUpdateClientBatchEnd) String() string { return proto.CompactTextString(m) } +func (*SpeculativeUpdateClientBatchEnd) ProtoMessage() {} +func (*SpeculativeUpdateClientBatchEnd) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{19} +} +func (m *SpeculativeUpdateClientBatchEnd) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SpeculativeUpdateClientBatchEnd) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpeculativeUpdateClientBatchEnd.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } - i-- - dAtA[i] = 0x12 - } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa + return b[:n], nil } - return len(dAtA) - i, nil } - -func (m *MsgCreateClientResponse) 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 *SpeculativeUpdateClientBatchEnd) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpeculativeUpdateClientBatchEnd.Merge(m, src) } - -func (m *MsgCreateClientResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +func (m *SpeculativeUpdateClientBatchEnd) XXX_Size() int { + return m.Size() +} +func (m *SpeculativeUpdateClientBatchEnd) XXX_DiscardUnknown() { + xxx_messageInfo_SpeculativeUpdateClientBatchEnd.DiscardUnknown(m) } -func (m *MsgCreateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 - } - if len(m.Message) > 0 { - i -= len(m.Message) - copy(dAtA[i:], m.Message) - i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil +var xxx_messageInfo_SpeculativeUpdateClientBatchEnd proto.InternalMessageInfo + +type ObservedStateTransition struct { + PrevHeight *types1.Height `protobuf:"bytes,1,opt,name=prev_height,json=prevHeight,proto3" json:"prev_height,omitempty"` + PrevStateId []byte `protobuf:"bytes,2,opt,name=prev_state_id,json=prevStateId,proto3" json:"prev_state_id,omitempty"` + PostHeight types1.Height `protobuf:"bytes,3,opt,name=post_height,json=postHeight,proto3" json:"post_height"` + PostStateId []byte `protobuf:"bytes,4,opt,name=post_state_id,json=postStateId,proto3" json:"post_state_id,omitempty"` } -func (m *MsgUpdateClient) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err +func (m *ObservedStateTransition) Reset() { *m = ObservedStateTransition{} } +func (m *ObservedStateTransition) String() string { return proto.CompactTextString(m) } +func (*ObservedStateTransition) ProtoMessage() {} +func (*ObservedStateTransition) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{19} +} +func (m *ObservedStateTransition) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ObservedStateTransition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ObservedStateTransition.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return dAtA[:n], nil +} +func (m *ObservedStateTransition) XXX_Merge(src proto.Message) { + xxx_messageInfo_ObservedStateTransition.Merge(m, src) +} +func (m *ObservedStateTransition) XXX_Size() int { + return m.Size() +} +func (m *ObservedStateTransition) XXX_DiscardUnknown() { + xxx_messageInfo_ObservedStateTransition.DiscardUnknown(m) } -func (m *MsgUpdateClient) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +var xxx_messageInfo_ObservedStateTransition proto.InternalMessageInfo + +type StitchedSpeculativeUpdateClientUnitResult struct { + Response MsgUpdateClientResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response"` + ObservedTransition ObservedStateTransition `protobuf:"bytes,2,opt,name=observed_transition,json=observedTransition,proto3" json:"observed_transition"` } -func (m *MsgUpdateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0x22 - } - if m.IncludeState { - i-- - if m.IncludeState { - dAtA[i] = 1 - } else { - dAtA[i] = 0 +func (m *StitchedSpeculativeUpdateClientUnitResult) Reset() { + *m = StitchedSpeculativeUpdateClientUnitResult{} +} +func (m *StitchedSpeculativeUpdateClientUnitResult) String() string { + return proto.CompactTextString(m) +} +func (*StitchedSpeculativeUpdateClientUnitResult) ProtoMessage() {} +func (*StitchedSpeculativeUpdateClientUnitResult) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{20} +} +func (m *StitchedSpeculativeUpdateClientUnitResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StitchedSpeculativeUpdateClientUnitResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StitchedSpeculativeUpdateClientUnitResult.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } - i-- - dAtA[i] = 0x18 + return b[:n], nil } - if m.Header != nil { - { - size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) +} +func (m *StitchedSpeculativeUpdateClientUnitResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_StitchedSpeculativeUpdateClientUnitResult.Merge(m, src) +} +func (m *StitchedSpeculativeUpdateClientUnitResult) XXX_Size() int { + return m.Size() +} +func (m *StitchedSpeculativeUpdateClientUnitResult) XXX_DiscardUnknown() { + xxx_messageInfo_StitchedSpeculativeUpdateClientUnitResult.DiscardUnknown(m) +} + +var xxx_messageInfo_StitchedSpeculativeUpdateClientUnitResult proto.InternalMessageInfo + +type ExecuteSpeculativeUpdateClientBatchResponse struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + Units []*StitchedSpeculativeUpdateClientUnitResult `protobuf:"bytes,2,rep,name=units,proto3" json:"units,omitempty"` +} + +func (m *ExecuteSpeculativeUpdateClientBatchResponse) Reset() { + *m = ExecuteSpeculativeUpdateClientBatchResponse{} +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) String() string { + return proto.CompactTextString(m) +} +func (*ExecuteSpeculativeUpdateClientBatchResponse) ProtoMessage() {} +func (*ExecuteSpeculativeUpdateClientBatchResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_28db3542707b11e8, []int{21} +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExecuteSpeculativeUpdateClientBatchResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } - i-- - dAtA[i] = 0x12 - } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa + return b[:n], nil } - return len(dAtA) - i, nil +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExecuteSpeculativeUpdateClientBatchResponse.Merge(m, src) +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) XXX_Size() int { + return m.Size() +} +func (m *ExecuteSpeculativeUpdateClientBatchResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ExecuteSpeculativeUpdateClientBatchResponse.DiscardUnknown(m) } -func (m *MsgUpdateClientResponse) 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 +var xxx_messageInfo_ExecuteSpeculativeUpdateClientBatchResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateClient)(nil), "lcp.service.elc.v1.MsgCreateClient") + proto.RegisterType((*MsgCreateClientResponse)(nil), "lcp.service.elc.v1.MsgCreateClientResponse") + proto.RegisterType((*MsgUpdateClient)(nil), "lcp.service.elc.v1.MsgUpdateClient") + proto.RegisterType((*MsgUpdateClientResponse)(nil), "lcp.service.elc.v1.MsgUpdateClientResponse") + proto.RegisterType((*MsgAggregateMessages)(nil), "lcp.service.elc.v1.MsgAggregateMessages") + proto.RegisterType((*MsgAggregateMessagesResponse)(nil), "lcp.service.elc.v1.MsgAggregateMessagesResponse") + proto.RegisterType((*MsgVerifyMembership)(nil), "lcp.service.elc.v1.MsgVerifyMembership") + proto.RegisterType((*MsgVerifyMembershipResponse)(nil), "lcp.service.elc.v1.MsgVerifyMembershipResponse") + proto.RegisterType((*MsgVerifyNonMembership)(nil), "lcp.service.elc.v1.MsgVerifyNonMembership") + proto.RegisterType((*MsgVerifyNonMembershipResponse)(nil), "lcp.service.elc.v1.MsgVerifyNonMembershipResponse") + proto.RegisterType((*MsgUpdateClientStreamChunk)(nil), "lcp.service.elc.v1.MsgUpdateClientStreamChunk") + proto.RegisterType((*UpdateClientStreamInit)(nil), "lcp.service.elc.v1.UpdateClientStreamInit") + proto.RegisterType((*UpdateClientStreamHeaderChunk)(nil), "lcp.service.elc.v1.UpdateClientStreamHeaderChunk") + proto.RegisterType((*ExplicitStateRef)(nil), "lcp.service.elc.v1.ExplicitStateRef") + proto.RegisterType((*MsgSpeculativeUpdateClientBatchStreamChunk)(nil), "lcp.service.elc.v1.MsgSpeculativeUpdateClientBatchStreamChunk") + proto.RegisterType((*SpeculativeUpdateClientBatchStreamInit)(nil), "lcp.service.elc.v1.SpeculativeUpdateClientBatchStreamInit") + proto.RegisterType((*SpeculativeUpdateClientUnitInit)(nil), "lcp.service.elc.v1.SpeculativeUpdateClientUnitInit") + proto.RegisterType((*SpeculativeUpdateClientUnitHeaderChunk)(nil), "lcp.service.elc.v1.SpeculativeUpdateClientUnitHeaderChunk") + proto.RegisterType((*SpeculativeUpdateClientUnitEnd)(nil), "lcp.service.elc.v1.SpeculativeUpdateClientUnitEnd") + proto.RegisterType((*SpeculativeUpdateClientBatchEnd)(nil), "lcp.service.elc.v1.SpeculativeUpdateClientBatchEnd") + proto.RegisterType((*ObservedStateTransition)(nil), "lcp.service.elc.v1.ObservedStateTransition") + proto.RegisterType((*StitchedSpeculativeUpdateClientUnitResult)(nil), "lcp.service.elc.v1.StitchedSpeculativeUpdateClientUnitResult") + proto.RegisterType((*ExecuteSpeculativeUpdateClientBatchResponse)(nil), "lcp.service.elc.v1.ExecuteSpeculativeUpdateClientBatchResponse") } -func (m *MsgUpdateClientResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +func init() { proto.RegisterFile("lcp/service/elc/v1/tx.proto", fileDescriptor_28db3542707b11e8) } + +var fileDescriptor_28db3542707b11e8 = []byte{ + // 1343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4d, 0x6f, 0xdb, 0x46, + 0x13, 0x16, 0x25, 0x59, 0x96, 0x46, 0xca, 0xd7, 0xda, 0xb0, 0x15, 0x25, 0xaf, 0x64, 0x30, 0x6f, + 0x5b, 0x35, 0x69, 0xc9, 0xda, 0x39, 0x14, 0x4d, 0x91, 0xb6, 0xb1, 0xe1, 0xc2, 0x3e, 0xa8, 0x09, + 0xe8, 0x3a, 0x40, 0xdb, 0x83, 0x42, 0x91, 0x6b, 0x6a, 0x51, 0x9a, 0x24, 0xb8, 0x4b, 0xc1, 0xba, + 0xf7, 0xd0, 0x4b, 0x81, 0x02, 0x05, 0x7a, 0x6d, 0x6e, 0xfd, 0x0d, 0xbd, 0xb4, 0xd7, 0x1c, 0x73, + 0xec, 0xc9, 0x68, 0x92, 0x4b, 0x81, 0x9e, 0x9a, 0x5f, 0x50, 0x70, 0xb9, 0xa2, 0x49, 0x7d, 0xd0, + 0x32, 0xec, 0xdc, 0x76, 0x76, 0x67, 0x9e, 0xd9, 0x7d, 0x66, 0x76, 0x66, 0x17, 0x6e, 0xd8, 0x86, + 0xa7, 0x52, 0xec, 0x0f, 0x88, 0x81, 0x55, 0x6c, 0x1b, 0xea, 0x60, 0x5d, 0x65, 0x47, 0x8a, 0xe7, + 0xbb, 0xcc, 0x45, 0xc8, 0x36, 0x3c, 0x45, 0x2c, 0x2a, 0xd8, 0x36, 0x94, 0xc1, 0x7a, 0x63, 0xd9, + 0x72, 0x2d, 0x97, 0x2f, 0xab, 0xe1, 0x28, 0xd2, 0x6c, 0x5c, 0xb7, 0x5c, 0xd7, 0xb2, 0xb1, 0xca, + 0xa5, 0x5e, 0x70, 0xa0, 0xea, 0xce, 0x50, 0x2c, 0xb5, 0x48, 0xcf, 0x50, 0x0d, 0xd7, 0xc7, 0xaa, + 0x61, 0x13, 0xec, 0xb0, 0xd0, 0x43, 0x34, 0x8a, 0x14, 0xe4, 0x9f, 0xf2, 0x70, 0xa5, 0x43, 0xad, + 0x2d, 0x1f, 0xeb, 0x0c, 0x6f, 0xf1, 0x15, 0xb4, 0x0e, 0x95, 0x48, 0xa7, 0x4b, 0xcc, 0xba, 0xb4, + 0x26, 0xb5, 0x2b, 0x9b, 0xcb, 0xaf, 0x8f, 0x5b, 0x57, 0x87, 0xfa, 0xa1, 0x7d, 0x4f, 0x8e, 0x97, + 0x64, 0xad, 0x1c, 0x8d, 0x77, 0x4d, 0xf4, 0x08, 0x6a, 0x62, 0x9e, 0x32, 0x9d, 0xe1, 0x7a, 0x7e, + 0x4d, 0x6a, 0x57, 0x37, 0x96, 0x95, 0x68, 0x67, 0xca, 0x68, 0x67, 0xca, 0x03, 0x67, 0xb8, 0xb9, + 0xfa, 0xfa, 0xb8, 0xb5, 0x94, 0xc2, 0xe2, 0x36, 0xb2, 0x56, 0x8d, 0xc4, 0xbd, 0x50, 0x42, 0x5f, + 0xc1, 0x15, 0xc3, 0x75, 0x28, 0x76, 0x68, 0x40, 0x05, 0x68, 0x21, 0x03, 0xb4, 0xf1, 0xfa, 0xb8, + 0xb5, 0x22, 0x40, 0xd3, 0x66, 0xb2, 0x76, 0x39, 0x9e, 0x89, 0xa0, 0x57, 0xa0, 0x44, 0x89, 0xe5, + 0x60, 0xbf, 0x5e, 0x5c, 0x93, 0xda, 0x35, 0x4d, 0x48, 0xf7, 0xca, 0xdf, 0x3f, 0x6d, 0xe5, 0xfe, + 0x7e, 0xda, 0xca, 0xc9, 0xdf, 0xc0, 0xea, 0x18, 0x29, 0x1a, 0xa6, 0x5e, 0x88, 0x82, 0xea, 0xb0, + 0x78, 0x88, 0x29, 0xd5, 0x2d, 0xcc, 0xa9, 0xa9, 0x69, 0x23, 0x11, 0xdd, 0x84, 0x4a, 0x08, 0xa4, + 0xb3, 0xc0, 0x8f, 0x08, 0xa8, 0x69, 0x27, 0x13, 0x09, 0xf0, 0xdf, 0x24, 0x4e, 0xf9, 0xbe, 0x67, + 0x9e, 0x8b, 0xf2, 0xf7, 0xa0, 0xd4, 0xc7, 0xba, 0x89, 0xfd, 0x2c, 0xb2, 0x35, 0xa1, 0x83, 0x6e, + 0xc1, 0x25, 0xe2, 0x18, 0x76, 0x60, 0xe2, 0x04, 0x99, 0x65, 0xad, 0x26, 0x26, 0xcf, 0x46, 0x4c, + 0x72, 0xeb, 0x17, 0x48, 0x0c, 0x83, 0xe5, 0x0e, 0xb5, 0x1e, 0x58, 0x96, 0x8f, 0x2d, 0x9d, 0xe1, + 0x4e, 0x64, 0x4e, 0x13, 0xdb, 0x92, 0x92, 0xdb, 0x42, 0x0d, 0x28, 0x0b, 0x17, 0xb4, 0x9e, 0x5f, + 0x2b, 0xb4, 0x6b, 0x5a, 0x2c, 0xa3, 0x26, 0x40, 0xec, 0x82, 0xd6, 0x0b, 0x7c, 0x35, 0x31, 0x93, + 0xf0, 0xfa, 0x04, 0x6e, 0x4e, 0xf3, 0x7a, 0x81, 0xe7, 0xfa, 0x57, 0x82, 0xa5, 0x0e, 0xb5, 0x1e, + 0x63, 0x9f, 0x1c, 0x0c, 0x3b, 0xf8, 0xb0, 0x87, 0x7d, 0xda, 0x27, 0x1e, 0xba, 0x31, 0x11, 0xf4, + 0x44, 0x78, 0x57, 0xa0, 0xe4, 0xf9, 0xf8, 0x80, 0x1c, 0x09, 0x64, 0x21, 0x21, 0x04, 0x45, 0x4f, + 0x67, 0x7d, 0x1e, 0xbf, 0x8a, 0xc6, 0xc7, 0x68, 0x19, 0x16, 0x06, 0xba, 0x1d, 0x60, 0x11, 0xb6, + 0x48, 0x40, 0x5b, 0x50, 0xf3, 0x7c, 0xd7, 0x3d, 0xe8, 0xf6, 0x31, 0xb1, 0xfa, 0xac, 0xbe, 0xc0, + 0xd3, 0xa4, 0xa1, 0x90, 0x9e, 0xa1, 0x84, 0x25, 0x41, 0x11, 0x85, 0x60, 0xb0, 0xae, 0xec, 0x70, + 0x8d, 0xcd, 0xe2, 0xb3, 0xe3, 0x56, 0x4e, 0xab, 0x72, 0xab, 0x68, 0x2a, 0x84, 0xe6, 0x62, 0xbd, + 0x14, 0x41, 0x73, 0x21, 0x11, 0x91, 0xc5, 0x19, 0x89, 0xd2, 0x85, 0x1b, 0x53, 0x8e, 0x7c, 0x81, + 0xa4, 0x1e, 0x4b, 0xb0, 0x12, 0x7b, 0xf8, 0xc2, 0x75, 0xde, 0x04, 0xaf, 0xe3, 0x0c, 0x16, 0xcf, + 0xc5, 0xe0, 0xc2, 0x74, 0x06, 0x4b, 0x33, 0x18, 0xec, 0x41, 0x73, 0xfa, 0xf9, 0x2e, 0x90, 0xc4, + 0x3f, 0x24, 0x68, 0x8c, 0xdd, 0xe7, 0x3d, 0xe6, 0x63, 0xfd, 0x70, 0xab, 0x1f, 0x38, 0xdf, 0xa2, + 0xcf, 0xa0, 0x48, 0x1c, 0xc2, 0x38, 0x7a, 0x75, 0xe3, 0xb6, 0x32, 0xd9, 0x91, 0x94, 0x49, 0xd3, + 0x5d, 0x87, 0xb0, 0x9d, 0x9c, 0xc6, 0x2d, 0xd1, 0x63, 0xa8, 0x45, 0x05, 0xa8, 0x6b, 0x84, 0x88, + 0xa2, 0x54, 0xad, 0xcf, 0x87, 0xb4, 0xc3, 0x2d, 0xf9, 0x56, 0x76, 0x72, 0x5a, 0xb5, 0x7f, 0x22, + 0x6e, 0x2e, 0xc2, 0x02, 0x07, 0x94, 0x7f, 0x90, 0x60, 0x65, 0xfa, 0x1e, 0xb2, 0xd3, 0x60, 0xa2, + 0x1e, 0xe6, 0x33, 0xeb, 0x61, 0x21, 0x55, 0x78, 0xae, 0x43, 0x99, 0x0d, 0x3d, 0xdc, 0x0d, 0x7c, + 0x9b, 0xe7, 0x44, 0x45, 0x5b, 0x0c, 0xe5, 0x7d, 0xdf, 0x96, 0xef, 0xc2, 0xff, 0x32, 0x0f, 0x12, + 0xe6, 0x99, 0xa9, 0x33, 0x5d, 0x44, 0x8c, 0x8f, 0xe5, 0x7f, 0x24, 0xb8, 0xba, 0x7d, 0xe4, 0xd9, + 0xc4, 0x20, 0x51, 0xf7, 0xd3, 0xf0, 0x01, 0xfa, 0x18, 0xaa, 0x9e, 0x8f, 0x07, 0xa3, 0xdc, 0x93, + 0x4e, 0xcb, 0x3d, 0x0d, 0x42, 0x75, 0x91, 0x74, 0x32, 0x5c, 0xe2, 0xc6, 0xfc, 0x6c, 0xe1, 0xf9, + 0xa3, 0x24, 0xe0, 0x88, 0xdc, 0xc3, 0xae, 0x89, 0x3e, 0x1c, 0xeb, 0xd9, 0x19, 0xed, 0x35, 0xdd, + 0x9a, 0xef, 0x4f, 0xb6, 0xe6, 0x62, 0x86, 0xed, 0x58, 0xfb, 0x95, 0x7f, 0x2e, 0xc0, 0xed, 0x0e, + 0xb5, 0xf6, 0x3c, 0x6c, 0x04, 0xb6, 0xce, 0xc8, 0x00, 0x27, 0x19, 0xdb, 0xd4, 0x99, 0xd1, 0x4f, + 0x26, 0xe1, 0xa3, 0x54, 0x12, 0xde, 0x9b, 0x96, 0x3a, 0xa7, 0x43, 0xa5, 0x92, 0x52, 0x83, 0x4a, + 0xe0, 0x10, 0xd6, 0xe5, 0xb0, 0x51, 0x46, 0xde, 0x3d, 0x03, 0xec, 0xbe, 0x43, 0x98, 0xc0, 0x2b, + 0x07, 0x62, 0x8c, 0xfa, 0x70, 0x8d, 0x63, 0xa6, 0xb2, 0xbd, 0x70, 0xe6, 0x2d, 0x87, 0xd8, 0xe9, + 0xb4, 0xbf, 0x12, 0xa4, 0xa7, 0xd0, 0x43, 0xe0, 0x5e, 0xbb, 0xd8, 0x31, 0x05, 0xed, 0x1b, 0x67, + 0x74, 0xb0, 0xed, 0x98, 0x3b, 0x39, 0x6d, 0x31, 0x88, 0x86, 0x27, 0x77, 0x69, 0x1b, 0xde, 0x9e, + 0x8f, 0xc9, 0xcc, 0xab, 0x25, 0x7f, 0x97, 0x87, 0xd6, 0x29, 0xd4, 0xa1, 0x55, 0x58, 0x8c, 0x42, + 0x30, 0x32, 0x2f, 0x71, 0x26, 0xcd, 0xd4, 0xd5, 0xca, 0xa7, 0xae, 0xd6, 0xb9, 0x9e, 0x30, 0x68, + 0x17, 0xa0, 0xa7, 0xd3, 0x91, 0x65, 0xd4, 0x0a, 0xff, 0x3f, 0x8d, 0xb7, 0xf1, 0x7b, 0x28, 0x4a, + 0x7a, 0x25, 0xb4, 0x8e, 0x5c, 0xbc, 0x05, 0x97, 0x4d, 0xec, 0x61, 0xc7, 0xc4, 0x8e, 0x31, 0xec, + 0x12, 0x93, 0xd6, 0x4b, 0x6b, 0x85, 0x76, 0x45, 0xbb, 0x74, 0x32, 0xbb, 0x6b, 0x52, 0x79, 0x7f, + 0x26, 0x9b, 0x63, 0x41, 0x9e, 0x4d, 0xc6, 0xa8, 0x56, 0xe4, 0x13, 0xb5, 0xe2, 0x23, 0x68, 0x66, + 0x87, 0x76, 0x26, 0x9c, 0xfc, 0x42, 0x82, 0xd5, 0x87, 0xbd, 0xf0, 0xbc, 0xd8, 0xe4, 0x47, 0xf9, + 0xd2, 0xd7, 0x1d, 0x4a, 0x18, 0x71, 0x9d, 0x37, 0x5f, 0x6d, 0x1e, 0x40, 0xd5, 0x73, 0x29, 0x1b, + 0x39, 0x28, 0xcc, 0xd9, 0x4a, 0x21, 0x34, 0x4a, 0xb8, 0x09, 0x21, 0x62, 0x37, 0x45, 0xe1, 0xc6, + 0xa5, 0x4c, 0xb8, 0x09, 0x9f, 0x05, 0xef, 0xee, 0x31, 0xc2, 0x8c, 0x3e, 0x36, 0x33, 0x78, 0xd2, + 0x30, 0x0d, 0x6c, 0x86, 0x3a, 0x50, 0xf6, 0x45, 0x37, 0x15, 0x47, 0xbe, 0x33, 0x2d, 0x27, 0x66, + 0x3c, 0x79, 0xc5, 0x16, 0x63, 0x08, 0xd4, 0x83, 0x25, 0x57, 0xf0, 0xdb, 0x65, 0x31, 0xb7, 0xa2, + 0xc4, 0x4c, 0x45, 0x9e, 0x11, 0x0e, 0x81, 0x8c, 0x46, 0x68, 0x27, 0x2b, 0xf2, 0x2f, 0x12, 0xdc, + 0xd9, 0x3e, 0xc2, 0x46, 0xc0, 0x70, 0xd6, 0x65, 0x8d, 0x1f, 0x09, 0x99, 0x5d, 0x70, 0x0f, 0x16, + 0xc2, 0xdc, 0x88, 0x9e, 0xcf, 0xd5, 0x8d, 0xfb, 0x53, 0x0b, 0xc9, 0xbc, 0x6c, 0x6a, 0x11, 0xd6, + 0xc6, 0xef, 0x25, 0x28, 0x74, 0xa8, 0x85, 0x9e, 0x40, 0x2d, 0xf5, 0xad, 0xbc, 0x35, 0x83, 0xda, + 0xa4, 0x52, 0xe3, 0xce, 0x1c, 0x4a, 0xf1, 0xd9, 0x9e, 0x40, 0x2d, 0xf5, 0x8b, 0xba, 0x35, 0x47, + 0xf0, 0x1a, 0x67, 0x89, 0x30, 0xa2, 0x80, 0x26, 0xdb, 0x39, 0x52, 0xe6, 0x80, 0x48, 0xb4, 0xb0, + 0x33, 0xb9, 0x6c, 0x4b, 0xe8, 0x57, 0x09, 0xe4, 0xd3, 0x0b, 0x31, 0xfa, 0x64, 0x06, 0xea, 0x9c, + 0x8d, 0xb5, 0xf1, 0xe9, 0xf4, 0xf2, 0x37, 0x77, 0x6a, 0xb5, 0x25, 0xe4, 0xc2, 0xb5, 0xc9, 0xef, + 0x5a, 0x7b, 0xc6, 0xbe, 0x26, 0x34, 0x1b, 0x1f, 0xcc, 0xab, 0x19, 0xc7, 0xc3, 0x86, 0xab, 0x13, + 0xdf, 0xa8, 0x77, 0x66, 0xa0, 0x8c, 0x2b, 0x36, 0xd4, 0x39, 0x15, 0x63, 0x6f, 0x01, 0x2c, 0x4d, + 0xfb, 0x5f, 0xdc, 0xce, 0xc4, 0x49, 0xe9, 0x36, 0x36, 0xe6, 0xd7, 0x8d, 0xcb, 0xca, 0xe7, 0xcf, + 0x5e, 0x34, 0x73, 0xcf, 0x5e, 0x36, 0xa5, 0xe7, 0x2f, 0x9b, 0xd2, 0x5f, 0x2f, 0x9b, 0xd2, 0x8f, + 0xaf, 0x9a, 0xb9, 0xe7, 0xaf, 0x9a, 0xb9, 0x3f, 0x5f, 0x35, 0x73, 0x5f, 0xb7, 0x2d, 0xc2, 0xfa, + 0x41, 0x4f, 0x31, 0xdc, 0x43, 0x35, 0xec, 0x08, 0x46, 0x5f, 0x27, 0x8e, 0xad, 0xf7, 0x54, 0xdb, + 0xf0, 0xde, 0xb7, 0x5c, 0xd5, 0xc7, 0xb6, 0x3e, 0x54, 0xb1, 0x6d, 0xf4, 0x4a, 0xfc, 0x19, 0x76, + 0xf7, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x79, 0x64, 0xab, 0x79, 0x67, 0x12, 0x00, 0x00, } -func (m *MsgUpdateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 - } - if len(m.Message) > 0 { - i -= len(m.Message) - copy(dAtA[i:], m.Message) - i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // CreateClient defines a rpc handler method for MsgCreateClient. + CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) + // UpdateClient defines a rpc handler method for MsgUpdateClient. + UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) + // UpdateClientStream defines a rpc handler method for MsgUpdateClient. + UpdateClientStream(ctx context.Context, opts ...grpc.CallOption) (Msg_UpdateClientStreamClient, error) + // SpeculativeUpdateClientBatchStream defines a client-streaming rpc + // handler method for explicit-state speculative UpdateClient batch execution. + SpeculativeUpdateClientBatchStream(ctx context.Context, opts ...grpc.CallOption) (Msg_SpeculativeUpdateClientBatchStreamClient, error) + // AggregateMessages defines a rpc handler method for MsgAggregateMessages + AggregateMessages(ctx context.Context, in *MsgAggregateMessages, opts ...grpc.CallOption) (*MsgAggregateMessagesResponse, error) + // VerifyMembership defines a rpc handler method for MsgVerifyMembership + VerifyMembership(ctx context.Context, in *MsgVerifyMembership, opts ...grpc.CallOption) (*MsgVerifyMembershipResponse, error) + // VerifyNonMembership defines a rpc handler method for MsgVerifyNonMembership + VerifyNonMembership(ctx context.Context, in *MsgVerifyNonMembership, opts ...grpc.CallOption) (*MsgVerifyNonMembershipResponse, error) } -func (m *MsgAggregateMessages) 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 +type msgClient struct { + cc grpc1.ClientConn } -func (m *MsgAggregateMessages) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} } -func (m *MsgAggregateMessages) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signatures) > 0 { - for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Signatures[iNdEx]) - copy(dAtA[i:], m.Signatures[iNdEx]) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signatures[iNdEx]))) - i-- - dAtA[i] = 0x1a - } - } - if len(m.Messages) > 0 { - for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Messages[iNdEx]) - copy(dAtA[i:], m.Messages[iNdEx]) - i = encodeVarintTx(dAtA, i, uint64(len(m.Messages[iNdEx]))) - i-- - dAtA[i] = 0x12 - } +func (c *msgClient) CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) { + out := new(MsgCreateClientResponse) + err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/CreateClient", in, out, opts...) + if err != nil { + return nil, err } - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0xa + return out, nil +} + +func (c *msgClient) UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) { + out := new(MsgUpdateClientResponse) + err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/UpdateClient", in, out, opts...) + if err != nil { + return nil, err } - return len(dAtA) - i, nil + return out, nil } -func (m *MsgAggregateMessagesResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) +func (c *msgClient) UpdateClientStream(ctx context.Context, opts ...grpc.CallOption) (Msg_UpdateClientStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &_Msg_serviceDesc.Streams[0], "/lcp.service.elc.v1.Msg/UpdateClientStream", opts...) if err != nil { return nil, err } - return dAtA[:n], nil + x := &msgUpdateClientStreamClient{stream} + return x, nil } -func (m *MsgAggregateMessagesResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +type Msg_UpdateClientStreamClient interface { + Send(*MsgUpdateClientStreamChunk) error + CloseAndRecv() (*MsgUpdateClientResponse, error) + grpc.ClientStream } -func (m *MsgAggregateMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 +type msgUpdateClientStreamClient struct { + grpc.ClientStream +} + +func (x *msgUpdateClientStreamClient) Send(m *MsgUpdateClientStreamChunk) error { + return x.ClientStream.SendMsg(m) +} + +func (x *msgUpdateClientStreamClient) CloseAndRecv() (*MsgUpdateClientResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err } - if len(m.Message) > 0 { - i -= len(m.Message) - copy(dAtA[i:], m.Message) - i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) - i-- - dAtA[i] = 0xa + m := new(MsgUpdateClientResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err } - return len(dAtA) - i, nil + return m, nil } -func (m *MsgVerifyMembership) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) +func (c *msgClient) SpeculativeUpdateClientBatchStream(ctx context.Context, opts ...grpc.CallOption) (Msg_SpeculativeUpdateClientBatchStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &_Msg_serviceDesc.Streams[1], "/lcp.service.elc.v1.Msg/SpeculativeUpdateClientBatchStream", opts...) if err != nil { return nil, err } - return dAtA[:n], nil + x := &msgSpeculativeUpdateClientBatchStreamClient{stream} + return x, nil } -func (m *MsgVerifyMembership) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +type Msg_SpeculativeUpdateClientBatchStreamClient interface { + Send(*MsgSpeculativeUpdateClientBatchStreamChunk) error + CloseAndRecv() (*ExecuteSpeculativeUpdateClientBatchResponse, error) + grpc.ClientStream } -func (m *MsgVerifyMembership) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0x3a - } - if len(m.Proof) > 0 { - i -= len(m.Proof) - copy(dAtA[i:], m.Proof) - i = encodeVarintTx(dAtA, i, uint64(len(m.Proof))) - i-- - dAtA[i] = 0x32 - } - { - size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - if len(m.Value) > 0 { - i -= len(m.Value) - copy(dAtA[i:], m.Value) - i = encodeVarintTx(dAtA, i, uint64(len(m.Value))) - i-- - dAtA[i] = 0x22 - } - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintTx(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0x1a - } - if len(m.Prefix) > 0 { - i -= len(m.Prefix) - copy(dAtA[i:], m.Prefix) - i = encodeVarintTx(dAtA, i, uint64(len(m.Prefix))) - i-- - dAtA[i] = 0x12 +type msgSpeculativeUpdateClientBatchStreamClient struct { + grpc.ClientStream +} + +func (x *msgSpeculativeUpdateClientBatchStreamClient) Send(m *MsgSpeculativeUpdateClientBatchStreamChunk) error { + return x.ClientStream.SendMsg(m) +} + +func (x *msgSpeculativeUpdateClientBatchStreamClient) CloseAndRecv() (*ExecuteSpeculativeUpdateClientBatchResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa + m := new(ExecuteSpeculativeUpdateClientBatchResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err } - return len(dAtA) - i, nil + return m, nil } -func (m *MsgVerifyMembershipResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) +func (c *msgClient) AggregateMessages(ctx context.Context, in *MsgAggregateMessages, opts ...grpc.CallOption) (*MsgAggregateMessagesResponse, error) { + out := new(MsgAggregateMessagesResponse) + err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/AggregateMessages", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *MsgVerifyMembershipResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) + return out, nil } -func (m *MsgVerifyMembershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 - } - if len(m.Message) > 0 { - i -= len(m.Message) - copy(dAtA[i:], m.Message) - i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) - i-- - dAtA[i] = 0xa +func (c *msgClient) VerifyMembership(ctx context.Context, in *MsgVerifyMembership, opts ...grpc.CallOption) (*MsgVerifyMembershipResponse, error) { + out := new(MsgVerifyMembershipResponse) + err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/VerifyMembership", in, out, opts...) + if err != nil { + return nil, err } - return len(dAtA) - i, nil + return out, nil } -func (m *MsgVerifyNonMembership) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) +func (c *msgClient) VerifyNonMembership(ctx context.Context, in *MsgVerifyNonMembership, opts ...grpc.CallOption) (*MsgVerifyNonMembershipResponse, error) { + out := new(MsgVerifyNonMembershipResponse) + err := c.cc.Invoke(ctx, "/lcp.service.elc.v1.Msg/VerifyNonMembership", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *MsgVerifyNonMembership) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateClient defines a rpc handler method for MsgCreateClient. + CreateClient(context.Context, *MsgCreateClient) (*MsgCreateClientResponse, error) + // UpdateClient defines a rpc handler method for MsgUpdateClient. + UpdateClient(context.Context, *MsgUpdateClient) (*MsgUpdateClientResponse, error) + // UpdateClientStream defines a rpc handler method for MsgUpdateClient. + UpdateClientStream(Msg_UpdateClientStreamServer) error + // SpeculativeUpdateClientBatchStream defines a client-streaming rpc + // handler method for explicit-state speculative UpdateClient batch execution. + SpeculativeUpdateClientBatchStream(Msg_SpeculativeUpdateClientBatchStreamServer) error + // AggregateMessages defines a rpc handler method for MsgAggregateMessages + AggregateMessages(context.Context, *MsgAggregateMessages) (*MsgAggregateMessagesResponse, error) + // VerifyMembership defines a rpc handler method for MsgVerifyMembership + VerifyMembership(context.Context, *MsgVerifyMembership) (*MsgVerifyMembershipResponse, error) + // VerifyNonMembership defines a rpc handler method for MsgVerifyNonMembership + VerifyNonMembership(context.Context, *MsgVerifyNonMembership) (*MsgVerifyNonMembershipResponse, error) } -func (m *MsgVerifyNonMembership) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0x32 - } - if len(m.Proof) > 0 { - i -= len(m.Proof) - copy(dAtA[i:], m.Proof) - i = encodeVarintTx(dAtA, i, uint64(len(m.Proof))) - i-- - dAtA[i] = 0x2a - } - { - size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - if len(m.Path) > 0 { - i -= len(m.Path) - copy(dAtA[i:], m.Path) - i = encodeVarintTx(dAtA, i, uint64(len(m.Path))) - i-- - dAtA[i] = 0x1a - } - if len(m.Prefix) > 0 { - i -= len(m.Prefix) - copy(dAtA[i:], m.Prefix) - i = encodeVarintTx(dAtA, i, uint64(len(m.Prefix))) - i-- - dAtA[i] = 0x12 - } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { } -func (m *MsgVerifyNonMembershipResponse) 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 (*UnimplementedMsgServer) CreateClient(ctx context.Context, req *MsgCreateClient) (*MsgCreateClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented") +} +func (*UnimplementedMsgServer) UpdateClient(ctx context.Context, req *MsgUpdateClient) (*MsgUpdateClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateClient not implemented") +} +func (*UnimplementedMsgServer) UpdateClientStream(srv Msg_UpdateClientStreamServer) error { + return status.Errorf(codes.Unimplemented, "method UpdateClientStream not implemented") +} +func (*UnimplementedMsgServer) SpeculativeUpdateClientBatchStream(srv Msg_SpeculativeUpdateClientBatchStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented") +} +func (*UnimplementedMsgServer) AggregateMessages(ctx context.Context, req *MsgAggregateMessages) (*MsgAggregateMessagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AggregateMessages not implemented") +} +func (*UnimplementedMsgServer) VerifyMembership(ctx context.Context, req *MsgVerifyMembership) (*MsgVerifyMembershipResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VerifyMembership not implemented") +} +func (*UnimplementedMsgServer) VerifyNonMembership(ctx context.Context, req *MsgVerifyNonMembership) (*MsgVerifyNonMembershipResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VerifyNonMembership not implemented") } -func (m *MsgVerifyNonMembershipResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) } -func (m *MsgVerifyNonMembershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 +func _Msg_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateClient) + if err := dec(in); err != nil { + return nil, err } - if len(m.Message) > 0 { - i -= len(m.Message) - copy(dAtA[i:], m.Message) - i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) - i-- - dAtA[i] = 0xa + if interceptor == nil { + return srv.(MsgServer).CreateClient(ctx, in) } - return len(dAtA) - i, nil + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lcp.service.elc.v1.Msg/CreateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateClient(ctx, req.(*MsgCreateClient)) + } + return interceptor(ctx, in, info, handler) } -func (m *MsgUpdateClientStreamChunk) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { +func _Msg_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateClient) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *MsgUpdateClientStreamChunk) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) + if interceptor == nil { + return srv.(MsgServer).UpdateClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lcp.service.elc.v1.Msg/UpdateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateClient(ctx, req.(*MsgUpdateClient)) + } + return interceptor(ctx, in, info, handler) } -func (m *MsgUpdateClientStreamChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Chunk != nil { +func _Msg_UpdateClientStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(MsgServer).UpdateClientStream(&msgUpdateClientStreamServer{stream}) +} + +type Msg_UpdateClientStreamServer interface { + SendAndClose(*MsgUpdateClientResponse) error + Recv() (*MsgUpdateClientStreamChunk, error) + grpc.ServerStream +} + +type msgUpdateClientStreamServer struct { + grpc.ServerStream +} + +func (x *msgUpdateClientStreamServer) SendAndClose(m *MsgUpdateClientResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *msgUpdateClientStreamServer) Recv() (*MsgUpdateClientStreamChunk, error) { + m := new(MsgUpdateClientStreamChunk) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Msg_SpeculativeUpdateClientBatchStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(MsgServer).SpeculativeUpdateClientBatchStream(&msgSpeculativeUpdateClientBatchStreamServer{stream}) +} + +type Msg_SpeculativeUpdateClientBatchStreamServer interface { + SendAndClose(*ExecuteSpeculativeUpdateClientBatchResponse) error + Recv() (*MsgSpeculativeUpdateClientBatchStreamChunk, error) + grpc.ServerStream +} + +type msgSpeculativeUpdateClientBatchStreamServer struct { + grpc.ServerStream +} + +func (x *msgSpeculativeUpdateClientBatchStreamServer) SendAndClose(m *ExecuteSpeculativeUpdateClientBatchResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *msgSpeculativeUpdateClientBatchStreamServer) Recv() (*MsgSpeculativeUpdateClientBatchStreamChunk, error) { + m := new(MsgSpeculativeUpdateClientBatchStreamChunk) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Msg_AggregateMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAggregateMessages) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AggregateMessages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lcp.service.elc.v1.Msg/AggregateMessages", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AggregateMessages(ctx, req.(*MsgAggregateMessages)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_VerifyMembership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVerifyMembership) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VerifyMembership(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lcp.service.elc.v1.Msg/VerifyMembership", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VerifyMembership(ctx, req.(*MsgVerifyMembership)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_VerifyNonMembership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVerifyNonMembership) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VerifyNonMembership(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lcp.service.elc.v1.Msg/VerifyNonMembership", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VerifyNonMembership(ctx, req.(*MsgVerifyNonMembership)) + } + return interceptor(ctx, in, info, handler) +} + +var Msg_serviceDesc = _Msg_serviceDesc +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "lcp.service.elc.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ { - size := m.Chunk.Size() - i -= size - if _, err := m.Chunk.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - } + MethodName: "CreateClient", + Handler: _Msg_CreateClient_Handler, + }, + { + MethodName: "UpdateClient", + Handler: _Msg_UpdateClient_Handler, + }, + { + MethodName: "AggregateMessages", + Handler: _Msg_AggregateMessages_Handler, + }, + { + MethodName: "VerifyMembership", + Handler: _Msg_VerifyMembership_Handler, + }, + { + MethodName: "VerifyNonMembership", + Handler: _Msg_VerifyNonMembership_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "UpdateClientStream", + Handler: _Msg_UpdateClientStream_Handler, + ClientStreams: true, + }, + { + StreamName: "SpeculativeUpdateClientBatchStream", + Handler: _Msg_SpeculativeUpdateClientBatchStream_Handler, + ClientStreams: true, + }, + }, + Metadata: "lcp/service/elc/v1/tx.proto", +} + +func (m *MsgCreateClient) 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 len(dAtA) - i, nil + return dAtA[:n], nil } -func (m *MsgUpdateClientStreamChunk_Init) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgCreateClient) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdateClientStreamChunk_Init) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgCreateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.Init != nil { + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + if m.ConsensusState != nil { { - size, err := m.Init.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1531,20 +1626,11 @@ func (m *MsgUpdateClientStreamChunk_Init) MarshalToSizedBuffer(dAtA []byte) (int i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0xa + dAtA[i] = 0x1a } - return len(dAtA) - i, nil -} -func (m *MsgUpdateClientStreamChunk_HeaderChunk) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgUpdateClientStreamChunk_HeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - if m.HeaderChunk != nil { + if m.ClientState != nil { { - size, err := m.HeaderChunk.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1554,9 +1640,17 @@ func (m *MsgUpdateClientStreamChunk_HeaderChunk) MarshalToSizedBuffer(dAtA []byt i-- dAtA[i] = 0x12 } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } -func (m *UpdateClientStreamInit) Marshal() (dAtA []byte, err error) { + +func (m *MsgCreateClientResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1566,51 +1660,34 @@ func (m *UpdateClientStreamInit) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *UpdateClientStreamInit) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgCreateClientResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *UpdateClientStreamInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgCreateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.TypeUrl) > 0 { - i -= len(m.TypeUrl) - copy(dAtA[i:], m.TypeUrl) - i = encodeVarintTx(dAtA, i, uint64(len(m.TypeUrl))) - i-- - dAtA[i] = 0x22 - } - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) - i-- - dAtA[i] = 0x1a - } - if m.IncludeState { - i-- - if m.IncludeState { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x12 } - if len(m.ClientId) > 0 { - i -= len(m.ClientId) - copy(dAtA[i:], m.ClientId) - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *UpdateClientStreamHeaderChunk) Marshal() (dAtA []byte, err error) { +func (m *MsgUpdateClient) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1620,342 +1697,2888 @@ func (m *UpdateClientStreamHeaderChunk) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *UpdateClientStreamHeaderChunk) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgUpdateClient) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *UpdateClientStreamHeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgUpdateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + if m.IncludeState { + i-- + if m.IncludeState { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Header != nil { + { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func encodeVarintTx(dAtA []byte, offset int, v uint64) int { - offset -= sovTx(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *MsgUpdateClientResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return base + return dAtA[:n], nil } -func (m *MsgCreateClient) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.ClientState != nil { - l = m.ClientState.Size() - n += 1 + l + sovTx(uint64(l)) - } - if m.ConsensusState != nil { - l = m.ConsensusState.Size() - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - return n + +func (m *MsgUpdateClientResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgCreateClientResponse) Size() (n int) { - if m == nil { - return 0 - } +func (m *MsgUpdateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Message) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *MsgUpdateClient) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.Header != nil { - l = m.Header.Size() - n += 1 + l + sovTx(uint64(l)) - } - if m.IncludeState { - n += 2 - } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) +func (m *MsgAggregateMessages) 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 n + return dAtA[:n], nil } -func (m *MsgUpdateClientResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Message) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - return n +func (m *MsgAggregateMessages) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgAggregateMessages) Size() (n int) { - if m == nil { - return 0 - } +func (m *MsgAggregateMessages) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0x1a + } } if len(m.Messages) > 0 { - for _, b := range m.Messages { - l = len(b) - n += 1 + l + sovTx(uint64(l)) + for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Messages[iNdEx]) + copy(dAtA[i:], m.Messages[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Messages[iNdEx]))) + i-- + dAtA[i] = 0x12 } } - if len(m.Signatures) > 0 { - for _, b := range m.Signatures { - l = len(b) - n += 1 + l + sovTx(uint64(l)) - } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *MsgAggregateMessagesResponse) Size() (n int) { - if m == nil { - return 0 +func (m *MsgAggregateMessagesResponse) 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 *MsgAggregateMessagesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAggregateMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Message) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *MsgVerifyMembership) Size() (n int) { - if m == nil { - return 0 +func (m *MsgVerifyMembership) 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 *MsgVerifyMembership) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyMembership) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x3a } - l = len(m.Prefix) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintTx(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x32 } - l = len(m.Path) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } - l = len(m.Value) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + i-- + dAtA[i] = 0x2a + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintTx(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x22 } - l = m.ProofHeight.Size() - n += 1 + l + sovTx(uint64(l)) - l = len(m.Proof) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintTx(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x1a } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Prefix) > 0 { + i -= len(m.Prefix) + copy(dAtA[i:], m.Prefix) + i = encodeVarintTx(dAtA, i, uint64(len(m.Prefix))) + i-- + dAtA[i] = 0x12 } - return n + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *MsgVerifyMembershipResponse) Size() (n int) { - if m == nil { - return 0 +func (m *MsgVerifyMembershipResponse) 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 *MsgVerifyMembershipResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyMembershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Message) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *MsgVerifyNonMembership) Size() (n int) { - if m == nil { - return 0 +func (m *MsgVerifyNonMembership) 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 *MsgVerifyNonMembership) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyNonMembership) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x32 } - l = len(m.Prefix) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintTx(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x2a } - l = len(m.Path) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } - l = m.ProofHeight.Size() - n += 1 + l + sovTx(uint64(l)) - l = len(m.Proof) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + i-- + dAtA[i] = 0x22 + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintTx(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0x1a } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Prefix) > 0 { + i -= len(m.Prefix) + copy(dAtA[i:], m.Prefix) + i = encodeVarintTx(dAtA, i, uint64(len(m.Prefix))) + i-- + dAtA[i] = 0x12 } - return n + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *MsgVerifyNonMembershipResponse) Size() (n int) { - if m == nil { - return 0 +func (m *MsgVerifyNonMembershipResponse) 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 *MsgVerifyNonMembershipResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyNonMembershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Message) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTx(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *MsgUpdateClientStreamChunk) Size() (n int) { - if m == nil { - return 0 +func (m *MsgUpdateClientStreamChunk) 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 *MsgUpdateClientStreamChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateClientStreamChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l if m.Chunk != nil { - n += m.Chunk.Size() + { + size := m.Chunk.Size() + i -= size + if _, err := m.Chunk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } } - return n + return len(dAtA) - i, nil } -func (m *MsgUpdateClientStreamChunk_Init) Size() (n int) { - if m == nil { - return 0 +func (m *MsgUpdateClientStreamChunk_Init) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateClientStreamChunk_Init) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Init != nil { + { + size, err := m.Init.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *MsgUpdateClientStreamChunk_HeaderChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateClientStreamChunk_HeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.HeaderChunk != nil { + { + size, err := m.HeaderChunk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *UpdateClientStreamInit) 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 *UpdateClientStreamInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateClientStreamInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Init != nil { - l = m.Init.Size() - n += 1 + l + sovTx(uint64(l)) + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = encodeVarintTx(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0x22 } - return n + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.IncludeState { + i-- + if m.IncludeState { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *MsgUpdateClientStreamChunk_HeaderChunk) Size() (n int) { - if m == nil { - return 0 + +func (m *UpdateClientStreamHeaderChunk) 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 *UpdateClientStreamHeaderChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateClientStreamHeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.HeaderChunk != nil { - l = m.HeaderChunk.Size() - n += 1 + l + sovTx(uint64(l)) + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *UpdateClientStreamInit) Size() (n int) { - if m == nil { - return 0 + +func (m *ExplicitStateRef) 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 *ExplicitStateRef) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExplicitStateRef) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ClientId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } - if m.IncludeState { - n += 2 + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - l = len(m.Signer) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.PrevStateId) > 0 { + i -= len(m.PrevStateId) + copy(dAtA[i:], m.PrevStateId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PrevStateId))) + i-- + dAtA[i] = 0x12 } - l = len(m.TypeUrl) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if m.PrevHeight != nil { + { + size, err := m.PrevHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) 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 *MsgSpeculativeUpdateClientBatchStreamChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Chunk != nil { + { + size := m.Chunk.Size() + i -= size + if _, err := m.Chunk.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_Init) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_Init) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Init != nil { + { + size, err := m.Init.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UnitInit != nil { + { + size, err := m.UnitInit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UnitHeaderChunk != nil { + { + size, err := m.UnitHeaderChunk.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UnitEnd != nil { + { + size, err := m.UnitEnd.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.BatchEnd != nil { + { + size, err := m.BatchEnd.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + return len(dAtA) - i, nil +} +func (m *SpeculativeUpdateClientBatchStreamInit) 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 *SpeculativeUpdateClientBatchStreamInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpeculativeUpdateClientBatchStreamInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SpeculativeUpdateClientUnitInit) 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 *SpeculativeUpdateClientUnitInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpeculativeUpdateClientUnitInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.BaseState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + if m.IncludeState { + i-- + if m.IncludeState { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = encodeVarintTx(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0x12 + } + if len(m.UnitId) > 0 { + i -= len(m.UnitId) + copy(dAtA[i:], m.UnitId) + i = encodeVarintTx(dAtA, i, uint64(len(m.UnitId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SpeculativeUpdateClientUnitHeaderChunk) 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 *SpeculativeUpdateClientUnitHeaderChunk) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpeculativeUpdateClientUnitHeaderChunk) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + if len(m.UnitId) > 0 { + i -= len(m.UnitId) + copy(dAtA[i:], m.UnitId) + i = encodeVarintTx(dAtA, i, uint64(len(m.UnitId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SpeculativeUpdateClientUnitEnd) 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 *SpeculativeUpdateClientUnitEnd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpeculativeUpdateClientUnitEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UnitId) > 0 { + i -= len(m.UnitId) + copy(dAtA[i:], m.UnitId) + i = encodeVarintTx(dAtA, i, uint64(len(m.UnitId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SpeculativeUpdateClientBatchEnd) 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 *SpeculativeUpdateClientBatchEnd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpeculativeUpdateClientBatchEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + return 0, nil +} + +func (m *ObservedStateTransition) 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 *ObservedStateTransition) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ObservedStateTransition) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PostStateId) > 0 { + i -= len(m.PostStateId) + copy(dAtA[i:], m.PostStateId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PostStateId))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.PostHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.PrevStateId) > 0 { + i -= len(m.PrevStateId) + copy(dAtA[i:], m.PrevStateId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PrevStateId))) + i-- + dAtA[i] = 0x12 + } + if m.PrevHeight != nil { + { + size, err := m.PrevHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *StitchedSpeculativeUpdateClientUnitResult) 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 *StitchedSpeculativeUpdateClientUnitResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StitchedSpeculativeUpdateClientUnitResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ObservedTransition.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Response.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ExecuteSpeculativeUpdateClientBatchResponse) 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 *ExecuteSpeculativeUpdateClientBatchResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecuteSpeculativeUpdateClientBatchResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Units) > 0 { + for iNdEx := len(m.Units) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Units[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgCreateClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Header != nil { + l = m.Header.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.IncludeState { + n += 2 + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgAggregateMessages) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Messages) > 0 { + for _, b := range m.Messages { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgAggregateMessagesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVerifyMembership) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Prefix) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVerifyMembershipResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVerifyNonMembership) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Prefix) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVerifyNonMembershipResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateClientStreamChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Chunk != nil { + n += m.Chunk.Size() + } + return n +} + +func (m *MsgUpdateClientStreamChunk_Init) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Init != nil { + l = m.Init.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *MsgUpdateClientStreamChunk_HeaderChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HeaderChunk != nil { + l = m.HeaderChunk.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *UpdateClientStreamInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.IncludeState { + n += 2 + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *UpdateClientStreamHeaderChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *ExplicitStateRef) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PrevHeight != nil { + l = m.PrevHeight.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.PrevStateId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Chunk != nil { + n += m.Chunk.Size() + } + return n +} + +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_Init) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Init != nil { + l = m.Init.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UnitInit != nil { + l = m.UnitInit.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UnitHeaderChunk != nil { + l = m.UnitHeaderChunk.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UnitEnd != nil { + l = m.UnitEnd.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BatchEnd != nil { + l = m.BatchEnd.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *SpeculativeUpdateClientBatchStreamInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *SpeculativeUpdateClientUnitInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UnitId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.IncludeState { + n += 2 + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.BaseState.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *SpeculativeUpdateClientUnitHeaderChunk) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UnitId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *SpeculativeUpdateClientUnitEnd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UnitId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *SpeculativeUpdateClientBatchEnd) Size() (n int) { + if m == nil { + return 0 + } + return 0 +} + +func (m *ObservedStateTransition) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PrevHeight != nil { + l = m.PrevHeight.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.PrevStateId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.PostHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.PostStateId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *StitchedSpeculativeUpdateClientUnitResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Response.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.ObservedTransition.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *ExecuteSpeculativeUpdateClientBatchResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Units) > 0 { + for _, e := range m.Units { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgCreateClient) 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 ErrIntOverflowTx + } + 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: MsgCreateClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateClientResponse) 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 ErrIntOverflowTx + } + 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: MsgCreateClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateClientResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateClient) 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 ErrIntOverflowTx + } + 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: MsgUpdateClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = &types.Any{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IncludeState", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IncludeState = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateClientResponse) 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 ErrIntOverflowTx + } + 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: MsgUpdateClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateClientResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAggregateMessages) 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 ErrIntOverflowTx + } + 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: MsgAggregateMessages: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAggregateMessages: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Messages = append(m.Messages, make([]byte, postIndex-iNdEx)) + copy(m.Messages[len(m.Messages)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAggregateMessagesResponse) 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 ErrIntOverflowTx + } + 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: MsgAggregateMessagesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAggregateMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVerifyMembership) 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 ErrIntOverflowTx + } + 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: MsgVerifyMembership: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVerifyMembership: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prefix", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Prefix = append(m.Prefix[:0], dAtA[iNdEx:postIndex]...) + if m.Prefix == nil { + m.Prefix = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + case 5: + 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + 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 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVerifyMembershipResponse) 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 ErrIntOverflowTx + } + 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: MsgVerifyMembershipResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVerifyMembershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } - return n -} -func (m *UpdateClientStreamHeaderChunk) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Data) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if iNdEx > l { + return io.ErrUnexpectedEOF } - return n -} - -func sovTx(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozTx(x uint64) (n int) { - return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return nil } -func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { +func (m *MsgVerifyNonMembership) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1978,10 +4601,10 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgCreateClient: wiretype end group for non-group") + return fmt.Errorf("proto: MsgVerifyNonMembership: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateClient: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgVerifyNonMembership: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2018,9 +4641,9 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Prefix", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2030,31 +4653,61 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if m.ClientState == nil { - m.ClientState = &types.Any{} - } - if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.Prefix = append(m.Prefix[:0], dAtA[iNdEx:postIndex]...) + if m.Prefix == nil { + m.Prefix = []byte{} } iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2081,16 +4734,165 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ConsensusState == nil { - m.ConsensusState = &types.Any{} + if err := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + iNdEx = postIndex + case 5: + 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + 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 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { return err } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVerifyNonMembershipResponse) 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 ErrIntOverflowTx + } + 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: MsgVerifyNonMembershipResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVerifyNonMembershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } iNdEx = postIndex - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -2117,9 +4919,9 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} } iNdEx = postIndex default: @@ -2143,7 +4945,7 @@ func (m *MsgCreateClient) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { +func (m *MsgUpdateClientStreamChunk) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2166,17 +4968,17 @@ func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgCreateClientResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgUpdateClientStreamChunk: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateClientResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgUpdateClientStreamChunk: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Init", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2186,31 +4988,32 @@ func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) - if m.Message == nil { - m.Message = []byte{} + v := &UpdateClientStreamInit{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + m.Chunk = &MsgUpdateClientStreamChunk_Init{v} iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field HeaderChunk", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2220,25 +5023,26 @@ func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + v := &UpdateClientStreamHeaderChunk{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + m.Chunk = &MsgUpdateClientStreamChunk_HeaderChunk{v} iNdEx = postIndex default: iNdEx = preIndex @@ -2261,7 +5065,7 @@ func (m *MsgCreateClientResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { +func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2284,10 +5088,10 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgUpdateClient: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateClientStreamInit: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUpdateClient: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateClientStreamInit: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2323,10 +5127,30 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { m.ClientId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IncludeState", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IncludeState = bool(v != 0) + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2336,33 +5160,31 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Header == nil { - m.Header = &types.Any{} - } - if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} } iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IncludeState", wireType) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2372,15 +5194,77 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.IncludeState = bool(v != 0) - case 4: + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateClientStreamHeaderChunk) 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 ErrIntOverflowTx + } + 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: UpdateClientStreamHeaderChunk: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateClientStreamHeaderChunk: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -2407,9 +5291,9 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} } iNdEx = postIndex default: @@ -2433,7 +5317,7 @@ func (m *MsgUpdateClient) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { +func (m *ExplicitStateRef) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2456,15 +5340,51 @@ func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgUpdateClientResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ExplicitStateRef: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUpdateClientResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ExplicitStateRef: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PrevHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PrevHeight == nil { + m.PrevHeight = &types1.Height{} + } + if err := m.PrevHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PrevStateId", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -2491,16 +5411,52 @@ func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) - if m.Message == nil { - m.Message = []byte{} + m.PrevStateId = append(m.PrevStateId[:0], dAtA[iNdEx:postIndex]...) + if m.PrevStateId == nil { + m.PrevStateId = []byte{} } iNdEx = postIndex - case 2: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2510,24 +5466,26 @@ func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + if m.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex default: @@ -2551,7 +5509,7 @@ func (m *MsgUpdateClientResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgAggregateMessages) Unmarshal(dAtA []byte) error { +func (m *MsgSpeculativeUpdateClientBatchStreamChunk) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2574,17 +5532,17 @@ func (m *MsgAggregateMessages) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgAggregateMessages: wiretype end group for non-group") + return fmt.Errorf("proto: MsgSpeculativeUpdateClientBatchStreamChunk: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAggregateMessages: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgSpeculativeUpdateClientBatchStreamChunk: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Init", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2594,31 +5552,32 @@ func (m *MsgAggregateMessages) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} + v := &SpeculativeUpdateClientBatchStreamInit{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + m.Chunk = &MsgSpeculativeUpdateClientBatchStreamChunk_Init{v} iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitInit", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2628,29 +5587,32 @@ func (m *MsgAggregateMessages) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Messages = append(m.Messages, make([]byte, postIndex-iNdEx)) - copy(m.Messages[len(m.Messages)-1], dAtA[iNdEx:postIndex]) + v := &SpeculativeUpdateClientUnitInit{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Chunk = &MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit{v} iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitHeaderChunk", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2660,79 +5622,32 @@ func (m *MsgAggregateMessages) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) - copy(m.Signatures[len(m.Signatures)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { + v := &SpeculativeUpdateClientUnitHeaderChunk{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgAggregateMessagesResponse) 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 ErrIntOverflowTx - } - 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: MsgAggregateMessagesResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAggregateMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + m.Chunk = &MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk{v} + iNdEx = postIndex + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitEnd", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2742,31 +5657,32 @@ func (m *MsgAggregateMessagesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) - if m.Message == nil { - m.Message = []byte{} + v := &SpeculativeUpdateClientUnitEnd{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + m.Chunk = &MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd{v} iNdEx = postIndex - case 2: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BatchEnd", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2776,25 +5692,26 @@ func (m *MsgAggregateMessagesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + v := &SpeculativeUpdateClientBatchEnd{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + m.Chunk = &MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd{v} iNdEx = postIndex default: iNdEx = preIndex @@ -2817,7 +5734,7 @@ func (m *MsgAggregateMessagesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { +func (m *SpeculativeUpdateClientBatchStreamInit) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2840,10 +5757,10 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgVerifyMembership: wiretype end group for non-group") + return fmt.Errorf("proto: SpeculativeUpdateClientBatchStreamInit: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgVerifyMembership: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SpeculativeUpdateClientBatchStreamInit: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2878,43 +5795,59 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { } m.ClientId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Prefix", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err } - if byteLen < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTx + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF } - if postIndex > l { + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SpeculativeUpdateClientUnitInit) 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 ErrIntOverflowTx + } + if iNdEx >= l { return io.ErrUnexpectedEOF } - m.Prefix = append(m.Prefix[:0], dAtA[iNdEx:postIndex]...) - if m.Prefix == nil { - m.Prefix = []byte{} + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break } - iNdEx = postIndex - case 3: + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SpeculativeUpdateClientUnitInit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SpeculativeUpdateClientUnitInit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2942,47 +5875,13 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Path = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) - if m.Value == nil { - m.Value = []byte{} - } + m.UnitId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2992,28 +5891,47 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IncludeState", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IncludeState = bool(v != 0) + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3040,16 +5958,16 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) - if m.Proof == nil { - m.Proof = []byte{} + m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) + if m.Signer == nil { + m.Signer = []byte{} } iNdEx = postIndex - case 7: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BaseState", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3059,24 +5977,23 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} + if err := m.BaseState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex default: @@ -3100,7 +6017,7 @@ func (m *MsgVerifyMembership) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgVerifyMembershipResponse) Unmarshal(dAtA []byte) error { +func (m *SpeculativeUpdateClientUnitHeaderChunk) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3123,17 +6040,17 @@ func (m *MsgVerifyMembershipResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgVerifyMembershipResponse: wiretype end group for non-group") + return fmt.Errorf("proto: SpeculativeUpdateClientUnitHeaderChunk: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgVerifyMembershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SpeculativeUpdateClientUnitHeaderChunk: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitId", wireType) } - var byteLen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3143,29 +6060,27 @@ func (m *MsgVerifyMembershipResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) - if m.Message == nil { - m.Message = []byte{} - } + m.UnitId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3192,9 +6107,9 @@ func (m *MsgVerifyMembershipResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} } iNdEx = postIndex default: @@ -3218,7 +6133,7 @@ func (m *MsgVerifyMembershipResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgVerifyNonMembership) Unmarshal(dAtA []byte) error { +func (m *SpeculativeUpdateClientUnitEnd) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3241,81 +6156,15 @@ func (m *MsgVerifyNonMembership) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgVerifyNonMembership: wiretype end group for non-group") + return fmt.Errorf("proto: SpeculativeUpdateClientUnitEnd: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgVerifyNonMembership: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SpeculativeUpdateClientUnitEnd: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClientId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Prefix", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Prefix = append(m.Prefix[:0], dAtA[iNdEx:postIndex]...) - if m.Prefix == nil { - m.Prefix = []byte{} - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UnitId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -3340,111 +6189,10 @@ func (m *MsgVerifyNonMembership) Unmarshal(dAtA []byte) error { if postIndex < 0 { return ErrInvalidLengthTx } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Path = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - 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 ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - 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 ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - 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 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} - } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnitId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -3467,7 +6215,49 @@ func (m *MsgVerifyNonMembership) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgVerifyNonMembershipResponse) Unmarshal(dAtA []byte) error { +func (m *SpeculativeUpdateClientBatchEnd) 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SpeculativeUpdateClientBatchEnd: wiretype end group for non-group") + } + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ObservedStateTransition) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3490,17 +6280,17 @@ func (m *MsgVerifyNonMembershipResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgVerifyNonMembershipResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ObservedStateTransition: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgVerifyNonMembershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ObservedStateTransition: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PrevHeight", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3510,29 +6300,31 @@ func (m *MsgVerifyNonMembershipResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) - if m.Message == nil { - m.Message = []byte{} + if m.PrevHeight == nil { + m.PrevHeight = &types1.Height{} + } + if err := m.PrevHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PrevStateId", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3559,64 +6351,14 @@ func (m *MsgVerifyNonMembershipResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + m.PrevStateId = append(m.PrevStateId[:0], dAtA[iNdEx:postIndex]...) + if m.PrevStateId == nil { + m.PrevStateId = []byte{} } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgUpdateClientStreamChunk) 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 ErrIntOverflowTx - } - 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: MsgUpdateClientStreamChunk: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUpdateClientStreamChunk: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Init", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PostHeight", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3643,17 +6385,15 @@ func (m *MsgUpdateClientStreamChunk) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &UpdateClientStreamInit{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.PostHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Chunk = &MsgUpdateClientStreamChunk_Init{v} iNdEx = postIndex - case 2: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field HeaderChunk", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PostStateId", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3663,26 +6403,25 @@ func (m *MsgUpdateClientStreamChunk) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - v := &UpdateClientStreamHeaderChunk{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.PostStateId = append(m.PostStateId[:0], dAtA[iNdEx:postIndex]...) + if m.PostStateId == nil { + m.PostStateId = []byte{} } - m.Chunk = &MsgUpdateClientStreamChunk_HeaderChunk{v} iNdEx = postIndex default: iNdEx = preIndex @@ -3705,7 +6444,7 @@ func (m *MsgUpdateClientStreamChunk) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { +func (m *StitchedSpeculativeUpdateClientUnitResult) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3728,17 +6467,17 @@ func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateClientStreamInit: wiretype end group for non-group") + return fmt.Errorf("proto: StitchedSpeculativeUpdateClientUnitResult: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateClientStreamInit: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: StitchedSpeculativeUpdateClientUnitResult: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Response", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3748,49 +6487,30 @@ func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.ClientId = string(dAtA[iNdEx:postIndex]) + if err := m.Response.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IncludeState", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.IncludeState = bool(v != 0) - case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ObservedTransition", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3800,57 +6520,24 @@ func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = append(m.Signer[:0], dAtA[iNdEx:postIndex]...) - if m.Signer == nil { - m.Signer = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF + if err := m.ObservedTransition.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.TypeUrl = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -3873,7 +6560,7 @@ func (m *UpdateClientStreamInit) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateClientStreamHeaderChunk) Unmarshal(dAtA []byte) error { +func (m *ExecuteSpeculativeUpdateClientBatchResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3896,17 +6583,17 @@ func (m *UpdateClientStreamHeaderChunk) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateClientStreamHeaderChunk: wiretype end group for non-group") + return fmt.Errorf("proto: ExecuteSpeculativeUpdateClientBatchResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateClientStreamHeaderChunk: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ExecuteSpeculativeUpdateClientBatchResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) } - var byteLen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -3916,24 +6603,56 @@ func (m *UpdateClientStreamHeaderChunk) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + byteLen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Units", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Units = append(m.Units, &StitchedSpeculativeUpdateClientUnitResult{}) + if err := m.Units[len(m.Units)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex default: From 3b90048dac4c3ac5c9b9111a6d764e49d85e0d67 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 12 May 2026 15:41:06 +0900 Subject: [PATCH 02/29] relay: configure explicit-state update clients --- proto/relayer/provers/lcp/config/config.proto | 6 ++ relay/config.go | 17 ++++- relay/config.pb.go | 68 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/proto/relayer/provers/lcp/config/config.proto b/proto/relayer/provers/lcp/config/config.proto index 0942b4e4..088c17cb 100644 --- a/proto/relayer/provers/lcp/config/config.proto +++ b/proto/relayer/provers/lcp/config/config.proto @@ -54,6 +54,12 @@ message ProverConfig { // if empty (default), local implementation will be used // if specified, SetupHeadersForUpdate will use this gRPC server instead string elc_updater_grpc_address = 34; + + // Max speculative UpdateClient units per request. + uint32 max_speculative_batch_units_per_request = 35; + + // Disable the explicit-state UpdateClient path for this prover. + bool disable_explicit_state_update_client = 36; } message Fraction { diff --git a/relay/config.go b/relay/config.go index 088ea7e1..87015536 100644 --- a/relay/config.go +++ b/relay/config.go @@ -20,7 +20,9 @@ const ( DefaultDialTimeout = 20 // seconds DefaultMessageAggregationBatchSize = 8 // It is necessary to subtract from 4 MB to account for metadata size. - DefaultMaxChunkSize = 4*1024*1024 - 1024 + DefaultMaxChunkSize = 4*1024*1024 - 1024 + MaxSpeculativeBatchHeaderChunkSize = DefaultMaxChunkSize + DefaultMaxSpeculativeBatchUnits = 256 ) var _ core.ProverConfig = (*ProverConfig)(nil) @@ -100,6 +102,13 @@ func (pc ProverConfig) GetMaxChunkSizeForUpdateClient() uint32 { } } +func (pc ProverConfig) GetMaxSpeculativeBatchUnitsPerRequest() int { + if pc.MaxSpeculativeBatchUnitsPerRequest == 0 { + return DefaultMaxSpeculativeBatchUnits + } + return int(pc.MaxSpeculativeBatchUnitsPerRequest) +} + func (pc ProverConfig) Validate() error { // origin prover config validation if err := pc.OriginProver.GetCachedValue().(core.ProverConfig).Validate(); err != nil { @@ -117,6 +126,12 @@ func (pc ProverConfig) Validate() error { if pc.MessageAggregation && pc.MessageAggregationBatchSize == 1 { return fmt.Errorf("MessageAggregationBatchSize must be greater than 1 if MessageAggregation is true and MessageAggregationBatchSize is set") } + if pc.MaxChunkSizeForUpdateClient > MaxSpeculativeBatchHeaderChunkSize { + return fmt.Errorf("MaxChunkSizeForUpdateClient must be less than or equal to %d", MaxSpeculativeBatchHeaderChunkSize) + } + if pc.MaxSpeculativeBatchUnitsPerRequest > DefaultMaxSpeculativeBatchUnits { + return fmt.Errorf("MaxSpeculativeBatchUnitsPerRequest must be less than or equal to %d", DefaultMaxSpeculativeBatchUnits) + } if pc.KeyUpdateBufferTime == 0 { return fmt.Errorf("KeyUpdateBufferTime must be greater than 0") } diff --git a/relay/config.pb.go b/relay/config.pb.go index 35eb9b4c..961ebb2f 100644 --- a/relay/config.pb.go +++ b/relay/config.pb.go @@ -70,6 +70,10 @@ type ProverConfig struct { // if empty (default), local implementation will be used // if specified, SetupHeadersForUpdate will use this gRPC server instead ElcUpdaterGrpcAddress string `protobuf:"bytes,34,opt,name=elc_updater_grpc_address,json=elcUpdaterGrpcAddress,proto3" json:"elc_updater_grpc_address,omitempty"` + // Max speculative UpdateClient units per request. + MaxSpeculativeBatchUnitsPerRequest uint32 `protobuf:"varint,35,opt,name=max_speculative_batch_units_per_request,json=maxSpeculativeBatchUnitsPerRequest,proto3" json:"max_speculative_batch_units_per_request,omitempty"` + // Disable the explicit-state UpdateClient path for this prover. + DisableExplicitStateUpdateClient bool `protobuf:"varint,36,opt,name=disable_explicit_state_update_client,json=disableExplicitStateUpdateClient,proto3" json:"disable_explicit_state_update_client,omitempty"` } func (m *ProverConfig) Reset() { *m = ProverConfig{} } @@ -434,6 +438,25 @@ func (m *ProverConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x92 } + if m.MaxSpeculativeBatchUnitsPerRequest != 0 { + i = encodeVarintConfig(dAtA, i, uint64(m.MaxSpeculativeBatchUnitsPerRequest)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x98 + } + if m.DisableExplicitStateUpdateClient { + i-- + if m.DisableExplicitStateUpdateClient { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xa0 + } if m.MaxChunkSizeForUpdateClient != 0 { i = encodeVarintConfig(dAtA, i, uint64(m.MaxChunkSizeForUpdateClient)) i-- @@ -906,6 +929,12 @@ func (m *ProverConfig) Size() (n int) { if l > 0 { n += 2 + l + sovConfig(uint64(l)) } + if m.MaxSpeculativeBatchUnitsPerRequest != 0 { + n += 2 + sovConfig(uint64(m.MaxSpeculativeBatchUnitsPerRequest)) + } + if m.DisableExplicitStateUpdateClient { + n += 3 + } return n } @@ -1651,6 +1680,45 @@ func (m *ProverConfig) Unmarshal(dAtA []byte) error { } m.ElcUpdaterGrpcAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 35: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxSpeculativeBatchUnitsPerRequest", wireType) + } + m.MaxSpeculativeBatchUnitsPerRequest = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConfig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxSpeculativeBatchUnitsPerRequest |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 36: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DisableExplicitStateUpdateClient", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConfig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DisableExplicitStateUpdateClient = bool(v != 0) default: iNdEx = preIndex skippy, err := skipConfig(dAtA[iNdEx:]) From 196a4933140d5df4a87e7c4cd04d0039bc7f34d9 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 12 May 2026 15:43:01 +0900 Subject: [PATCH 03/29] relay: stream explicit-state update clients --- relay/client.go | 2 + relay/elcupdater/module/service.go | 10 - relay/explicit_state_batch.go | 228 ++++++++++++++++++ relay/explicit_state_batch_types.go | 19 ++ relay/explicit_state_source_header_units.go | 81 +++++++ relay/explicit_state_update_client.go | 202 ++++++++++++++++ relay/lcp.go | 68 ++++-- relay/prover.go | 244 +++++++++++++++++++- 8 files changed, 812 insertions(+), 42 deletions(-) create mode 100644 relay/explicit_state_batch.go create mode 100644 relay/explicit_state_batch_types.go create mode 100644 relay/explicit_state_source_header_units.go create mode 100644 relay/explicit_state_update_client.go diff --git a/relay/client.go b/relay/client.go index 90556b70..c88521df 100644 --- a/relay/client.go +++ b/relay/client.go @@ -13,6 +13,7 @@ type ( ) type LCPServiceClient struct { + conn *grpc.ClientConn ELCMsgClient ELCQueryClient EnclaveQueryClient @@ -20,6 +21,7 @@ type LCPServiceClient struct { func NewLCPServiceClient(conn *grpc.ClientConn) LCPServiceClient { return LCPServiceClient{ + conn: conn, ELCMsgClient: elc.NewMsgClient(conn), ELCQueryClient: elc.NewQueryClient(conn), EnclaveQueryClient: enclave.NewQueryClient(conn), diff --git a/relay/elcupdater/module/service.go b/relay/elcupdater/module/service.go index c1537b96..35abd86f 100644 --- a/relay/elcupdater/module/service.go +++ b/relay/elcupdater/module/service.go @@ -21,8 +21,6 @@ import ( "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/codes" - "google.golang.org/grpc/health" - healthpb "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/status" ) @@ -205,13 +203,6 @@ func (srv *Service) runGRPCServer(ctx context.Context) error { // Register ELCUpdater service elcupdater_grpc.RegisterServiceServer(grpcServer, grpcService) - - // Register standard grpc.health.v1 liveness service. The overall ("") - // status reports SERVING for as long as this gRPC server is accepting - // requests. - healthSrv := health.NewServer() - healthpb.RegisterHealthServer(grpcServer, healthSrv) - // Create listener lis, err := net.Listen("tcp", srv.GRPCAddr) if err != nil { @@ -231,7 +222,6 @@ func (srv *Service) runGRPCServer(ctx context.Context) error { <-ctx.Done() logger.InfoContext(ctx, "Stopping ELCUpdater gRPC server") - healthSrv.Shutdown() grpcServer.GracefulStop() return nil diff --git a/relay/explicit_state_batch.go b/relay/explicit_state_batch.go new file mode 100644 index 00000000..58d551bd --- /dev/null +++ b/relay/explicit_state_batch.go @@ -0,0 +1,228 @@ +package relay + +import ( + "context" + "errors" + "fmt" + "io" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/gogoproto/proto" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + "github.com/datachainlab/lcp-go/relay/elc" +) + +type speculativeBatchStreamSender struct { + stream elc.Msg_SpeculativeUpdateClientBatchStreamClient + chunkSize uint32 + count int +} + +func openSpeculativeUpdateClientBatchStream( + ctx context.Context, + client LCPServiceClient, + clientID string, + chunkSize uint32, +) (*speculativeBatchStreamSender, error) { + if err := validateSpeculativeBatchStreamChunkSize(chunkSize); err != nil { + return nil, err + } + stream, err := client.SpeculativeUpdateClientBatchStream(ctx) + if err != nil { + return nil, fmt.Errorf("failed to call SpeculativeUpdateClientBatchStream: %w", err) + } + if err := stream.Send(&elc.MsgSpeculativeUpdateClientBatchStreamChunk{ + Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_Init{ + Init: &elc.SpeculativeUpdateClientBatchStreamInit{ + ClientId: clientID, + }, + }, + }); err != nil { + _ = stream.CloseSend() + return nil, fmt.Errorf("failed to send speculative batch init: %w", err) + } + return &speculativeBatchStreamSender{ + stream: stream, + chunkSize: chunkSize, + }, nil +} + +func (s *speculativeBatchStreamSender) Send(unit *SpeculativeUpdateClientUnit) error { + if s == nil || s.stream == nil { + return fmt.Errorf("speculative batch stream is not open") + } + if err := sendSpeculativeUpdateClientUnit(s.stream, unit, s.chunkSize); err != nil { + return err + } + s.count++ + return nil +} + +func (s *speculativeBatchStreamSender) CloseAndRecv() (*ExecuteSpeculativeUpdateClientBatchResponse, error) { + if s == nil || s.stream == nil { + return nil, fmt.Errorf("speculative batch stream is not open") + } + if err := s.stream.Send(&elc.MsgSpeculativeUpdateClientBatchStreamChunk{ + Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd{ + BatchEnd: &elc.SpeculativeUpdateClientBatchEnd{}, + }, + }); err != nil { + err, _ = s.enrichSendError(err) + return nil, fmt.Errorf("failed to send speculative batch end: %w", err) + } + return s.recvCloseStatus() +} + +func (s *speculativeBatchStreamSender) recvCloseStatus() (*ExecuteSpeculativeUpdateClientBatchResponse, error) { + if s == nil || s.stream == nil { + return nil, fmt.Errorf("speculative batch stream is not open") + } + resp, err := s.stream.CloseAndRecv() + if err != nil { + return nil, err + } + return resp, nil +} + +func (s *speculativeBatchStreamSender) enrichSendError(sendErr error) (error, bool) { + if !errors.Is(sendErr, io.EOF) { + return sendErr, false + } + _, closeErr := s.recvCloseStatus() + if closeErr == nil { + return sendErr, true + } + return fmt.Errorf("%w; server status after send failure: %v", sendErr, closeErr), true +} + +func (s *speculativeBatchStreamSender) CloseSend() error { + if s == nil || s.stream == nil { + return nil + } + return s.stream.CloseSend() +} + +func sendSpeculativeUpdateClientUnit( + stream elc.Msg_SpeculativeUpdateClientBatchStreamClient, + unit *SpeculativeUpdateClientUnit, + chunkSize uint32, +) error { + if unit == nil { + return fmt.Errorf("unit must not be nil") + } + if unit.Update == nil { + return fmt.Errorf("unit update must not be nil") + } + if unit.Update.Header == nil { + return fmt.Errorf("unit update header must not be nil") + } + if len(unit.Update.Header.Value) == 0 { + return fmt.Errorf("unit update header value must not be empty") + } + if err := validateSpeculativeBatchStreamChunkSize(chunkSize); err != nil { + return err + } + baseState := cloneExplicitStateRef(unit.BaseState) + if baseState == nil { + return fmt.Errorf("unit base_state must not be nil") + } + + unitInitChunk := &elc.MsgSpeculativeUpdateClientBatchStreamChunk{ + Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit{ + UnitInit: &elc.SpeculativeUpdateClientUnitInit{ + UnitId: unit.UnitId, + TypeUrl: unit.Update.Header.TypeUrl, + IncludeState: unit.Update.IncludeState, + Signer: append([]byte(nil), unit.Update.Signer...), + BaseState: *baseState, + }, + }, + } + if size := proto.Size(unitInitChunk); size > DefaultMaxChunkSize { + return fmt.Errorf( + "unit init chunk exceeds max safe speculative batch stream chunk size: unit_id=%q size=%d max=%d", + unit.UnitId, + size, + DefaultMaxChunkSize, + ) + } + if err := stream.Send(unitInitChunk); err != nil { + return fmt.Errorf("failed to send unit init: %w", err) + } + + chunks, err := splitBytes(unit.Update.Header.Value, chunkSize) + if err != nil { + return fmt.Errorf("failed to split unit header: %w", err) + } + for i, chunk := range chunks { + if err := stream.Send(&elc.MsgSpeculativeUpdateClientBatchStreamChunk{ + Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk{ + UnitHeaderChunk: &elc.SpeculativeUpdateClientUnitHeaderChunk{ + UnitId: unit.UnitId, + Data: chunk, + }, + }, + }); err != nil { + return fmt.Errorf("failed to send unit header chunk: index=%d, %w", i, err) + } + } + + if err := stream.Send(&elc.MsgSpeculativeUpdateClientBatchStreamChunk{ + Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd{ + UnitEnd: &elc.SpeculativeUpdateClientUnitEnd{ + UnitId: unit.UnitId, + }, + }, + }); err != nil { + return fmt.Errorf("failed to send unit end: %w", err) + } + return nil +} + +func validateSpeculativeBatchStreamChunkSize(chunkSize uint32) error { + if chunkSize == 0 { + return fmt.Errorf("chunk size must be greater than 0") + } + if chunkSize > MaxSpeculativeBatchHeaderChunkSize { + return fmt.Errorf("chunk size must be less than or equal to %d", MaxSpeculativeBatchHeaderChunkSize) + } + return nil +} + +func unitIDForError(unit *SpeculativeUpdateClientUnit) string { + if unit == nil { + return "" + } + return unit.UnitId +} + +func cloneExplicitStateRef(ref *ExplicitStateRef) *ExplicitStateRef { + if ref == nil { + return nil + } + var prevHeight *clienttypes.Height + if ref.PrevHeight != nil { + h := *ref.PrevHeight + prevHeight = &h + } + return &ExplicitStateRef{ + PrevHeight: prevHeight, + PrevStateId: append([]byte(nil), ref.PrevStateId...), + ClientState: cloneAny(ref.ClientState), + ConsensusState: cloneAny(ref.ConsensusState), + } +} + +func cloneAny(any *codectypes.Any) *codectypes.Any { + if any == nil { + return nil + } + return &codectypes.Any{ + TypeUrl: any.TypeUrl, + Value: append([]byte(nil), any.Value...), + } +} + +func buildSpeculativeUnitID(i int) string { + return fmt.Sprintf("unit-%04d", i) +} diff --git a/relay/explicit_state_batch_types.go b/relay/explicit_state_batch_types.go new file mode 100644 index 00000000..8426de5f --- /dev/null +++ b/relay/explicit_state_batch_types.go @@ -0,0 +1,19 @@ +package relay + +import "github.com/datachainlab/lcp-go/relay/elc" + +type ExplicitStateRef = elc.ExplicitStateRef +type ObservedStateTransition = elc.ObservedStateTransition +type StitchedSpeculativeUpdateClientUnitResult = elc.StitchedSpeculativeUpdateClientUnitResult +type ExecuteSpeculativeUpdateClientBatchResponse = elc.ExecuteSpeculativeUpdateClientBatchResponse + +type SpeculativeUpdateClientUnit struct { + UnitId string + Update *elc.MsgUpdateClient + BaseState *ExplicitStateRef +} + +type ExecuteSpeculativeUpdateClientBatchRequest struct { + ClientId string + Units []*SpeculativeUpdateClientUnit +} diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go new file mode 100644 index 00000000..254a6405 --- /dev/null +++ b/relay/explicit_state_source_header_units.go @@ -0,0 +1,81 @@ +package relay + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/hyperledger-labs/yui-relayer/core" +) + +type ExplicitStateSourceHeaderUnit struct { + Header core.Header + AnyHeader *codectypes.Any + BaseState *ExplicitStateRef +} + +type ExplicitStateSourceHeaderUnitOrError struct { + Unit *ExplicitStateSourceHeaderUnit + Error error +} + +func makeExplicitStateSourceHeaderUnitStream( + units []*ExplicitStateSourceHeaderUnit, +) <-chan *ExplicitStateSourceHeaderUnitOrError { + ch := make(chan *ExplicitStateSourceHeaderUnitOrError, len(units)) + for _, unit := range units { + ch <- &ExplicitStateSourceHeaderUnitOrError{Unit: unit} + } + close(ch) + return ch +} + +func drainExplicitStateSourceHeaderUnitStream( + unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, +) ([]*ExplicitStateSourceHeaderUnit, error) { + var units []*ExplicitStateSourceHeaderUnit + i := 0 + for item := range unitStream { + unit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, i) + if err != nil { + return nil, err + } + units = append(units, unit) + i += 1 + } + return units, nil +} + +func explicitStateSourceHeaderUnitFromStreamItemOrError( + item *ExplicitStateSourceHeaderUnitOrError, + i int, +) (*ExplicitStateSourceHeaderUnit, error) { + if item == nil { + return nil, fmt.Errorf("received nil explicit-state source header stream item: i=%v", i) + } + if item.Error != nil { + return nil, fmt.Errorf("failed to setup an explicit-state source header unit: i=%v %w", i, item.Error) + } + if item.Unit == nil { + return nil, fmt.Errorf("received nil explicit-state source header unit: i=%v", i) + } + if item.Unit.AnyHeader == nil { + return nil, fmt.Errorf("explicit-state source header unit missing packed header: i=%v", i) + } + return item.Unit, nil +} + +func extractAnyHeadersFromSourceUnits( + units []*ExplicitStateSourceHeaderUnit, +) ([]*codectypes.Any, error) { + anyHeaders := make([]*codectypes.Any, 0, len(units)) + for i, unit := range units { + if unit == nil { + return nil, fmt.Errorf("source header unit must not be nil: i=%v", i) + } + if unit.AnyHeader == nil { + return nil, fmt.Errorf("source header unit missing packed header: i=%v", i) + } + anyHeaders = append(anyHeaders, unit.AnyHeader) + } + return anyHeaders, nil +} diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go new file mode 100644 index 00000000..64fb8476 --- /dev/null +++ b/relay/explicit_state_update_client.go @@ -0,0 +1,202 @@ +package relay + +import ( + "context" + "crypto/sha256" + "fmt" + + "github.com/datachainlab/lcp-go/relay/elc" + elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" +) + +func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { + return !pr.config.DisableExplicitStateUpdateClient +} + +func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { + return baseState != nil && + baseState.PrevHeight != nil && + baseState.ClientState != nil && + baseState.ConsensusState != nil +} + +func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( + ctx context.Context, + unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, + elcClientID string, + includeState bool, + signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, []*ExplicitStateSourceHeaderUnit, error) { + var results []*elcupdater_storage.UpdateClientResult + var sourceHeaderUnits []*ExplicitStateSourceHeaderUnit + + maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() + var sender *speculativeBatchStreamSender + closed := true + batchSigners := make([][]byte, 0, maxUnits) + batchIndex := 0 + unitIndex := 0 + defer func() { + if !closed && sender != nil { + _ = sender.CloseSend() + } + }() + + openBatch := func() error { + if sender != nil { + return nil + } + pr.getLogger().InfoContext( + ctx, + "invoke speculative update client batch", + "client_id", elcClientID, + "batch_index", batchIndex, + "batch_unit_limit", maxUnits, + ) + nextSender, err := openSpeculativeUpdateClientBatchStream( + ctx, + pr.lcpServiceClient, + elcClientID, + pr.config.GetMaxChunkSizeForUpdateClient(), + ) + if err != nil { + return err + } + sender = nextSender + closed = false + return nil + } + + flushBatch := func() error { + if sender == nil { + return nil + } + resp, err := sender.CloseAndRecv() + closed = true + if err != nil { + return fmt.Errorf("failed explicit-state update client batch: %w", err) + } + if len(resp.Units) != len(batchSigners) { + return fmt.Errorf("unexpected speculative batch response shape: units=%d sent=%d", len(resp.Units), len(batchSigners)) + } + for i, unit := range resp.Units { + if unit == nil { + return fmt.Errorf("unexpected speculative batch response unit at index %d", i) + } + results = append(results, &elcupdater_storage.UpdateClientResult{ + Message: unit.Response.Message, + Signature: unit.Response.Signature, + Signer: batchSigners[i], + }) + } + sender = nil + batchSigners = batchSigners[:0] + batchIndex++ + return nil + } + + for item := range unitStream { + sourceUnit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, unitIndex) + if err != nil { + return nil, sourceHeaderUnits, err + } + if sourceUnit.BaseState == nil { + sourceHeaderUnits = append(sourceHeaderUnits, sourceUnit) + if err := flushBatch(); err != nil { + return nil, sourceHeaderUnits, err + } + serialResults, err := pr.executeELCUpdateHeaderUnits( + ctx, + []*ExplicitStateSourceHeaderUnit{sourceUnit}, + elcClientID, + includeState, + signer, + ) + if err != nil { + return nil, sourceHeaderUnits, err + } + results = append(results, serialResults...) + unitIndex++ + continue + } + sourceHeaderUnits = append(sourceHeaderUnits, sourceUnit) + if sourceUnit.AnyHeader == nil { + return nil, sourceHeaderUnits, fmt.Errorf("explicit-state source header unit[%d] missing packed header", unitIndex) + } + if sourceUnit.BaseState == nil { + return nil, sourceHeaderUnits, fmt.Errorf("explicit-state source header unit[%d] missing base state", unitIndex) + } + baseState := cloneExplicitStateRef(sourceUnit.BaseState) + + unitID := buildSpeculativeUnitID(unitIndex) + update := &elc.MsgUpdateClient{ + ClientId: elcClientID, + Header: sourceUnit.AnyHeader, + IncludeState: includeState, + Signer: signer, + } + if sender == nil && unitIndex > 0 && !hasCanonicalExplicitStatePayload(baseState) { + return nil, sourceHeaderUnits, fmt.Errorf( + "cannot split explicit-state batch at unit %s: missing base state payload", + unitID, + ) + } + + if err := openBatch(); err != nil { + return nil, sourceHeaderUnits, err + } + logExplicitStateUnitSend(ctx, pr, elcClientID, unitID, batchIndex, len(batchSigners), unitIndex, includeState, update) + if err := sender.Send(&SpeculativeUpdateClientUnit{ + UnitId: unitID, + Update: update, + BaseState: baseState, + }); err != nil { + err, _ = sender.enrichSendError(err) + return nil, sourceHeaderUnits, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) + } + batchSigners = append(batchSigners, update.Signer) + unitIndex++ + + if len(batchSigners) == maxUnits { + if err := flushBatch(); err != nil { + return nil, sourceHeaderUnits, err + } + } + } + if err := flushBatch(); err != nil { + return nil, sourceHeaderUnits, err + } + return results, sourceHeaderUnits, nil +} + +func logExplicitStateUnitSend( + ctx context.Context, + pr *Prover, + elcClientID string, + unitID string, + batchIndex int, + batchUnitIndex int, + unitIndex int, + includeState bool, + update *elc.MsgUpdateClient, +) { + headerBytes := 0 + headerSHA256 := "" + if update != nil && update.Header != nil { + headerBytes = len(update.Header.Value) + headerHash := sha256.Sum256(update.Header.Value) + headerSHA256 = fmt.Sprintf("%x", headerHash) + } + pr.getLogger().InfoContext( + ctx, + "send speculative update client unit", + "client_id", elcClientID, + "unit_id", unitID, + "batch_index", batchIndex, + "batch_unit_index", batchUnitIndex, + "unit_index", unitIndex, + "include_state", includeState, + "header_bytes", headerBytes, + "header_sha256", headerSHA256, + ) +} diff --git a/relay/lcp.go b/relay/lcp.go index 77faa5ef..f52dd255 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -394,30 +394,58 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat pr.getLogger().InfoContext(ctx, "try to setup headers", "elc_client_id", elcClientID, "client_state.latest_height", clientState.GetLatestHeight(), "latest", latestHeader.GetHeight()) - // 2. query the header from the upstream chain + sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) + signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() + if pr.shouldUseExplicitStateUpdateClient() { + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(ctx, sourceChain, latestHeader) + if err != nil { + return nil, err + } + if ok { + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx, + sourceHeaderUnitStream, + elcClientID, + includeState, + signer, + explicitStateFallbackOperationEnclaveKeyUpdate, + ) + if err != nil { + return nil, err + } + responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) + for _, result := range results { + responses = append(responses, &elc.MsgUpdateClientResponse{ + Message: result.Message, + Signature: result.Signature, + }) + } + return responses, nil + } + } - headerStream, err := pr.originProver.SetupHeadersForUpdate(ctx, NewLCPQuerier(pr.lcpServiceClient, elcClientID), latestHeader) + // 2. query the header from the upstream chain. + headerStream, err := pr.originProver.SetupHeadersForUpdate(ctx, sourceChain, latestHeader) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to setup headers for update: header=%v %w", latestHeader, err) } - // 3. send a request that contains a header from 2 to update the client in ELC - var responses []*elc.MsgUpdateClientResponse - i := 0 - for h := range headerStream { - if h.Error != nil { - return nil, fmt.Errorf("failed to setup a header for update: i=%v %w", i, h.Error) - } - anyHeader, err := clienttypes.PackClientMessage(h.Header) - if err != nil { - return nil, fmt.Errorf("failed to pack header: i=%v header=%v %w", i, h.Header, err) - } - res, err := updateClient(ctx, pr.config.GetMaxChunkSizeForUpdateClient(), pr.lcpServiceClient, anyHeader, elcClientID, includeState, pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to update ELC: i=%v elc_client_id=%v %w", i, pr.config.ElcClientId, err) - } - responses = append(responses, res) - i += 1 + results, err := pr.executeELCUpdateHeaderStream( + ctx, + headerStream, + elcClientID, + includeState, + signer, + ) + if err != nil { + return nil, err + } + responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) + for _, result := range results { + responses = append(responses, &elc.MsgUpdateClientResponse{ + Message: result.Message, + Signature: result.Signature, + }) } return responses, nil diff --git a/relay/prover.go b/relay/prover.go index b3f1eef8..1e6ea222 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -3,8 +3,10 @@ package relay import ( "bytes" "context" + "errors" "fmt" "os" + "strings" "time" "github.com/cosmos/cosmos-sdk/codec" @@ -18,12 +20,15 @@ import ( "github.com/datachainlab/lcp-go/sgx" "github.com/hyperledger-labs/yui-relayer/core" "github.com/hyperledger-labs/yui-relayer/log" + yuiotelcore "github.com/hyperledger-labs/yui-relayer/otelcore" "github.com/hyperledger-labs/yui-relayer/signer" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" + grpcstatus "google.golang.org/grpc/status" ) type Prover struct { @@ -50,10 +55,19 @@ type Prover struct { gauge *Int64Gauge } +type ExplicitStateChunkProvider interface { + SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) +} + var ( _ core.Prover = (*Prover)(nil) ) +const ( + explicitStateFallbackOperationUpdateClient = "update_client" + explicitStateFallbackOperationEnclaveKeyUpdate = "enclave_key_update" +) + func NewProver(config ProverConfig, originChain core.Chain, originProver core.Prover) (*Prover, error) { conn, err := grpc.Dial( config.LcpServiceAddress, @@ -283,42 +297,248 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // updateELCForUpdateClient performs the initial setup and updateClient calls // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { + if pr.shouldUseExplicitStateUpdateClient() { + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(ctx, dstChain, latestFinalizedHeader) + if err != nil { + return nil, err + } + if ok { + signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx, + sourceHeaderUnitStream, + pr.config.ElcClientId, + false, + signer, + explicitStateFallbackOperationUpdateClient, + ) + if err != nil { + return nil, err + } + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) + } + } + headerStream, err := pr.originProver.SetupHeadersForUpdate(ctx, dstChain, latestFinalizedHeader) if err != nil { return nil, fmt.Errorf("failed to setup headers for update: header=%v %w", latestFinalizedHeader, err) } - var results []*elcupdater_storage.UpdateClientResult + signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() + results, err := pr.executeELCUpdateHeaderStream( + ctx, + headerStream, + pr.config.ElcClientId, + false, + signer, + ) + if err != nil { + return nil, err + } + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) +} + +func (pr *Prover) validateUpdateELCResults(ctx context.Context, latestFinalizedHeader core.Header, results []*elcupdater_storage.UpdateClientResult) ([]*elcupdater_storage.UpdateClientResult, error) { + if len(results) == 0 { + if pr.gauge != nil { + pr.gauge.Set(ctx, int64(latestFinalizedHeader.GetHeight().GetRevisionHeight())) + } + return nil, nil + } + + for _, result := range results { + if _, err := lcptypes.EthABIDecodeHeaderedProxyMessage(result.Message); err != nil { + return nil, fmt.Errorf("failed to decode headered proxy message: message=%x %w", result.Message, err) + } + } + + if pr.gauge != nil { + pr.gauge.Set(ctx, int64(latestFinalizedHeader.GetHeight().GetRevisionHeight())) + } + return results, nil +} + +func (pr *Prover) executeELCUpdateHeaderStream( + ctx context.Context, + headerStream <-chan *core.HeaderOrError, + elcClientID string, + includeState bool, + signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, error) { + var results []*elcupdater_storage.UpdateClientResult + i := 0 for h := range headerStream { + if h == nil { + return nil, fmt.Errorf("received nil header stream item: i=%v", i) + } if h.Error != nil { - return nil, fmt.Errorf("failed to setup a header for update: %w", h.Error) + return nil, fmt.Errorf("failed to setup a header for update: i=%v %w", i, h.Error) + } + if h.Header == nil { + return nil, fmt.Errorf("received nil header in header stream: i=%v", i) } anyHeader, err := clienttypes.PackClientMessage(h.Header) if err != nil { - return nil, fmt.Errorf("failed to pack header: header=%v %w", h.Header, err) + return nil, fmt.Errorf("failed to pack header: i=%v header=%v %w", i, h.Header, err) } - signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() - res, err := updateClient(ctx, pr.config.GetMaxChunkSizeForUpdateClient(), pr.lcpServiceClient, anyHeader, pr.config.ElcClientId, false, signer) + res, err := updateClient(ctx, pr.config.GetMaxChunkSizeForUpdateClient(), pr.lcpServiceClient, anyHeader, elcClientID, includeState, signer) if err != nil { - return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", pr.config.ElcClientId, err) - } - // ensure the message is valid - if _, err := lcptypes.EthABIDecodeHeaderedProxyMessage(res.Message); err != nil { - return nil, fmt.Errorf("failed to decode headered proxy message: message=%x %w", res.Message, err) + return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) } results = append(results, &elcupdater_storage.UpdateClientResult{ Message: res.Message, Signature: res.Signature, Signer: signer, }) + i += 1 } + return results, nil +} - if pr.gauge != nil { - pr.gauge.Set(ctx, int64(latestFinalizedHeader.GetHeight().GetRevisionHeight())) +func (pr *Prover) executeELCUpdateHeaderUnits( + ctx context.Context, + sourceHeaderUnits []*ExplicitStateSourceHeaderUnit, + elcClientID string, + includeState bool, + signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, error) { + anyHeaders, err := extractAnyHeadersFromSourceUnits(sourceHeaderUnits) + if err != nil { + return nil, err + } + var results []*elcupdater_storage.UpdateClientResult + for _, anyHeader := range anyHeaders { + res, err := updateClient(ctx, pr.config.GetMaxChunkSizeForUpdateClient(), pr.lcpServiceClient, anyHeader, elcClientID, includeState, signer) + if err != nil { + return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) + } + results = append(results, &elcupdater_storage.UpdateClientResult{ + Message: res.Message, + Signature: res.Signature, + Signer: signer, + }) } return results, nil } +func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx context.Context, + sourceHeaderUnitStream <-chan *ExplicitStateSourceHeaderUnitOrError, + elcClientID string, + includeState bool, + signer []byte, + operation string, +) ([]*elcupdater_storage.UpdateClientResult, error) { + results, sourceHeaderUnits, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( + ctx, + sourceHeaderUnitStream, + elcClientID, + includeState, + signer, + ) + if err == nil { + return results, nil + } + if !shouldFallbackToSerialUpdateClient(err) { + return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) + } + if shouldLogSerialUpdateClientFallback(err) { + pr.getLogger().InfoContext( + ctx, + "fall back to serial update client after explicit-state batch failure", + "operation", operation, + "client_id", elcClientID, + "error", err.Error(), + ) + } + remainingUnits, collectErr := drainExplicitStateSourceHeaderUnitStream(sourceHeaderUnitStream) + if collectErr != nil { + return nil, collectErr + } + sourceHeaderUnits = append(sourceHeaderUnits, remainingUnits...) + return pr.executeELCUpdateHeaderUnits(ctx, sourceHeaderUnits, elcClientID, includeState, signer) +} + +const ( + speculativeBatchFailureKindBaseStateMismatch = "BaseStateMismatch" + speculativeBatchFailureKindDependencyStateMismatch = "DependencyStateMismatch" +) + +// Serial fallback is allowed only for capability failures and explicit-state +// prediction mismatches that indicate this ELC cannot provide a stable ordered +// chain base state for the current attempt. Ordered-chain merge/write-set +// failures must stay visible so we do not silently mask explicit-state bugs as +// a successful serial update-client. +func shouldFallbackToSerialUpdateClient(err error) bool { + for current := err; current != nil; current = errors.Unwrap(current) { + if grpcstatus.Code(current) == codes.Unimplemented { + return true + } + if kind, ok := speculativeBatchFailureKindFromError(current); ok { + switch kind { + case speculativeBatchFailureKindBaseStateMismatch, speculativeBatchFailureKindDependencyStateMismatch: + return true + } + } + } + return false +} + +func shouldLogSerialUpdateClientFallback(err error) bool { + for current := err; current != nil; current = errors.Unwrap(current) { + if grpcstatus.Code(current) == codes.Unimplemented { + return false + } + } + return true +} + +func speculativeBatchFailureKindFromError(err error) (string, bool) { + statusErr, ok := grpcstatus.FromError(err) + if !ok || statusErr.Code() != codes.Aborted { + return "", false + } + message := statusErr.Message() + for _, kind := range []string{ + speculativeBatchFailureKindBaseStateMismatch, + speculativeBatchFailureKindDependencyStateMismatch, + } { + if strings.HasPrefix(message, kind+": ") { + return kind, true + } + } + return "", false +} + +func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( + ctx context.Context, + dstChain core.FinalityAwareChain, + latestFinalizedHeader core.Header, +) (<-chan *ExplicitStateSourceHeaderUnitOrError, bool, error) { + if provider, ok := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider); ok { + unitStream, err := provider.SetupExplicitStateChunksForUpdate(ctx, dstChain, latestFinalizedHeader) + if err != nil { + return nil, true, err + } + if unitStream == nil { + return nil, true, fmt.Errorf("explicit-state chunk provider returned nil source header unit stream") + } + return unitStream, true, nil + } + return nil, false, nil +} + +func unwrapExplicitStateOriginProver(prover core.Prover) core.Prover { + for { + switch p := prover.(type) { + case *yuiotelcore.Prover: + prover = p.Prover + default: + return prover + } + } +} + func splitMessagesBySigner(messages [][]byte, signatures [][]byte, signers [][]byte) ([]*elc.MsgAggregateMessages, error) { if len(messages) == 0 { return nil, fmt.Errorf("messages must not be empty") From 246ef6722b23495638a47dedb2e2cd0f6d691aad Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 12 May 2026 15:43:31 +0900 Subject: [PATCH 04/29] relay: test explicit-state update clients --- relay/explicit_state_batch_test.go | 95 ++ relay/explicit_state_plan_test.go | 1258 ++++++++++++++++++++ relay/explicit_state_update_client_test.go | 138 +++ 3 files changed, 1491 insertions(+) create mode 100644 relay/explicit_state_batch_test.go create mode 100644 relay/explicit_state_plan_test.go create mode 100644 relay/explicit_state_update_client_test.go diff --git a/relay/explicit_state_batch_test.go b/relay/explicit_state_batch_test.go new file mode 100644 index 00000000..05bf23c6 --- /dev/null +++ b/relay/explicit_state_batch_test.go @@ -0,0 +1,95 @@ +package relay + +import ( + "io" + "strings" + "testing" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/datachainlab/lcp-go/relay/elc" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestSendSpeculativeUpdateClientUnitRejectsOversizedUnitInit(t *testing.T) { + stream := &recordingSpeculativeBatchStream{} + err := sendSpeculativeUpdateClientUnit(stream, &SpeculativeUpdateClientUnit{ + UnitId: "unit-large", + Update: &elc.MsgUpdateClient{ + ClientId: "client-0", + Header: &codectypes.Any{ + TypeUrl: "header", + Value: []byte("h"), + }, + }, + BaseState: &ExplicitStateRef{ + ClientState: &codectypes.Any{ + TypeUrl: "client", + Value: make([]byte, int(MaxSpeculativeBatchHeaderChunkSize)), + }, + ConsensusState: &codectypes.Any{ + TypeUrl: "consensus", + Value: []byte("s"), + }, + }, + }, DefaultMaxChunkSize) + if err == nil { + t.Fatal("expected oversized unit init error") + } + if !strings.Contains(err.Error(), `unit_id="unit-large"`) { + t.Fatalf("expected unit id in error, got %v", err) + } + if len(stream.sent) != 0 { + t.Fatalf("expected no stream sends after oversized unit init, got %d", len(stream.sent)) + } +} + +func TestSpeculativeBatchStreamSenderEnrichesEOFWithServerStatus(t *testing.T) { + stream := &recordingSpeculativeBatchStream{ + closeErr: status.Error(codes.ResourceExhausted, "speculative unit header payload too large"), + } + sender := &speculativeBatchStreamSender{stream: stream} + + err, closed := sender.enrichSendError(io.EOF) + if !closed { + t.Fatal("expected EOF enrichment to close the stream with CloseAndRecv") + } + if !strings.Contains(err.Error(), "server status after send failure") { + t.Fatalf("expected enriched server status, got %v", err) + } + if !strings.Contains(err.Error(), "speculative unit header payload too large") { + t.Fatalf("expected server detail in error, got %v", err) + } +} + +type recordingSpeculativeBatchStream struct { + grpc.ClientStream + sent []*elc.MsgSpeculativeUpdateClientBatchStreamChunk + sendErrAfter int + sendErr error + closeErr error + closeAndRecvCalled bool + closeSendCalled bool +} + +func (s *recordingSpeculativeBatchStream) Send(m *elc.MsgSpeculativeUpdateClientBatchStreamChunk) error { + if s.sendErrAfter > 0 && len(s.sent) >= s.sendErrAfter { + return s.sendErr + } + s.sent = append(s.sent, m) + return nil +} + +func (s *recordingSpeculativeBatchStream) CloseAndRecv() (*elc.ExecuteSpeculativeUpdateClientBatchResponse, error) { + s.closeAndRecvCalled = true + if s.closeErr != nil { + return nil, s.closeErr + } + return &elc.ExecuteSpeculativeUpdateClientBatchResponse{}, nil +} + +func (s *recordingSpeculativeBatchStream) CloseSend() error { + s.closeSendCalled = true + return nil +} diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go new file mode 100644 index 00000000..474b3736 --- /dev/null +++ b/relay/explicit_state_plan_test.go @@ -0,0 +1,1258 @@ +package relay + +import ( + "context" + "encoding/binary" + "fmt" + "io" + "math/big" + "net" + "testing" + "time" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/std" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + tmclienttypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" + "github.com/datachainlab/lcp-go/relay/elc" + "github.com/datachainlab/lcp-go/relay/elcupdater" + "github.com/datachainlab/lcp-go/relay/enclave" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/hyperledger-labs/yui-relayer/core" + ylog "github.com/hyperledger-labs/yui-relayer/log" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "google.golang.org/grpc/test/bufconn" +) + +type explicitStateBatchTestService interface { + SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error +} + +type explicitStateFallbackTestServer struct { + elc.UnimplementedQueryServer + elc.UnimplementedMsgServer + batchCalls int + updateCalls int +} + +func (s *explicitStateFallbackTestServer) SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + s.batchCalls++ + return status.Error(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented") +} + +func (s *explicitStateFallbackTestServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { + for { + chunk, err := stream.Recv() + if err == io.EOF { + s.updateCalls++ + return stream.SendAndClose(&elc.MsgUpdateClientResponse{ + Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(10+s.updateCalls), byte(s.updateCalls)), + Signature: []byte(fmt.Sprintf("sig-%d", s.updateCalls-1)), + }) + } + if err != nil { + return err + } + if chunk == nil { + return fmt.Errorf("received nil update client stream chunk") + } + } +} + +type explicitStateBatchMultiRequestServer struct { + elc.UnimplementedMsgServer + captured *[]*ExecuteSpeculativeUpdateClientBatchRequest +} + +func (s explicitStateBatchMultiRequestServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + *s.captured = append(*s.captured, req) + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) + for i := range req.Units { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: elc.MsgUpdateClientResponse{ + Message: []byte(fmt.Sprintf("msg-%s", req.Units[i].UnitId)), + Signature: []byte(fmt.Sprintf("sig-%d", i)), + }, + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: req.ClientId, + Units: units, + }) +} + +type explicitStateSourceUnitStreamingObserveServer struct { + elc.UnimplementedMsgServer + firstUnitEnd chan struct{} +} + +func (s explicitStateSourceUnitStreamingObserveServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + unitCount := 0 + for { + chunk, err := stream.Recv() + if err != nil { + return err + } + switch c := chunk.GetChunk().(type) { + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd: + unitCount++ + if c.UnitEnd.UnitId == "unit-0000" { + close(s.firstUnitEnd) + } + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd: + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, unitCount) + for i := 0; i < unitCount; i++ { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: elc.MsgUpdateClientResponse{ + Message: []byte(fmt.Sprintf("msg-%d", i)), + Signature: []byte(fmt.Sprintf("sig-%d", i)), + }, + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: "07-tendermint-11", + Units: units, + }) + } + } +} + +type explicitStateSplitBoundaryServer struct { + elc.UnimplementedQueryServer + elc.UnimplementedMsgServer + batchCalls int + batchUnitCounts []int + updateCalls int +} + +func (s *explicitStateSplitBoundaryServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + s.batchCalls++ + s.batchUnitCounts = append(s.batchUnitCounts, len(req.Units)) + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) + for i := range req.Units { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: elc.MsgUpdateClientResponse{ + Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(11+i), byte(i)), + Signature: []byte(fmt.Sprintf("batch-sig-%d", i)), + }, + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: req.ClientId, + Units: units, + }) +} + +func (s *explicitStateSplitBoundaryServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { + for { + chunk, err := stream.Recv() + if err == io.EOF { + s.updateCalls++ + return stream.SendAndClose(&elc.MsgUpdateClientResponse{ + Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(20+s.updateCalls), byte(10+s.updateCalls)), + Signature: []byte(fmt.Sprintf("serial-sig-%d", s.updateCalls)), + }) + } + if err != nil { + return err + } + if chunk == nil { + return fmt.Errorf("received nil update client stream chunk") + } + } +} + +type explicitStateIntegrationTestServer struct { + elc.UnimplementedQueryServer + elc.UnimplementedMsgServer + captured **ExecuteSpeculativeUpdateClientBatchRequest +} + +var ( + explicitStateTestHeaderedMessageABI, _ = abi.NewType("tuple", "struct HeaderedMessage", []abi.ArgumentMarshaling{ + {Name: "header", Type: "bytes32"}, + {Name: "message", Type: "bytes"}, + }) + explicitStateTestHeaderedContextABI, _ = abi.NewType("tuple", "struct HeaderedMessageContext", []abi.ArgumentMarshaling{ + {Name: "header", Type: "bytes32"}, + {Name: "context_bytes", Type: "bytes"}, + }) + explicitStateTestUpdateStateProxyMessageABI, _ = abi.NewType("tuple", "struct UpdateStateProxyMessage", []abi.ArgumentMarshaling{ + {Name: "prev_height", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "revision_number", Type: "uint64"}, + {Name: "revision_height", Type: "uint64"}, + }}, + {Name: "prev_state_id", Type: "bytes32"}, + {Name: "post_height", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "revision_number", Type: "uint64"}, + {Name: "revision_height", Type: "uint64"}, + }}, + {Name: "post_state_id", Type: "bytes32"}, + {Name: "timestamp", Type: "uint128"}, + {Name: "context", Type: "bytes"}, + {Name: "emitted_states", Type: "tuple[]", Components: []abi.ArgumentMarshaling{ + {Name: "height", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "revision_number", Type: "uint64"}, + {Name: "revision_height", Type: "uint64"}, + }}, + {Name: "state", Type: "bytes"}, + }}, + }) +) + +type explicitStateTestHeight struct { + RevisionNumber uint64 + RevisionHeight uint64 +} + +type explicitStateTestEmittedState struct { + Height explicitStateTestHeight + State []byte +} + +type explicitStateTestUpdateStateProxyMessage struct { + PrevHeight explicitStateTestHeight + PrevStateId [32]byte + PostHeight explicitStateTestHeight + PostStateId [32]byte + Timestamp *big.Int + Context []byte + EmittedStates []explicitStateTestEmittedState +} + +func mustMakeExplicitStateTestEmptyContext() []byte { + var header [32]byte + binary.BigEndian.PutUint16(header[:2], lcptypes.LCPMessageContextTypeEmpty) + bz, err := abi.Arguments{{Type: explicitStateTestHeaderedContextABI}}.Pack(struct { + Header [32]byte + ContextBytes []byte + }{ + Header: header, + ContextBytes: nil, + }) + if err != nil { + panic(err) + } + return bz +} + +func mustMakeExplicitStateTestHeaderedUpdateStateMessage(postHeight uint64, stateIDByte byte) []byte { + var postStateID [32]byte + postStateID[31] = stateIDByte + message, err := abi.Arguments{{Type: explicitStateTestUpdateStateProxyMessageABI}}.Pack(explicitStateTestUpdateStateProxyMessage{ + PostHeight: explicitStateTestHeight{ + RevisionNumber: 0, + RevisionHeight: postHeight, + }, + PostStateId: postStateID, + Timestamp: big.NewInt(1), + Context: mustMakeExplicitStateTestEmptyContext(), + EmittedStates: nil, + }) + if err != nil { + panic(err) + } + var header [32]byte + binary.BigEndian.PutUint16(header[:2], lcptypes.LCPMessageVersion) + binary.BigEndian.PutUint16(header[2:4], lcptypes.LCPMessageTypeUpdateState) + bz, err := abi.Arguments{{Type: explicitStateTestHeaderedMessageABI}}.Pack(struct { + Header [32]byte + Message []byte + }{ + Header: header, + Message: message, + }) + if err != nil { + panic(err) + } + return bz +} + +func (s explicitStateIntegrationTestServer) Client(_ context.Context, req *elc.QueryClientRequest) (*elc.QueryClientResponse, error) { + clientStateAny, err := clienttypes.PackClientState(&lcptypes.ClientState{ + LatestHeight: clienttypes.Height{RevisionHeight: 7}, + }) + if err != nil { + return nil, err + } + consensusStateAny, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{ + StateId: []byte("state-7"), + }) + if err != nil { + return nil, err + } + return &elc.QueryClientResponse{ + Found: req.ClientId == "07-tendermint-11", + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + }, nil +} + +func (s explicitStateIntegrationTestServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + *s.captured = req + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) + for i := range req.Units { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: elc.MsgUpdateClientResponse{ + Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(11+i), byte(i+1)), + Signature: []byte{byte('s'), byte('0' + i)}, + }, + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: req.ClientId, + Units: units, + }) +} + +func recvSpeculativeBatchStreamRequest(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) (*ExecuteSpeculativeUpdateClientBatchRequest, error) { + initChunk, err := stream.Recv() + if err != nil { + return nil, err + } + init := initChunk.GetInit() + if init == nil { + return nil, fmt.Errorf("first stream chunk must be init") + } + req := &ExecuteSpeculativeUpdateClientBatchRequest{ + ClientId: init.ClientId, + } + var openUnit *SpeculativeUpdateClientUnit + for { + chunk, err := stream.Recv() + if err != nil { + if err == io.EOF { + if openUnit != nil { + return nil, fmt.Errorf("unexpected EOF while unit %q is open", openUnit.UnitId) + } + break + } + return nil, err + } + switch c := chunk.GetChunk().(type) { + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit: + if openUnit != nil { + return nil, fmt.Errorf("received unit init while unit %q is open", openUnit.UnitId) + } + if c.UnitInit == nil { + return nil, fmt.Errorf("received nil unit init") + } + openUnit = &SpeculativeUpdateClientUnit{ + UnitId: c.UnitInit.UnitId, + Update: &elc.MsgUpdateClient{ + ClientId: init.ClientId, + Header: &codectypes.Any{TypeUrl: c.UnitInit.TypeUrl}, + IncludeState: c.UnitInit.IncludeState, + Signer: append([]byte(nil), c.UnitInit.Signer...), + }, + BaseState: decodeGeneratedExplicitStateRef(&c.UnitInit.BaseState), + } + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitHeaderChunk: + if openUnit == nil { + return nil, fmt.Errorf("received unit header chunk without open unit") + } + if c.UnitHeaderChunk == nil { + return nil, fmt.Errorf("received nil unit header chunk") + } + if c.UnitHeaderChunk.UnitId != openUnit.UnitId { + return nil, fmt.Errorf("unit header chunk id mismatch: open=%q chunk=%q", openUnit.UnitId, c.UnitHeaderChunk.UnitId) + } + openUnit.Update.Header.Value = append(openUnit.Update.Header.Value, c.UnitHeaderChunk.Data...) + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd: + if openUnit == nil { + return nil, fmt.Errorf("received unit end without open unit") + } + if c.UnitEnd == nil { + return nil, fmt.Errorf("received nil unit end") + } + if c.UnitEnd.UnitId != openUnit.UnitId { + return nil, fmt.Errorf("unit end id mismatch: open=%q end=%q", openUnit.UnitId, c.UnitEnd.UnitId) + } + req.Units = append(req.Units, openUnit) + openUnit = nil + case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd: + if openUnit != nil { + return nil, fmt.Errorf("received batch end while unit %q is open", openUnit.UnitId) + } + return req, nil + default: + return nil, fmt.Errorf("expected speculative batch unit chunk") + } + } + return req, nil +} + +func decodeGeneratedExplicitStateRef(ref *elc.ExplicitStateRef) *ExplicitStateRef { + if ref == nil { + return nil + } + return &ExplicitStateRef{ + PrevHeight: ref.PrevHeight, + PrevStateId: append([]byte(nil), ref.PrevStateId...), + ClientState: ref.ClientState, + ConsensusState: ref.ConsensusState, + } +} + +type fakeOriginProver struct { + headers []core.Header + explicitStateChunks []*ExplicitStateSourceHeaderUnit +} + +func (p fakeOriginProver) Init(string, time.Duration, codec.ProtoCodecMarshaler, bool) error { + return nil +} + +func (p fakeOriginProver) SetRelayInfo(*core.PathEnd, *core.ProvableChain, *core.PathEnd) error { + return nil +} + +func (p fakeOriginProver) SetupForRelay(context.Context) error { + return nil +} + +func (p fakeOriginProver) CreateInitialLightClientState(context.Context, ibcexported.Height) (ibcexported.ClientState, ibcexported.ConsensusState, error) { + return nil, nil, nil +} + +func (p fakeOriginProver) SetupHeadersForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *core.HeaderOrError, error) { + return core.MakeHeaderStream(p.headers...), nil +} + +func (p fakeOriginProver) SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) { + return makeExplicitStateSourceHeaderUnitStream(p.explicitStateChunks), nil +} + +func mustExplicitStateSourceUnitsFromHeaders(t *testing.T, headers ...core.Header) []*ExplicitStateSourceHeaderUnit { + t.Helper() + units, err := collectExplicitStateSourceHeaderUnitsForTest(core.MakeHeaderStream(headers...)) + if err != nil { + t.Fatalf("collectExplicitStateSourceHeaderUnitsForTest() error = %v", err) + } + return units +} + +func collectExplicitStateSourceHeaderUnitsForTest( + headerStream <-chan *core.HeaderOrError, +) ([]*ExplicitStateSourceHeaderUnit, error) { + var units []*ExplicitStateSourceHeaderUnit + i := 0 + for h := range headerStream { + unit, err := explicitStateSourceHeaderUnitFromStreamItemForTest(h, i) + if err != nil { + return nil, err + } + units = append(units, unit) + i += 1 + } + return units, nil +} + +func explicitStateSourceHeaderUnitFromStreamItemForTest( + h *core.HeaderOrError, + i int, +) (*ExplicitStateSourceHeaderUnit, error) { + if h == nil { + return nil, fmt.Errorf("received nil header stream item: i=%v", i) + } + if h.Error != nil { + return nil, fmt.Errorf("failed to setup a header for update: i=%v %w", i, h.Error) + } + if h.Header == nil { + return nil, fmt.Errorf("received nil header in header stream: i=%v", i) + } + anyHeader, err := clienttypes.PackClientMessage(h.Header) + if err != nil { + return nil, fmt.Errorf("failed to pack header: i=%v header=%v %w", i, h.Header, err) + } + return &ExplicitStateSourceHeaderUnit{ + Header: h.Header, + AnyHeader: anyHeader, + }, nil +} + +func mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t *testing.T, headers ...core.Header) []*ExplicitStateSourceHeaderUnit { + t.Helper() + units := mustExplicitStateSourceUnitsFromHeaders(t, headers...) + for i, unit := range units { + if unit == nil { + t.Fatalf("source unit[%d] is nil", i) + } + prevHeight := &clienttypes.Height{RevisionHeight: uint64(i + 10)} + unit.BaseState = &ExplicitStateRef{ + PrevHeight: prevHeight, + ClientState: &codectypes.Any{TypeUrl: fmt.Sprintf("client/%d", i), Value: []byte(fmt.Sprintf("client-%d", i))}, + ConsensusState: &codectypes.Any{TypeUrl: fmt.Sprintf("consensus/%d", i), Value: []byte(fmt.Sprintf("consensus-%d", i))}, + } + } + return units +} + +func mustPackTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) *codectypes.Any { + t.Helper() + anyHeader, err := codectypes.NewAnyWithValue(&tmclienttypes.Header{ + TrustedHeight: clienttypes.Height{RevisionHeight: trustedHeight}, + }) + if err != nil { + t.Fatalf("failed to pack tendermint header: %v", err) + } + return anyHeader +} + +func (p fakeOriginProver) CheckRefreshRequired(context.Context, core.ChainInfoICS02Querier) (bool, error) { + return false, nil +} + +func (p fakeOriginProver) GetLatestFinalizedHeader(context.Context) (core.Header, error) { + if len(p.headers) == 0 { + return nil, nil + } + return p.headers[len(p.headers)-1], nil +} + +func (p fakeOriginProver) ProveState(core.QueryContext, string, []byte) ([]byte, clienttypes.Height, error) { + return nil, clienttypes.Height{}, nil +} + +func (p fakeOriginProver) ProveHostConsensusState(core.QueryContext, ibcexported.Height, ibcexported.ConsensusState) ([]byte, error) { + return nil, nil +} + +func makeSpeculativeBatchTestUpdate(clientID string, signer []byte, index int) *elc.MsgUpdateClient { + return &elc.MsgUpdateClient{ + ClientId: clientID, + Header: &codectypes.Any{ + TypeUrl: "/test.Header", + Value: []byte(fmt.Sprintf("header-%d", index)), + }, + Signer: signer, + } +} + +func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUnits(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + listener := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + t.Cleanup(server.Stop) + + firstUnitEnd := make(chan struct{}) + observeServer := &explicitStateSourceUnitStreamingObserveServer{firstUnitEnd: firstUnitEnd} + elc.RegisterMsgServer(server, observeServer) + go func() { + _ = server.Serve(listener) + }() + + conn, err := grpc.NewClient( + "passthrough:///bufnet", + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), + ) + if err != nil { + t.Fatalf("grpc.NewClient() error = %v", err) + } + t.Cleanup(func() { _ = conn.Close() }) + + unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError) + done := make(chan error, 1) + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + lcpServiceClient: NewLCPServiceClient(conn), + } + go func() { + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + context.Background(), + unitStream, + "07-tendermint-11", + false, + []byte("signer"), + "test", + ) + if err != nil { + done <- err + return + } + if len(results) != 2 { + done <- fmt.Errorf("unexpected result count: %d", len(results)) + return + } + done <- nil + }() + + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 0).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/10", Value: []byte("client-10")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/10", Value: []byte("consensus-10")}, + }, + }} + select { + case <-firstUnitEnd: + case <-time.After(2 * time.Second): + t.Fatal("first unit was not streamed before the second source unit was provided") + } + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 1).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 11}, + ClientState: &codectypes.Any{TypeUrl: "client/11", Value: []byte("client-11")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/11", Value: []byte("consensus-11")}, + }, + }} + close(unitStream) + + if err := <-done; err != nil { + t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) + } +} + +func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUnit(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + listener := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + t.Cleanup(server.Stop) + + svc := &explicitStateSplitBoundaryServer{} + elc.RegisterQueryServer(server, svc) + elc.RegisterMsgServer(server, svc) + go func() { + _ = server.Serve(listener) + }() + + conn, err := grpc.NewClient( + "passthrough:///bufnet", + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), + ) + if err != nil { + t.Fatalf("grpc.NewClient() error = %v", err) + } + t.Cleanup(func() { _ = conn.Close() }) + + unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError, 3) + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 0).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/10", Value: []byte("client-10")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/10", Value: []byte("consensus-10")}, + }, + }} + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 1).Header, + BaseState: nil, + }} + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 2).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 12}, + ClientState: &codectypes.Any{TypeUrl: "client/12", Value: []byte("client-12")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/12", Value: []byte("consensus-12")}, + }, + }} + close(unitStream) + + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + lcpServiceClient: NewLCPServiceClient(conn), + } + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + context.Background(), + unitStream, + "07-tendermint-11", + false, + []byte("signer"), + "test", + ) + if err != nil { + t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) + } + if len(results) != 3 { + t.Fatalf("unexpected result count: %d", len(results)) + } + if svc.batchCalls != 2 { + t.Fatalf("expected speculative batches before and after nil base-state unit, got %d", svc.batchCalls) + } + if len(svc.batchUnitCounts) != 2 || svc.batchUnitCounts[0] != 1 || svc.batchUnitCounts[1] != 1 { + t.Fatalf("unexpected speculative batch unit counts: %#v", svc.batchUnitCounts) + } + if svc.updateCalls != 1 { + t.Fatalf("expected one serial update-client call for nil base-state unit, got %d", svc.updateCalls) + } +} + +func TestUpdateELCForUpdateClientKeepsTendermintHeadersOrdered(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + var captured *ExecuteSpeculativeUpdateClientBatchRequest + elc.RegisterQueryServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + elc.RegisterMsgServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + headers := []core.Header{ + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + } + explicitStateChunks := mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...) + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + explicitStateChunks: explicitStateChunks, + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + } + + results, err := pr.updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + headers[len(headers)-1], + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if captured == nil { + t.Fatal("expected speculative batch request to be captured") + } + if len(captured.Units) != 2 { + t.Fatalf("unexpected captured unit count: %d", len(captured.Units)) + } + if captured.ClientId != "07-tendermint-11" { + t.Fatalf("unexpected client id: %s", captured.ClientId) + } + if captured.Units[0].BaseState == nil || captured.Units[0].BaseState.PrevHeight == nil || !captured.Units[0].BaseState.PrevHeight.EQ(*explicitStateChunks[0].BaseState.PrevHeight) { + t.Fatalf("unexpected first base state: %#v", captured.Units[0].BaseState) + } + if captured.Units[1].BaseState == nil || captured.Units[1].BaseState.PrevHeight == nil || !captured.Units[1].BaseState.PrevHeight.EQ(*explicitStateChunks[1].BaseState.PrevHeight) { + t.Fatalf("unexpected second base state: %#v", captured.Units[1].BaseState) + } + if len(results) != 2 { + t.Fatalf("unexpected result count: %d", len(results)) + } + wantSigner := common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes() + for i, result := range results { + if result == nil { + t.Fatalf("result[%d] is nil", i) + } + msg, err := lcptypes.EthABIDecodeHeaderedProxyMessage(result.Message) + if err != nil { + t.Fatalf("result[%d] message decode error = %v", i, err) + } + updateStateMsg, err := msg.GetUpdateStateProxyMessage() + if err != nil { + t.Fatalf("result[%d] update-state decode error = %v", i, err) + } + if got, want := updateStateMsg.PostHeight.RevisionHeight, uint64(11+i); got != want { + t.Fatalf("unexpected result[%d] post height: got=%d want=%d", i, got, want) + } + if string(result.Signer) != string(wantSigner) { + t.Fatalf("unexpected result[%d] signer: %x", i, result.Signer) + } + } +} + +func TestUpdateELCForEnclaveKeyUpdateUsesSpeculativeBatchStream(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + var captured *ExecuteSpeculativeUpdateClientBatchRequest + elc.RegisterQueryServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + elc.RegisterMsgServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + headers := []core.Header{ + &tmclienttypes.Header{ + TrustedHeight: clienttypes.Height{RevisionHeight: 10}, + SignedHeader: &tmproto.SignedHeader{Header: &tmproto.Header{Height: 11}}, + }, + &tmclienttypes.Header{ + TrustedHeight: clienttypes.Height{RevisionHeight: 10}, + SignedHeader: &tmproto.SignedHeader{Header: &tmproto.Header{Height: 12}}, + }, + } + baseStates := []*ExplicitStateRef{ + { + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/10", Value: []byte("client-10")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/10", Value: []byte("consensus-10")}, + }, + { + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/10b", Value: []byte("client-10b")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/10b", Value: []byte("consensus-10b")}, + }, + } + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + explicitStateChunks: []*ExplicitStateSourceHeaderUnit{ + { + AnyHeader: mustPackTMHeaderForExplicitStateTest(t, 10), + BaseState: baseStates[0], + }, + { + AnyHeader: mustPackTMHeaderForExplicitStateTest(t, 10), + BaseState: baseStates[1], + }, + }, + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + } + + responses, err := pr.updateELC(context.Background(), "07-tendermint-11", true) + if err != nil { + t.Fatalf("updateELC() error = %v", err) + } + if captured == nil { + t.Fatal("expected speculative batch request to be captured") + } + if captured.ClientId != "07-tendermint-11" { + t.Fatalf("unexpected client id: %s", captured.ClientId) + } + if len(captured.Units) != 2 { + t.Fatalf("unexpected captured unit count: %d", len(captured.Units)) + } + for i, unit := range captured.Units { + if unit.Update == nil { + t.Fatalf("unit[%d] update is nil", i) + } + if !unit.Update.IncludeState { + t.Fatalf("unit[%d] include_state is false", i) + } + if unit.BaseState == nil || unit.BaseState.ClientState == nil || unit.BaseState.ConsensusState == nil { + t.Fatalf("unit[%d] missing embedded base state: %#v", i, unit.BaseState) + } + } + if len(responses) != 2 { + t.Fatalf("unexpected response count: %d", len(responses)) + } +} + +func TestUpdateELCForUpdateClientSingleHeaderStaysSingleUnitBatch(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + var captured *ExecuteSpeculativeUpdateClientBatchRequest + elc.RegisterQueryServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + elc.RegisterMsgServer(server, &explicitStateIntegrationTestServer{captured: &captured}) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + headers := []core.Header{ + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + } + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + } + + results, err := pr.updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + headers[0], + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if captured == nil { + t.Fatal("expected speculative batch request to be captured") + } + if len(captured.Units) != 1 { + t.Fatalf("unexpected captured unit count: %d", len(captured.Units)) + } + if len(results) != 1 { + t.Fatalf("unexpected result count: %d", len(results)) + } +} + +func TestUpdateELCForUpdateClientFallsBackToSerialWhenBatchRPCUnavailable(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + fallbackServer := &explicitStateFallbackTestServer{} + elc.RegisterQueryServer(server, fallbackServer) + elc.RegisterMsgServer(server, fallbackServer) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + anyHeader := mustPackTMHeaderForExplicitStateTest(t, 10) + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + codec: coreCodec, + originProver: fakeOriginProver{ + explicitStateChunks: []*ExplicitStateSourceHeaderUnit{ + { + AnyHeader: anyHeader, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/0", Value: []byte("client")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/0", Value: []byte("consensus")}, + }, + }, + }, + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + } + + results, err := pr.updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if fallbackServer.batchCalls != 1 { + t.Fatalf("expected speculative batch to be attempted once, got %d", fallbackServer.batchCalls) + } + if fallbackServer.updateCalls != 1 { + t.Fatalf("expected serial update-client fallback to be used once, got %d", fallbackServer.updateCalls) + } + if len(results) != 1 { + t.Fatalf("unexpected result count: %d", len(results)) + } + if _, err := lcptypes.EthABIDecodeHeaderedProxyMessage(results[0].Message); err != nil { + t.Fatalf("result message decode error = %v", err) + } +} + +func TestUpdateELCForUpdateClientDisablesExplicitStateWhenConfigDisabled(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + fallbackServer := &explicitStateFallbackTestServer{} + elc.RegisterQueryServer(server, fallbackServer) + elc.RegisterMsgServer(server, fallbackServer) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + header := &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}} + pr := &Prover{ + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + DisableExplicitStateUpdateClient: true, + }, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: []core.Header{header}, + explicitStateChunks: mustExplicitStateSourceUnitsFromHeaders(t, header), + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + } + + results, err := pr.updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + header, + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if fallbackServer.batchCalls != 0 { + t.Fatalf("expected speculative batch to be disabled, got %d calls", fallbackServer.batchCalls) + } + if fallbackServer.updateCalls != 1 { + t.Fatalf("expected serial update-client to be used once, got %d", fallbackServer.updateCalls) + } + if len(results) != 1 { + t.Fatalf("unexpected result count: %d", len(results)) + } +} + +func TestShouldLogSerialUpdateClientFallbackSuppressesUnimplemented(t *testing.T) { + err := fmt.Errorf( + "failed explicit-state update client batch: %w", + status.Error(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented"), + ) + + if !shouldFallbackToSerialUpdateClient(err) { + t.Fatal("expected Unimplemented to trigger serial fallback") + } + if shouldLogSerialUpdateClientFallback(err) { + t.Fatal("expected Unimplemented fallback log to be suppressed") + } +} + +func TestShouldFallbackToSerialUpdateClientAllowsBaseStateMismatch(t *testing.T) { + err := fmt.Errorf( + "failed explicit-state update client batch: %w", + status.Error(codes.Aborted, "BaseStateMismatch: base prev_height mismatch: expected=10 observed=11"), + ) + + if !shouldFallbackToSerialUpdateClient(err) { + t.Fatal("expected BaseStateMismatch to trigger serial fallback") + } + if !shouldLogSerialUpdateClientFallback(err) { + t.Fatal("expected BaseStateMismatch fallback log to be kept") + } +} + +func TestShouldFallbackToSerialUpdateClientAllowsDependencyStateMismatch(t *testing.T) { + err := fmt.Errorf( + "failed explicit-state update client batch: %w", + status.Error(codes.Aborted, "DependencyStateMismatch: unit unit-0001 base state does not match dependency unit-0000 post state"), + ) + + if !shouldFallbackToSerialUpdateClient(err) { + t.Fatal("expected DependencyStateMismatch to trigger serial fallback") + } +} + +func TestShouldFallbackToSerialUpdateClientRejectsEOF(t *testing.T) { + err := fmt.Errorf("send failed: %w", io.EOF) + + if shouldFallbackToSerialUpdateClient(err) { + t.Fatal("expected EOF not to trigger serial fallback") + } +} + +func TestShouldFallbackToSerialUpdateClientRejectsConflictingWriteSet(t *testing.T) { + err := fmt.Errorf( + "failed explicit-state update client batch: %w", + status.Error(codes.Aborted, "ConflictingWriteSet: independent speculative units unit-0000 and unit-0001 write the same key clients/arbitrum-1/clientState"), + ) + + if shouldFallbackToSerialUpdateClient(err) { + t.Fatal("expected ConflictingWriteSet not to trigger serial fallback") + } +} + +func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvider(t *testing.T) { + expectedBaseState := &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 12}, + PrevStateId: []byte("state-12"), + } + expected := []*ExplicitStateSourceHeaderUnit{ + { + AnyHeader: mustPackTMHeaderForExplicitStateTest(t, 12), + BaseState: expectedBaseState, + }, + } + pr := &Prover{ + originProver: fakeOriginProver{ + explicitStateChunks: expected, + }, + } + + unitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 12}}, + ) + if err != nil { + t.Fatalf("collectExplicitStateChunkSourceHeaderUnitStreamForUpdate() error = %v", err) + } + if !ok { + t.Fatal("expected chunk provider to be used") + } + units, err := drainExplicitStateSourceHeaderUnitStream(unitStream) + if err != nil { + t.Fatalf("drainExplicitStateSourceHeaderUnitStream() error = %v", err) + } + if len(units) != 1 { + t.Fatalf("unexpected source unit count: %d", len(units)) + } + if units[0] != expected[0] { + t.Fatalf("expected chunk provider result to be used directly: %#v", units[0]) + } +} diff --git a/relay/explicit_state_update_client_test.go b/relay/explicit_state_update_client_test.go new file mode 100644 index 00000000..ab2257b8 --- /dev/null +++ b/relay/explicit_state_update_client_test.go @@ -0,0 +1,138 @@ +package relay + +import ( + "fmt" + "testing" + "time" + + gogoproto "github.com/cosmos/gogoproto/proto" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + tmclienttypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" +) + +func TestBuildExplicitStateRefFromCanonicalState(t *testing.T) { + ref, err := buildExplicitStateRefFromCanonicalState( + &lcptypes.ClientState{LatestHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 11}}, + &lcptypes.ConsensusState{StateId: []byte("post-0")}, + ) + if err != nil { + t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) + } + if ref.PrevHeight == nil || ref.PrevHeight.RevisionHeight != 11 { + t.Fatalf("unexpected prev height: %+v", ref.PrevHeight) + } + if string(ref.PrevStateId) != "post-0" { + t.Fatalf("unexpected prev state id: %q", string(ref.PrevStateId)) + } +} + +func TestBuildExplicitStateRefFromCanonicalStateZeroHeight(t *testing.T) { + ref, err := buildExplicitStateRefFromCanonicalState( + &lcptypes.ClientState{}, + &lcptypes.ConsensusState{}, + ) + if err != nil { + t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) + } + if ref.PrevHeight != nil { + t.Fatalf("expected nil prev height for zero canonical height: %+v", ref.PrevHeight) + } + if len(ref.PrevStateId) != 0 { + t.Fatalf("expected empty prev state id: %x", ref.PrevStateId) + } +} + +func TestBuildExplicitStateRefFromCanonicalStateTendermint(t *testing.T) { + clientState := tmclienttypes.NewClientState( + "ibc0", + tmclienttypes.DefaultTrustLevel, + 14*24*time.Hour, + 21*24*time.Hour, + 10*time.Second, + clienttypes.NewHeight(0, 21), + nil, + []string{"upgrade", "upgradedIBCState"}, + ) + consensusState := tmclienttypes.NewConsensusState( + time.Unix(1773717522, 0), + commitmenttypes.NewMerkleRoot([]byte("apphash")), + []byte("next-validators"), + ) + + ref, err := buildExplicitStateRefFromCanonicalState(clientState, consensusState) + if err != nil { + t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) + } + if ref.PrevHeight == nil || ref.PrevHeight.RevisionHeight != 21 { + t.Fatalf("unexpected prev height: %+v", ref.PrevHeight) + } + if len(ref.PrevStateId) != 0 { + t.Fatalf("expected tendermint explicit state ref to omit prev state id: %x", ref.PrevStateId) + } +} + +func TestShouldUseExplicitStateUpdateClientUsesProverConfig(t *testing.T) { + if got := (&Prover{}).shouldUseExplicitStateUpdateClient(); !got { + t.Fatal("expected explicit-state update client to be enabled by default") + } + + pr := &Prover{ + config: ProverConfig{DisableExplicitStateUpdateClient: true}, + } + if got := pr.shouldUseExplicitStateUpdateClient(); got { + t.Fatal("expected explicit-state update client to be disabled by config") + } +} + +func TestProverConfigDisableExplicitStateUpdateClientRoundTrip(t *testing.T) { + bz, err := gogoproto.Marshal(&ProverConfig{DisableExplicitStateUpdateClient: true}) + if err != nil { + t.Fatalf("Marshal() error = %v", err) + } + + var got ProverConfig + if err := gogoproto.Unmarshal(bz, &got); err != nil { + t.Fatalf("Unmarshal() error = %v", err) + } + if !got.DisableExplicitStateUpdateClient { + t.Fatal("expected disable_explicit_state_update_client to round-trip") + } +} + +func buildExplicitStateRefFromCanonicalState( + clientState ibcexported.ClientState, + consensusState ibcexported.ConsensusState, +) (*ExplicitStateRef, error) { + ref := &ExplicitStateRef{} + if clientState == nil { + return nil, fmt.Errorf("client state must not be nil") + } + latestHeight, ok := clientState.GetLatestHeight().(clienttypes.Height) + if !ok { + return nil, fmt.Errorf("unsupported latest height type for explicit state ref: %T", clientState.GetLatestHeight()) + } + if !latestHeight.IsZero() { + h := latestHeight + ref.PrevHeight = &h + } + + if cs, ok := consensusState.(*lcptypes.ConsensusState); ok && cs != nil && len(cs.StateId) > 0 { + ref.PrevStateId = append([]byte(nil), cs.StateId...) + } + + anyClientState, err := clienttypes.PackClientState(clientState) + if err != nil { + return nil, fmt.Errorf("failed to pack client state for explicit state payload: %w", err) + } + ref.ClientState = anyClientState + + anyConsensusState, err := clienttypes.PackConsensusState(consensusState) + if err != nil { + return nil, fmt.Errorf("failed to pack consensus state for explicit state ref: %w", err) + } + ref.ConsensusState = anyConsensusState + return ref, nil +} From 6b0d62ecc24ac7f380c8271d97543db1fc3d3c4a Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 14 May 2026 13:48:24 +0900 Subject: [PATCH 05/29] relay: address speculative update review --- relay/config.go | 7 ++--- relay/explicit_state_update_client.go | 38 +++++++++++---------------- relay/prover.go | 10 ++++--- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/relay/config.go b/relay/config.go index 87015536..8823d89b 100644 --- a/relay/config.go +++ b/relay/config.go @@ -22,7 +22,8 @@ const ( // It is necessary to subtract from 4 MB to account for metadata size. DefaultMaxChunkSize = 4*1024*1024 - 1024 MaxSpeculativeBatchHeaderChunkSize = DefaultMaxChunkSize - DefaultMaxSpeculativeBatchUnits = 256 + MaxSpeculativeBatchUnitsLimit = 256 + DefaultMaxSpeculativeBatchUnits = MaxSpeculativeBatchUnitsLimit ) var _ core.ProverConfig = (*ProverConfig)(nil) @@ -129,8 +130,8 @@ func (pc ProverConfig) Validate() error { if pc.MaxChunkSizeForUpdateClient > MaxSpeculativeBatchHeaderChunkSize { return fmt.Errorf("MaxChunkSizeForUpdateClient must be less than or equal to %d", MaxSpeculativeBatchHeaderChunkSize) } - if pc.MaxSpeculativeBatchUnitsPerRequest > DefaultMaxSpeculativeBatchUnits { - return fmt.Errorf("MaxSpeculativeBatchUnitsPerRequest must be less than or equal to %d", DefaultMaxSpeculativeBatchUnits) + if pc.MaxSpeculativeBatchUnitsPerRequest > MaxSpeculativeBatchUnitsLimit { + return fmt.Errorf("MaxSpeculativeBatchUnitsPerRequest must be less than or equal to %d", MaxSpeculativeBatchUnitsLimit) } if pc.KeyUpdateBufferTime == 0 { return fmt.Errorf("KeyUpdateBufferTime must be greater than 0") diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 64fb8476..d55b162d 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -2,7 +2,6 @@ package relay import ( "context" - "crypto/sha256" "fmt" "github.com/datachainlab/lcp-go/relay/elc" @@ -28,7 +27,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( signer []byte, ) ([]*elcupdater_storage.UpdateClientResult, []*ExplicitStateSourceHeaderUnit, error) { var results []*elcupdater_storage.UpdateClientResult - var sourceHeaderUnits []*ExplicitStateSourceHeaderUnit + var fallbackUnits []*ExplicitStateSourceHeaderUnit maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() var sender *speculativeBatchStreamSender @@ -98,12 +97,12 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( for item := range unitStream { sourceUnit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, unitIndex) if err != nil { - return nil, sourceHeaderUnits, err + return nil, fallbackUnits, err } if sourceUnit.BaseState == nil { - sourceHeaderUnits = append(sourceHeaderUnits, sourceUnit) + fallbackUnits = append(fallbackUnits, sourceUnit) if err := flushBatch(); err != nil { - return nil, sourceHeaderUnits, err + return results, fallbackUnits, err } serialResults, err := pr.executeELCUpdateHeaderUnits( ctx, @@ -113,19 +112,14 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( signer, ) if err != nil { - return nil, sourceHeaderUnits, err + return results, fallbackUnits, err } results = append(results, serialResults...) + fallbackUnits = fallbackUnits[:0] unitIndex++ continue } - sourceHeaderUnits = append(sourceHeaderUnits, sourceUnit) - if sourceUnit.AnyHeader == nil { - return nil, sourceHeaderUnits, fmt.Errorf("explicit-state source header unit[%d] missing packed header", unitIndex) - } - if sourceUnit.BaseState == nil { - return nil, sourceHeaderUnits, fmt.Errorf("explicit-state source header unit[%d] missing base state", unitIndex) - } + fallbackUnits = append(fallbackUnits, sourceUnit) baseState := cloneExplicitStateRef(sourceUnit.BaseState) unitID := buildSpeculativeUnitID(unitIndex) @@ -136,14 +130,14 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( Signer: signer, } if sender == nil && unitIndex > 0 && !hasCanonicalExplicitStatePayload(baseState) { - return nil, sourceHeaderUnits, fmt.Errorf( + return results, fallbackUnits, fmt.Errorf( "cannot split explicit-state batch at unit %s: missing base state payload", unitID, ) } if err := openBatch(); err != nil { - return nil, sourceHeaderUnits, err + return results, fallbackUnits, err } logExplicitStateUnitSend(ctx, pr, elcClientID, unitID, batchIndex, len(batchSigners), unitIndex, includeState, update) if err := sender.Send(&SpeculativeUpdateClientUnit{ @@ -152,21 +146,23 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( BaseState: baseState, }); err != nil { err, _ = sender.enrichSendError(err) - return nil, sourceHeaderUnits, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) + return results, fallbackUnits, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) } batchSigners = append(batchSigners, update.Signer) unitIndex++ if len(batchSigners) == maxUnits { if err := flushBatch(); err != nil { - return nil, sourceHeaderUnits, err + return results, fallbackUnits, err } + fallbackUnits = fallbackUnits[:0] } } if err := flushBatch(); err != nil { - return nil, sourceHeaderUnits, err + return results, fallbackUnits, err } - return results, sourceHeaderUnits, nil + fallbackUnits = fallbackUnits[:0] + return results, fallbackUnits, nil } func logExplicitStateUnitSend( @@ -181,11 +177,8 @@ func logExplicitStateUnitSend( update *elc.MsgUpdateClient, ) { headerBytes := 0 - headerSHA256 := "" if update != nil && update.Header != nil { headerBytes = len(update.Header.Value) - headerHash := sha256.Sum256(update.Header.Value) - headerSHA256 = fmt.Sprintf("%x", headerHash) } pr.getLogger().InfoContext( ctx, @@ -197,6 +190,5 @@ func logExplicitStateUnitSend( "unit_index", unitIndex, "include_state", includeState, "header_bytes", headerBytes, - "header_sha256", headerSHA256, ) } diff --git a/relay/prover.go b/relay/prover.go index 1e6ea222..95e3d7b5 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -429,7 +429,7 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( signer []byte, operation string, ) ([]*elcupdater_storage.UpdateClientResult, error) { - results, sourceHeaderUnits, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( + results, fallbackUnits, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( ctx, sourceHeaderUnitStream, elcClientID, @@ -455,8 +455,12 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( if collectErr != nil { return nil, collectErr } - sourceHeaderUnits = append(sourceHeaderUnits, remainingUnits...) - return pr.executeELCUpdateHeaderUnits(ctx, sourceHeaderUnits, elcClientID, includeState, signer) + fallbackUnits = append(fallbackUnits, remainingUnits...) + serialResults, serialErr := pr.executeELCUpdateHeaderUnits(ctx, fallbackUnits, elcClientID, includeState, signer) + if serialErr != nil { + return nil, serialErr + } + return append(results, serialResults...), nil } const ( From ed3dbacda3afb5f3f3073cc6f5b9438f0c4e462c Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 14 May 2026 14:05:32 +0900 Subject: [PATCH 06/29] relay: clear processed fallback units --- relay/explicit_state_update_client.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index d55b162d..212045d2 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -19,6 +19,11 @@ func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { baseState.ConsensusState != nil } +func clearExplicitStateSourceHeaderUnits(units []*ExplicitStateSourceHeaderUnit) []*ExplicitStateSourceHeaderUnit { + clear(units) + return units[:0] +} + func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( ctx context.Context, unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, @@ -115,7 +120,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( return results, fallbackUnits, err } results = append(results, serialResults...) - fallbackUnits = fallbackUnits[:0] + fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) unitIndex++ continue } @@ -155,13 +160,13 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if err := flushBatch(); err != nil { return results, fallbackUnits, err } - fallbackUnits = fallbackUnits[:0] + fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) } } if err := flushBatch(); err != nil { return results, fallbackUnits, err } - fallbackUnits = fallbackUnits[:0] + fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) return results, fallbackUnits, nil } From 7ec481d6dce0f0023100260cb88a6f10ce2f2f91 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 14 May 2026 14:51:21 +0900 Subject: [PATCH 07/29] relay: bound speculative fallback retention --- relay/config.go | 24 ++++++ relay/explicit_state_plan_test.go | 94 +++++++++++++++++++++ relay/explicit_state_source_header_units.go | 27 +++++- relay/explicit_state_update_client.go | 34 ++++++++ 4 files changed, 176 insertions(+), 3 deletions(-) diff --git a/relay/config.go b/relay/config.go index 8823d89b..9a3c3370 100644 --- a/relay/config.go +++ b/relay/config.go @@ -24,6 +24,12 @@ const ( MaxSpeculativeBatchHeaderChunkSize = DefaultMaxChunkSize MaxSpeculativeBatchUnitsLimit = 256 DefaultMaxSpeculativeBatchUnits = MaxSpeculativeBatchUnitsLimit + // DefaultMaxSpeculativeBatchBytes caps the per-batch in-flight header + // payload that the relayer retains for fallback. The streaming worker + // flushes the current batch whenever appending another unit would exceed + // this limit, which keeps peak memory bounded even when individual + // upstream headers (for example Arbitrum size-capped chunks) are large. + DefaultMaxSpeculativeBatchBytes = 192 * 1024 * 1024 ) var _ core.ProverConfig = (*ProverConfig)(nil) @@ -110,6 +116,24 @@ func (pc ProverConfig) GetMaxSpeculativeBatchUnitsPerRequest() int { return int(pc.MaxSpeculativeBatchUnitsPerRequest) } +// speculativeBatchBytesPerRequestOverride lets tests dial the per-batch byte +// cap down to a value compatible with synthetic header sizes. Production code +// must not set this; the zero value yields the production default. +var speculativeBatchBytesPerRequestOverride int + +// GetMaxSpeculativeBatchBytesPerRequest returns the per-batch in-flight header +// payload cap. The streaming worker flushes the current speculative batch +// before adding another unit when the accumulated header bytes would exceed +// this value, bounding fallback retention memory independent of unit count. +func (pc ProverConfig) GetMaxSpeculativeBatchBytesPerRequest() int { + if speculativeBatchBytesPerRequestOverride > 0 { + return speculativeBatchBytesPerRequestOverride + } + // Reserved for a future proto-level setting; until then, the default + // captures the operational target on its own. + return DefaultMaxSpeculativeBatchBytes +} + func (pc ProverConfig) Validate() error { // origin prover config validation if err := pc.OriginProver.GetCachedValue().(core.ProverConfig).Validate(); err != nil { diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 474b3736..d98202ec 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -519,6 +519,22 @@ func mustPackTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) *c return anyHeader } +// mustBuildTMHeaderForExplicitStateTest returns both the typed header and its +// packed Any form. Use it for tests that exercise the serial fallback path, +// because the streaming worker clears the packed Any after a successful Send +// and the fallback repacks from the typed header. +func mustBuildTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) (*tmclienttypes.Header, *codectypes.Any) { + t.Helper() + header := &tmclienttypes.Header{ + TrustedHeight: clienttypes.Height{RevisionHeight: trustedHeight}, + } + anyHeader, err := codectypes.NewAnyWithValue(header) + if err != nil { + t.Fatalf("failed to pack tendermint header: %v", err) + } + return header, anyHeader +} + func (p fakeOriginProver) CheckRefreshRequired(context.Context, core.ChainInfoICS02Querier) (bool, error) { return false, nil } @@ -711,6 +727,84 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUni } } +func TestExecuteExplicitStateSourceHeaderUnitStreamFlushesBatchOnByteBudget(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + // Synthetic headers in this test are 8 bytes each ("header-N"). A budget of + // 12 bytes permits one unit per batch and forces a flush before the second. + t.Cleanup(func() { speculativeBatchBytesPerRequestOverride = 0 }) + speculativeBatchBytesPerRequestOverride = 12 + + listener := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + t.Cleanup(server.Stop) + + svc := &explicitStateSplitBoundaryServer{} + elc.RegisterQueryServer(server, svc) + elc.RegisterMsgServer(server, svc) + go func() { + _ = server.Serve(listener) + }() + + conn, err := grpc.NewClient( + "passthrough:///bufnet", + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), + ) + if err != nil { + t.Fatalf("grpc.NewClient() error = %v", err) + } + t.Cleanup(func() { _ = conn.Close() }) + + unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError, 2) + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 0).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, + ClientState: &codectypes.Any{TypeUrl: "client/10", Value: []byte("client-10")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/10", Value: []byte("consensus-10")}, + }, + }} + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 1).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 11}, + ClientState: &codectypes.Any{TypeUrl: "client/11", Value: []byte("client-11")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/11", Value: []byte("consensus-11")}, + }, + }} + close(unitStream) + + pr := &Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + lcpServiceClient: NewLCPServiceClient(conn), + } + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + context.Background(), + unitStream, + "07-tendermint-11", + false, + []byte("signer"), + "test", + ) + if err != nil { + t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) + } + if len(results) != 2 { + t.Fatalf("unexpected result count: %d", len(results)) + } + if svc.batchCalls != 2 { + t.Fatalf("expected byte budget to force two batches, got batch_calls=%d", svc.batchCalls) + } + if len(svc.batchUnitCounts) != 2 || svc.batchUnitCounts[0] != 1 || svc.batchUnitCounts[1] != 1 { + t.Fatalf("expected byte budget to keep batches at one unit each, got counts=%#v", svc.batchUnitCounts) + } +} + func TestUpdateELCForUpdateClientKeepsTendermintHeadersOrdered(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go index 254a6405..d627eda9 100644 --- a/relay/explicit_state_source_header_units.go +++ b/relay/explicit_state_source_header_units.go @@ -4,6 +4,7 @@ import ( "fmt" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/hyperledger-labs/yui-relayer/core" ) @@ -72,10 +73,30 @@ func extractAnyHeadersFromSourceUnits( if unit == nil { return nil, fmt.Errorf("source header unit must not be nil: i=%v", i) } - if unit.AnyHeader == nil { - return nil, fmt.Errorf("source header unit missing packed header: i=%v", i) + anyHeader, err := ensureAnyHeaderForSourceUnit(unit) + if err != nil { + return nil, fmt.Errorf("source header unit at i=%v: %w", i, err) } - anyHeaders = append(anyHeaders, unit.AnyHeader) + anyHeaders = append(anyHeaders, anyHeader) } return anyHeaders, nil } + +// ensureAnyHeaderForSourceUnit returns the packed AnyHeader for a source unit, +// repacking from unit.Header if the packed form was released after a successful +// speculative Send. The repacked AnyHeader is cached back into the unit so a +// subsequent fallback iteration does not pay the encoding cost twice. +func ensureAnyHeaderForSourceUnit(unit *ExplicitStateSourceHeaderUnit) (*codectypes.Any, error) { + if unit.AnyHeader != nil { + return unit.AnyHeader, nil + } + if unit.Header == nil { + return nil, fmt.Errorf("source header unit missing packed header and core header") + } + anyHeader, err := clienttypes.PackClientMessage(unit.Header) + if err != nil { + return nil, fmt.Errorf("failed to repack source header from core header: %w", err) + } + unit.AnyHeader = anyHeader + return anyHeader, nil +} diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 212045d2..351390a0 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -35,11 +35,13 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( var fallbackUnits []*ExplicitStateSourceHeaderUnit maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() + maxBatchBytes := pr.config.GetMaxSpeculativeBatchBytesPerRequest() var sender *speculativeBatchStreamSender closed := true batchSigners := make([][]byte, 0, maxUnits) batchIndex := 0 unitIndex := 0 + batchBytes := 0 defer func() { if !closed && sender != nil { _ = sender.CloseSend() @@ -96,6 +98,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( sender = nil batchSigners = batchSigners[:0] batchIndex++ + batchBytes = 0 return nil } @@ -124,6 +127,27 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( unitIndex++ continue } + headerBytes := 0 + if sourceUnit.AnyHeader != nil { + headerBytes = len(sourceUnit.AnyHeader.Value) + } + // Flush the in-flight batch before appending a unit whose header would + // push aggregate retention past the byte cap. The retention is bounded + // per batch (cleared in flushBatch's success path), so this caps peak + // fallback memory by batch boundary rather than maxUnits * header_size. + if sender != nil && headerBytes > 0 && batchBytes+headerBytes > maxBatchBytes { + if !hasCanonicalExplicitStatePayload(sourceUnit.BaseState) { + return results, fallbackUnits, fmt.Errorf( + "cannot split explicit-state batch at unit %s by byte budget: missing base state payload (batch_bytes=%d header_bytes=%d budget=%d)", + buildSpeculativeUnitID(unitIndex), batchBytes, headerBytes, maxBatchBytes, + ) + } + if err := flushBatch(); err != nil { + return results, fallbackUnits, err + } + fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) + } + fallbackUnits = append(fallbackUnits, sourceUnit) baseState := cloneExplicitStateRef(sourceUnit.BaseState) @@ -153,7 +177,17 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( err, _ = sender.enrichSendError(err) return results, fallbackUnits, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) } + // The gRPC layer has already marshalled and queued the header bytes, so + // release our in-process copy when the typed header is available for + // serial fallback repacking. Providers may supply only AnyHeader; keep it + // in that case because ensureAnyHeaderForSourceUnit cannot repack without + // sourceUnit.Header. + if sourceUnit.Header != nil { + sourceUnit.AnyHeader = nil + } + update.Header = nil batchSigners = append(batchSigners, update.Signer) + batchBytes += headerBytes unitIndex++ if len(batchSigners) == maxUnits { From 09b164eea5a740635f8009d6e47b4274dd5401d3 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Fri, 22 May 2026 18:19:38 +0900 Subject: [PATCH 08/29] Fallback incomplete non-leading speculative base states --- relay/explicit_state_plan_test.go | 24 +++++++++++++++--------- relay/explicit_state_update_client.go | 9 +-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index d98202ec..429629db 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -647,7 +647,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn } } -func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUnit(t *testing.T) { +func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesNilAndIncompleteNonLeadingBaseStateUnits(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) } @@ -675,7 +675,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUni } t.Cleanup(func() { _ = conn.Close() }) - unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError, 3) + unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError, 4) unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 0).Header, BaseState: &ExplicitStateRef{ @@ -691,9 +691,15 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUni unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 2).Header, BaseState: &ExplicitStateRef{ - PrevHeight: &clienttypes.Height{RevisionHeight: 12}, - ClientState: &codectypes.Any{TypeUrl: "client/12", Value: []byte("client-12")}, - ConsensusState: &codectypes.Any{TypeUrl: "consensus/12", Value: []byte("consensus-12")}, + PrevHeight: &clienttypes.Height{RevisionHeight: 12}, + }, + }} + unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 3).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 13}, + ClientState: &codectypes.Any{TypeUrl: "client/13", Value: []byte("client-13")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus/13", Value: []byte("consensus-13")}, }, }} close(unitStream) @@ -713,17 +719,17 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesOnlyNilBaseStateUni if err != nil { t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) } - if len(results) != 3 { + if len(results) != 4 { t.Fatalf("unexpected result count: %d", len(results)) } if svc.batchCalls != 2 { - t.Fatalf("expected speculative batches before and after nil base-state unit, got %d", svc.batchCalls) + t.Fatalf("expected speculative batches around serial base-state units, got %d", svc.batchCalls) } if len(svc.batchUnitCounts) != 2 || svc.batchUnitCounts[0] != 1 || svc.batchUnitCounts[1] != 1 { t.Fatalf("unexpected speculative batch unit counts: %#v", svc.batchUnitCounts) } - if svc.updateCalls != 1 { - t.Fatalf("expected one serial update-client call for nil base-state unit, got %d", svc.updateCalls) + if svc.updateCalls != 2 { + t.Fatalf("expected two serial update-client calls for nil/incomplete base-state units, got %d", svc.updateCalls) } } diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 351390a0..b4da1b30 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -107,7 +107,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if err != nil { return nil, fallbackUnits, err } - if sourceUnit.BaseState == nil { + if sourceUnit.BaseState == nil || (unitIndex > 0 && !hasCanonicalExplicitStatePayload(sourceUnit.BaseState)) { fallbackUnits = append(fallbackUnits, sourceUnit) if err := flushBatch(); err != nil { return results, fallbackUnits, err @@ -158,13 +158,6 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( IncludeState: includeState, Signer: signer, } - if sender == nil && unitIndex > 0 && !hasCanonicalExplicitStatePayload(baseState) { - return results, fallbackUnits, fmt.Errorf( - "cannot split explicit-state batch at unit %s: missing base state payload", - unitID, - ) - } - if err := openBatch(); err != nil { return results, fallbackUnits, err } From 41745b5d5456f1fc3e81e05a86aecb0d654556f3 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Fri, 22 May 2026 18:22:45 +0900 Subject: [PATCH 09/29] Fallback incomplete speculative base states --- relay/explicit_state_plan_test.go | 2 +- relay/explicit_state_update_client.go | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 429629db..9d3b6e3e 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -647,7 +647,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn } } -func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesNilAndIncompleteNonLeadingBaseStateUnits(t *testing.T) { +func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesNilAndIncompleteBaseStateUnits(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) } diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index b4da1b30..9d85113a 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -107,7 +107,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if err != nil { return nil, fallbackUnits, err } - if sourceUnit.BaseState == nil || (unitIndex > 0 && !hasCanonicalExplicitStatePayload(sourceUnit.BaseState)) { + if !hasCanonicalExplicitStatePayload(sourceUnit.BaseState) { fallbackUnits = append(fallbackUnits, sourceUnit) if err := flushBatch(); err != nil { return results, fallbackUnits, err @@ -136,12 +136,6 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( // per batch (cleared in flushBatch's success path), so this caps peak // fallback memory by batch boundary rather than maxUnits * header_size. if sender != nil && headerBytes > 0 && batchBytes+headerBytes > maxBatchBytes { - if !hasCanonicalExplicitStatePayload(sourceUnit.BaseState) { - return results, fallbackUnits, fmt.Errorf( - "cannot split explicit-state batch at unit %s by byte budget: missing base state payload (batch_bytes=%d header_bytes=%d budget=%d)", - buildSpeculativeUnitID(unitIndex), batchBytes, headerBytes, maxBatchBytes, - ) - } if err := flushBatch(); err != nil { return results, fallbackUnits, err } From ca6dde17267240239778b8e8ee58c5bc10715f92 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Fri, 22 May 2026 18:32:03 +0900 Subject: [PATCH 10/29] Validate complete speculative unit base states --- relay/explicit_state_batch.go | 6 +++--- relay/explicit_state_batch_test.go | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/relay/explicit_state_batch.go b/relay/explicit_state_batch.go index 58d551bd..0dfb9b73 100644 --- a/relay/explicit_state_batch.go +++ b/relay/explicit_state_batch.go @@ -122,10 +122,10 @@ func sendSpeculativeUpdateClientUnit( if err := validateSpeculativeBatchStreamChunkSize(chunkSize); err != nil { return err } - baseState := cloneExplicitStateRef(unit.BaseState) - if baseState == nil { - return fmt.Errorf("unit base_state must not be nil") + if !hasCanonicalExplicitStatePayload(unit.BaseState) { + return fmt.Errorf("unit base_state must be complete") } + baseState := cloneExplicitStateRef(unit.BaseState) unitInitChunk := &elc.MsgSpeculativeUpdateClientBatchStreamChunk{ Chunk: &elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitInit{ diff --git a/relay/explicit_state_batch_test.go b/relay/explicit_state_batch_test.go index 05bf23c6..125cafee 100644 --- a/relay/explicit_state_batch_test.go +++ b/relay/explicit_state_batch_test.go @@ -6,6 +6,7 @@ import ( "testing" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/datachainlab/lcp-go/relay/elc" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -24,6 +25,7 @@ func TestSendSpeculativeUpdateClientUnitRejectsOversizedUnitInit(t *testing.T) { }, }, BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: 10}, ClientState: &codectypes.Any{ TypeUrl: "client", Value: make([]byte, int(MaxSpeculativeBatchHeaderChunkSize)), From 5f4819c2098a829058ae40ef6f5c1d02ae2263dc Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 4 Jun 2026 11:57:03 +0900 Subject: [PATCH 11/29] relay: address explicit-state update review - restore the ELC updater gRPC health service - hard-fail explicit-state speculative batch failures instead of falling back to serial update-client - cancel and discard-drain source streams on batch errors so unbuffered producers can exit - reject nil or incomplete explicit-state base states instead of serializing those units - remove unused explicit-state fallback/dead helper code and stale client connection storage - clarify speculative batch unit limit as the peer LCP service protocol limit - update tests for hard-fail behavior, blocked producer cleanup, explicit-vs-legacy parity, and removed fallback paths --- proto/relayer/provers/lcp/config/config.proto | 2 + relay/client.go | 2 - relay/config.go | 18 +- relay/elcupdater/module/service.go | 10 + relay/explicit_state_batch.go | 18 +- relay/explicit_state_batch_test.go | 10 +- relay/explicit_state_plan_test.go | 428 +++++++++++++----- relay/explicit_state_source_header_units.go | 52 +-- relay/explicit_state_update_client.go | 63 +-- relay/explicit_state_update_client_test.go | 103 ----- relay/lcp.go | 6 +- relay/prover.go | 129 +----- 12 files changed, 396 insertions(+), 445 deletions(-) diff --git a/proto/relayer/provers/lcp/config/config.proto b/proto/relayer/provers/lcp/config/config.proto index 088c17cb..b8afb2c2 100644 --- a/proto/relayer/provers/lcp/config/config.proto +++ b/proto/relayer/provers/lcp/config/config.proto @@ -56,6 +56,8 @@ message ProverConfig { string elc_updater_grpc_address = 34; // Max speculative UpdateClient units per request. + // Zero uses the relayer default. Values above the current LCP service + // protocol limit are rejected by config validation. uint32 max_speculative_batch_units_per_request = 35; // Disable the explicit-state UpdateClient path for this prover. diff --git a/relay/client.go b/relay/client.go index c88521df..90556b70 100644 --- a/relay/client.go +++ b/relay/client.go @@ -13,7 +13,6 @@ type ( ) type LCPServiceClient struct { - conn *grpc.ClientConn ELCMsgClient ELCQueryClient EnclaveQueryClient @@ -21,7 +20,6 @@ type LCPServiceClient struct { func NewLCPServiceClient(conn *grpc.ClientConn) LCPServiceClient { return LCPServiceClient{ - conn: conn, ELCMsgClient: elc.NewMsgClient(conn), ELCQueryClient: elc.NewQueryClient(conn), EnclaveQueryClient: enclave.NewQueryClient(conn), diff --git a/relay/config.go b/relay/config.go index 9a3c3370..013d8047 100644 --- a/relay/config.go +++ b/relay/config.go @@ -22,13 +22,17 @@ const ( // It is necessary to subtract from 4 MB to account for metadata size. DefaultMaxChunkSize = 4*1024*1024 - 1024 MaxSpeculativeBatchHeaderChunkSize = DefaultMaxChunkSize - MaxSpeculativeBatchUnitsLimit = 256 - DefaultMaxSpeculativeBatchUnits = MaxSpeculativeBatchUnitsLimit + DefaultMaxSpeculativeBatchUnits = 256 + // MaxSpeculativeBatchUnitsLimit is the peer LCP service protocol limit, not + // just a relayer default. Keep it in sync with LCP's + // MAX_SPECULATIVE_BATCH_UNITS and raise both sides together before allowing + // larger requests. + MaxSpeculativeBatchUnitsLimit = DefaultMaxSpeculativeBatchUnits // DefaultMaxSpeculativeBatchBytes caps the per-batch in-flight header - // payload that the relayer retains for fallback. The streaming worker - // flushes the current batch whenever appending another unit would exceed - // this limit, which keeps peak memory bounded even when individual - // upstream headers (for example Arbitrum size-capped chunks) are large. + // payload. The streaming worker flushes the current batch whenever appending + // another unit would exceed this limit, which keeps peak request memory + // bounded even when individual upstream headers or size-capped chunks are + // large. DefaultMaxSpeculativeBatchBytes = 192 * 1024 * 1024 ) @@ -124,7 +128,7 @@ var speculativeBatchBytesPerRequestOverride int // GetMaxSpeculativeBatchBytesPerRequest returns the per-batch in-flight header // payload cap. The streaming worker flushes the current speculative batch // before adding another unit when the accumulated header bytes would exceed -// this value, bounding fallback retention memory independent of unit count. +// this value, bounding request memory independent of unit count. func (pc ProverConfig) GetMaxSpeculativeBatchBytesPerRequest() int { if speculativeBatchBytesPerRequestOverride > 0 { return speculativeBatchBytesPerRequestOverride diff --git a/relay/elcupdater/module/service.go b/relay/elcupdater/module/service.go index 35abd86f..c1537b96 100644 --- a/relay/elcupdater/module/service.go +++ b/relay/elcupdater/module/service.go @@ -21,6 +21,8 @@ import ( "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/health" + healthpb "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/status" ) @@ -203,6 +205,13 @@ func (srv *Service) runGRPCServer(ctx context.Context) error { // Register ELCUpdater service elcupdater_grpc.RegisterServiceServer(grpcServer, grpcService) + + // Register standard grpc.health.v1 liveness service. The overall ("") + // status reports SERVING for as long as this gRPC server is accepting + // requests. + healthSrv := health.NewServer() + healthpb.RegisterHealthServer(grpcServer, healthSrv) + // Create listener lis, err := net.Listen("tcp", srv.GRPCAddr) if err != nil { @@ -222,6 +231,7 @@ func (srv *Service) runGRPCServer(ctx context.Context) error { <-ctx.Done() logger.InfoContext(ctx, "Stopping ELCUpdater gRPC server") + healthSrv.Shutdown() grpcServer.GracefulStop() return nil diff --git a/relay/explicit_state_batch.go b/relay/explicit_state_batch.go index 0dfb9b73..68e15b7d 100644 --- a/relay/explicit_state_batch.go +++ b/relay/explicit_state_batch.go @@ -7,7 +7,7 @@ import ( "io" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/gogoproto/proto" + gogoproto "github.com/cosmos/gogoproto/proto" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/datachainlab/lcp-go/relay/elc" ) @@ -138,7 +138,7 @@ func sendSpeculativeUpdateClientUnit( }, }, } - if size := proto.Size(unitInitChunk); size > DefaultMaxChunkSize { + if size := gogoproto.Size(unitInitChunk); size > DefaultMaxChunkSize { return fmt.Errorf( "unit init chunk exceeds max safe speculative batch stream chunk size: unit_id=%q size=%d max=%d", unit.UnitId, @@ -189,13 +189,6 @@ func validateSpeculativeBatchStreamChunkSize(chunkSize uint32) error { return nil } -func unitIDForError(unit *SpeculativeUpdateClientUnit) string { - if unit == nil { - return "" - } - return unit.UnitId -} - func cloneExplicitStateRef(ref *ExplicitStateRef) *ExplicitStateRef { if ref == nil { return nil @@ -217,12 +210,9 @@ func cloneAny(any *codectypes.Any) *codectypes.Any { if any == nil { return nil } - return &codectypes.Any{ - TypeUrl: any.TypeUrl, - Value: append([]byte(nil), any.Value...), - } + return gogoproto.Clone(any).(*codectypes.Any) } func buildSpeculativeUnitID(i int) string { - return fmt.Sprintf("unit-%04d", i) + return fmt.Sprintf("unit-%d", i) } diff --git a/relay/explicit_state_batch_test.go b/relay/explicit_state_batch_test.go index 125cafee..08fc89e1 100644 --- a/relay/explicit_state_batch_test.go +++ b/relay/explicit_state_batch_test.go @@ -57,6 +57,9 @@ func TestSpeculativeBatchStreamSenderEnrichesEOFWithServerStatus(t *testing.T) { if !closed { t.Fatal("expected EOF enrichment to close the stream with CloseAndRecv") } + if !stream.closeAndRecvCalled { + t.Fatal("expected CloseAndRecv to be called") + } if !strings.Contains(err.Error(), "server status after send failure") { t.Fatalf("expected enriched server status, got %v", err) } @@ -68,17 +71,11 @@ func TestSpeculativeBatchStreamSenderEnrichesEOFWithServerStatus(t *testing.T) { type recordingSpeculativeBatchStream struct { grpc.ClientStream sent []*elc.MsgSpeculativeUpdateClientBatchStreamChunk - sendErrAfter int - sendErr error closeErr error closeAndRecvCalled bool - closeSendCalled bool } func (s *recordingSpeculativeBatchStream) Send(m *elc.MsgSpeculativeUpdateClientBatchStreamChunk) error { - if s.sendErrAfter > 0 && len(s.sent) >= s.sendErrAfter { - return s.sendErr - } s.sent = append(s.sent, m) return nil } @@ -92,6 +89,5 @@ func (s *recordingSpeculativeBatchStream) CloseAndRecv() (*elc.ExecuteSpeculativ } func (s *recordingSpeculativeBatchStream) CloseSend() error { - s.closeSendCalled = true return nil } diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 9d3b6e3e..db98eb7b 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -1,6 +1,7 @@ package relay import ( + "bytes" "context" "encoding/binary" "fmt" @@ -20,6 +21,7 @@ import ( lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" "github.com/datachainlab/lcp-go/relay/elc" "github.com/datachainlab/lcp-go/relay/elcupdater" + elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" "github.com/datachainlab/lcp-go/relay/enclave" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -36,19 +38,19 @@ type explicitStateBatchTestService interface { SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error } -type explicitStateFallbackTestServer struct { +type unsupportedSpeculativeBatchServer struct { elc.UnimplementedQueryServer elc.UnimplementedMsgServer batchCalls int updateCalls int } -func (s *explicitStateFallbackTestServer) SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { +func (s *unsupportedSpeculativeBatchServer) SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { s.batchCalls++ return status.Error(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented") } -func (s *explicitStateFallbackTestServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { +func (s *unsupportedSpeculativeBatchServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { for { chunk, err := stream.Recv() if err == io.EOF { @@ -93,6 +95,27 @@ func (s explicitStateBatchMultiRequestServer) SpeculativeUpdateClientBatchStream }) } +type blockingSpeculativeBatchErrorServer struct { + elc.UnimplementedMsgServer + secondSendAttempted <-chan struct{} +} + +func (s blockingSpeculativeBatchErrorServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + if len(req.Units) != 1 { + return fmt.Errorf("expected exactly one unit before batch failure, got %d", len(req.Units)) + } + select { + case <-s.secondSendAttempted: + case <-time.After(2 * time.Second): + return fmt.Errorf("producer did not attempt the second send before batch failure") + } + return status.Error(codes.Aborted, "test speculative batch failure") +} + type explicitStateSourceUnitStreamingObserveServer struct { elc.UnimplementedMsgServer firstUnitEnd chan struct{} @@ -108,7 +131,7 @@ func (s explicitStateSourceUnitStreamingObserveServer) SpeculativeUpdateClientBa switch c := chunk.GetChunk().(type) { case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd: unitCount++ - if c.UnitEnd.UnitId == "unit-0000" { + if c.UnitEnd.UnitId == "unit-0" { close(s.firstUnitEnd) } case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd: @@ -325,6 +348,54 @@ func (s explicitStateIntegrationTestServer) SpeculativeUpdateClientBatchStream(s }) } +type explicitStateParityTestServer struct { + elc.UnimplementedMsgServer + batchCalls int + updateCalls int +} + +func (s *explicitStateParityTestServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + s.batchCalls++ + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) + for i := range req.Units { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: makeExplicitStateParityResponse(i), + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: req.ClientId, + Units: units, + }) +} + +func (s *explicitStateParityTestServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { + for { + chunk, err := stream.Recv() + if err == io.EOF { + resp := makeExplicitStateParityResponse(s.updateCalls) + s.updateCalls++ + return stream.SendAndClose(&resp) + } + if err != nil { + return err + } + if chunk == nil { + return fmt.Errorf("received nil update client stream chunk") + } + } +} + +func makeExplicitStateParityResponse(i int) elc.MsgUpdateClientResponse { + return elc.MsgUpdateClientResponse{ + Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(11+i), byte(i+1)), + Signature: []byte(fmt.Sprintf("sig-%d", i)), + } +} + func recvSpeculativeBatchStreamRequest(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) (*ExecuteSpeculativeUpdateClientBatchRequest, error) { initChunk, err := stream.Recv() if err != nil { @@ -520,9 +591,7 @@ func mustPackTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) *c } // mustBuildTMHeaderForExplicitStateTest returns both the typed header and its -// packed Any form. Use it for tests that exercise the serial fallback path, -// because the streaming worker clears the packed Any after a successful Send -// and the fallback repacks from the typed header. +// packed Any form. Use it for tests that need both representations. func mustBuildTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) (*tmclienttypes.Header, *codectypes.Any) { t.Helper() header := &tmclienttypes.Header{ @@ -606,7 +675,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn "07-tendermint-11", false, []byte("signer"), - "test", + nil, ) if err != nil { done <- err @@ -647,7 +716,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn } } -func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesNilAndIncompleteBaseStateUnits(t *testing.T) { +func TestExecuteExplicitStateSourceHeaderUnitStreamRejectsNilAndIncompleteBaseStateUnits(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) } @@ -714,22 +783,16 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSerializesNilAndIncompleteBas "07-tendermint-11", false, []byte("signer"), - "test", + nil, ) - if err != nil { - t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) + if err == nil { + t.Fatal("expected incomplete base-state unit to fail") } - if len(results) != 4 { + if len(results) != 0 { t.Fatalf("unexpected result count: %d", len(results)) } - if svc.batchCalls != 2 { - t.Fatalf("expected speculative batches around serial base-state units, got %d", svc.batchCalls) - } - if len(svc.batchUnitCounts) != 2 || svc.batchUnitCounts[0] != 1 || svc.batchUnitCounts[1] != 1 { - t.Fatalf("unexpected speculative batch unit counts: %#v", svc.batchUnitCounts) - } - if svc.updateCalls != 2 { - t.Fatalf("expected two serial update-client calls for nil/incomplete base-state units, got %d", svc.updateCalls) + if svc.updateCalls != 0 { + t.Fatalf("expected serial update-client fallback not to be used, got %d", svc.updateCalls) } } @@ -795,7 +858,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamFlushesBatchOnByteBudget(t *t "07-tendermint-11", false, []byte("signer"), - "test", + nil, ) if err != nil { t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) @@ -811,6 +874,120 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamFlushesBatchOnByteBudget(t *t } } +func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + svc := &explicitStateParityTestServer{} + elc.RegisterMsgServer(server, svc) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + headers := []core.Header{ + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 11}}, + } + activeEnclaveKey := &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + } + + explicitResults, err := (&Prover{ + config: ProverConfig{ElcClientId: "07-tendermint-11"}, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: activeEnclaveKey, + }).updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + headers[len(headers)-1], + ) + if err != nil { + t.Fatalf("explicit updateELCForUpdateClient() error = %v", err) + } + + legacyResults, err := (&Prover{ + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + DisableExplicitStateUpdateClient: true, + }, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: activeEnclaveKey, + }).updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + headers[len(headers)-1], + ) + if err != nil { + t.Fatalf("legacy updateELCForUpdateClient() error = %v", err) + } + + if svc.batchCalls != 1 { + t.Fatalf("expected one explicit-state batch call, got %d", svc.batchCalls) + } + if svc.updateCalls != len(headers) { + t.Fatalf("expected %d legacy update-client calls, got %d", len(headers), svc.updateCalls) + } + assertUpdateClientResultsEqual(t, explicitResults, legacyResults) +} + +func assertUpdateClientResultsEqual(t *testing.T, got, want []*elcupdater_storage.UpdateClientResult) { + t.Helper() + if len(got) != len(want) { + t.Fatalf("result length mismatch: got=%d want=%d", len(got), len(want)) + } + for i := range got { + if got[i] == nil || want[i] == nil { + t.Fatalf("nil result at index %d: got=%#v want=%#v", i, got[i], want[i]) + } + if !bytes.Equal(got[i].Message, want[i].Message) { + t.Fatalf("message mismatch at index %d: got=%x want=%x", i, got[i].Message, want[i].Message) + } + if !bytes.Equal(got[i].Signature, want[i].Signature) { + t.Fatalf("signature mismatch at index %d: got=%x want=%x", i, got[i].Signature, want[i].Signature) + } + if !bytes.Equal(got[i].Signer, want[i].Signer) { + t.Fatalf("signer mismatch at index %d: got=%x want=%x", i, got[i].Signer, want[i].Signer) + } + } +} + func TestUpdateELCForUpdateClientKeepsTendermintHeadersOrdered(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) @@ -1101,16 +1278,16 @@ func TestUpdateELCForUpdateClientSingleHeaderStaysSingleUnitBatch(t *testing.T) } } -func TestUpdateELCForUpdateClientFallsBackToSerialWhenBatchRPCUnavailable(t *testing.T) { +func TestUpdateELCForUpdateClientReturnsErrorWhenBatchRPCUnavailable(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) } lis := bufconn.Listen(1024 * 1024) server := grpc.NewServer() - fallbackServer := &explicitStateFallbackTestServer{} - elc.RegisterQueryServer(server, fallbackServer) - elc.RegisterMsgServer(server, fallbackServer) + unsupportedBatchServer := &unsupportedSpeculativeBatchServer{} + elc.RegisterQueryServer(server, unsupportedBatchServer) + elc.RegisterMsgServer(server, unsupportedBatchServer) defer server.Stop() go func() { if err := server.Serve(lis); err != nil { @@ -1167,20 +1344,115 @@ func TestUpdateELCForUpdateClientFallsBackToSerialWhenBatchRPCUnavailable(t *tes elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, ) + if err == nil { + t.Fatal("expected updateELCForUpdateClient() to fail when speculative batch RPC is unavailable") + } + if unsupportedBatchServer.batchCalls != 1 { + t.Fatalf("expected speculative batch to be attempted once, got %d", unsupportedBatchServer.batchCalls) + } + if unsupportedBatchServer.updateCalls != 0 { + t.Fatalf("expected serial update-client fallback not to be used, got %d", unsupportedBatchServer.updateCalls) + } + if len(results) != 0 { + t.Fatalf("unexpected result count: %d", len(results)) + } +} + +func TestExecuteExplicitStateSourceHeaderUnitStreamCancelsBlockedSourceProducerOnBatchError(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + secondSendAttempted := make(chan struct{}) + elc.RegisterMsgServer(server, &blockingSpeculativeBatchErrorServer{ + secondSendAttempted: secondSendAttempted, + }) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) if err != nil { - t.Fatalf("updateELCForUpdateClient() error = %v", err) + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError) + producerCtx, producerCancel := context.WithCancel(context.Background()) + defer producerCancel() + producerStoppedAt := make(chan int, 1) + go func() { + defer close(unitStream) + for i := 0; i < 10; i++ { + if i == 1 { + close(secondSendAttempted) + } + select { + case unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ + AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), i).Header, + BaseState: &ExplicitStateRef{ + PrevHeight: &clienttypes.Height{RevisionHeight: uint64(10 + i)}, + ClientState: &codectypes.Any{TypeUrl: fmt.Sprintf("client/%d", i), Value: []byte("client")}, + ConsensusState: &codectypes.Any{TypeUrl: fmt.Sprintf("consensus/%d", i), Value: []byte("consensus")}, + }, + }}: + case <-producerCtx.Done(): + producerStoppedAt <- i + return + } + } + producerStoppedAt <- -1 + }() + + var cancelWaitErr error + producerStoppedIndex := -2 + cancelAndWaitForProducer := func() { + producerCancel() + select { + case producerStoppedIndex = <-producerStoppedAt: + case <-time.After(2 * time.Second): + cancelWaitErr = fmt.Errorf("producer did not stop after cancellation") + } } - if fallbackServer.batchCalls != 1 { - t.Fatalf("expected speculative batch to be attempted once, got %d", fallbackServer.batchCalls) + + pr := &Prover{ + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + MaxSpeculativeBatchUnitsPerRequest: 1, + }, + lcpServiceClient: NewLCPServiceClient(conn), } - if fallbackServer.updateCalls != 1 { - t.Fatalf("expected serial update-client fallback to be used once, got %d", fallbackServer.updateCalls) + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + context.Background(), + unitStream, + "07-tendermint-11", + false, + []byte("signer"), + cancelAndWaitForProducer, + ) + if err == nil { + t.Fatal("expected speculative batch error") } - if len(results) != 1 { + if len(results) != 0 { t.Fatalf("unexpected result count: %d", len(results)) } - if _, err := lcptypes.EthABIDecodeHeaderedProxyMessage(results[0].Message); err != nil { - t.Fatalf("result message decode error = %v", err) + if cancelWaitErr != nil { + t.Fatal(cancelWaitErr) + } + if producerStoppedIndex != 1 { + t.Fatalf("expected producer to be cancelled while sending unit 1, got %d", producerStoppedIndex) } } @@ -1191,9 +1463,9 @@ func TestUpdateELCForUpdateClientDisablesExplicitStateWhenConfigDisabled(t *test lis := bufconn.Listen(1024 * 1024) server := grpc.NewServer() - fallbackServer := &explicitStateFallbackTestServer{} - elc.RegisterQueryServer(server, fallbackServer) - elc.RegisterMsgServer(server, fallbackServer) + unsupportedBatchServer := &unsupportedSpeculativeBatchServer{} + elc.RegisterQueryServer(server, unsupportedBatchServer) + elc.RegisterMsgServer(server, unsupportedBatchServer) defer server.Stop() go func() { if err := server.Serve(lis); err != nil { @@ -1248,75 +1520,17 @@ func TestUpdateELCForUpdateClientDisablesExplicitStateWhenConfigDisabled(t *test if err != nil { t.Fatalf("updateELCForUpdateClient() error = %v", err) } - if fallbackServer.batchCalls != 0 { - t.Fatalf("expected speculative batch to be disabled, got %d calls", fallbackServer.batchCalls) + if unsupportedBatchServer.batchCalls != 0 { + t.Fatalf("expected speculative batch to be disabled, got %d calls", unsupportedBatchServer.batchCalls) } - if fallbackServer.updateCalls != 1 { - t.Fatalf("expected serial update-client to be used once, got %d", fallbackServer.updateCalls) + if unsupportedBatchServer.updateCalls != 1 { + t.Fatalf("expected serial update-client to be used once, got %d", unsupportedBatchServer.updateCalls) } if len(results) != 1 { t.Fatalf("unexpected result count: %d", len(results)) } } -func TestShouldLogSerialUpdateClientFallbackSuppressesUnimplemented(t *testing.T) { - err := fmt.Errorf( - "failed explicit-state update client batch: %w", - status.Error(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented"), - ) - - if !shouldFallbackToSerialUpdateClient(err) { - t.Fatal("expected Unimplemented to trigger serial fallback") - } - if shouldLogSerialUpdateClientFallback(err) { - t.Fatal("expected Unimplemented fallback log to be suppressed") - } -} - -func TestShouldFallbackToSerialUpdateClientAllowsBaseStateMismatch(t *testing.T) { - err := fmt.Errorf( - "failed explicit-state update client batch: %w", - status.Error(codes.Aborted, "BaseStateMismatch: base prev_height mismatch: expected=10 observed=11"), - ) - - if !shouldFallbackToSerialUpdateClient(err) { - t.Fatal("expected BaseStateMismatch to trigger serial fallback") - } - if !shouldLogSerialUpdateClientFallback(err) { - t.Fatal("expected BaseStateMismatch fallback log to be kept") - } -} - -func TestShouldFallbackToSerialUpdateClientAllowsDependencyStateMismatch(t *testing.T) { - err := fmt.Errorf( - "failed explicit-state update client batch: %w", - status.Error(codes.Aborted, "DependencyStateMismatch: unit unit-0001 base state does not match dependency unit-0000 post state"), - ) - - if !shouldFallbackToSerialUpdateClient(err) { - t.Fatal("expected DependencyStateMismatch to trigger serial fallback") - } -} - -func TestShouldFallbackToSerialUpdateClientRejectsEOF(t *testing.T) { - err := fmt.Errorf("send failed: %w", io.EOF) - - if shouldFallbackToSerialUpdateClient(err) { - t.Fatal("expected EOF not to trigger serial fallback") - } -} - -func TestShouldFallbackToSerialUpdateClientRejectsConflictingWriteSet(t *testing.T) { - err := fmt.Errorf( - "failed explicit-state update client batch: %w", - status.Error(codes.Aborted, "ConflictingWriteSet: independent speculative units unit-0000 and unit-0001 write the same key clients/arbitrum-1/clientState"), - ) - - if shouldFallbackToSerialUpdateClient(err) { - t.Fatal("expected ConflictingWriteSet not to trigger serial fallback") - } -} - func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvider(t *testing.T) { expectedBaseState := &ExplicitStateRef{ PrevHeight: &clienttypes.Height{RevisionHeight: 12}, @@ -1345,14 +1559,18 @@ func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvid if !ok { t.Fatal("expected chunk provider to be used") } - units, err := drainExplicitStateSourceHeaderUnitStream(unitStream) + item, ok := <-unitStream + if !ok { + t.Fatal("expected one source unit") + } + unit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, 0) if err != nil { - t.Fatalf("drainExplicitStateSourceHeaderUnitStream() error = %v", err) + t.Fatalf("explicitStateSourceHeaderUnitFromStreamItemOrError() error = %v", err) } - if len(units) != 1 { - t.Fatalf("unexpected source unit count: %d", len(units)) + if unit != expected[0] { + t.Fatalf("expected chunk provider result to be used directly: %#v", unit) } - if units[0] != expected[0] { - t.Fatalf("expected chunk provider result to be used directly: %#v", units[0]) + if item, ok := <-unitStream; ok { + t.Fatalf("unexpected extra source unit: %#v", item) } } diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go index d627eda9..34d694df 100644 --- a/relay/explicit_state_source_header_units.go +++ b/relay/explicit_state_source_header_units.go @@ -4,7 +4,6 @@ import ( "fmt" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/hyperledger-labs/yui-relayer/core" ) @@ -30,20 +29,11 @@ func makeExplicitStateSourceHeaderUnitStream( return ch } -func drainExplicitStateSourceHeaderUnitStream( +func drainExplicitStateSourceHeaderUnitStreamDiscard( unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, -) ([]*ExplicitStateSourceHeaderUnit, error) { - var units []*ExplicitStateSourceHeaderUnit - i := 0 - for item := range unitStream { - unit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, i) - if err != nil { - return nil, err - } - units = append(units, unit) - i += 1 +) { + for range unitStream { } - return units, nil } func explicitStateSourceHeaderUnitFromStreamItemOrError( @@ -64,39 +54,3 @@ func explicitStateSourceHeaderUnitFromStreamItemOrError( } return item.Unit, nil } - -func extractAnyHeadersFromSourceUnits( - units []*ExplicitStateSourceHeaderUnit, -) ([]*codectypes.Any, error) { - anyHeaders := make([]*codectypes.Any, 0, len(units)) - for i, unit := range units { - if unit == nil { - return nil, fmt.Errorf("source header unit must not be nil: i=%v", i) - } - anyHeader, err := ensureAnyHeaderForSourceUnit(unit) - if err != nil { - return nil, fmt.Errorf("source header unit at i=%v: %w", i, err) - } - anyHeaders = append(anyHeaders, anyHeader) - } - return anyHeaders, nil -} - -// ensureAnyHeaderForSourceUnit returns the packed AnyHeader for a source unit, -// repacking from unit.Header if the packed form was released after a successful -// speculative Send. The repacked AnyHeader is cached back into the unit so a -// subsequent fallback iteration does not pay the encoding cost twice. -func ensureAnyHeaderForSourceUnit(unit *ExplicitStateSourceHeaderUnit) (*codectypes.Any, error) { - if unit.AnyHeader != nil { - return unit.AnyHeader, nil - } - if unit.Header == nil { - return nil, fmt.Errorf("source header unit missing packed header and core header") - } - anyHeader, err := clienttypes.PackClientMessage(unit.Header) - if err != nil { - return nil, fmt.Errorf("failed to repack source header from core header: %w", err) - } - unit.AnyHeader = anyHeader - return anyHeader, nil -} diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 9d85113a..fe726981 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -19,20 +19,14 @@ func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { baseState.ConsensusState != nil } -func clearExplicitStateSourceHeaderUnits(units []*ExplicitStateSourceHeaderUnit) []*ExplicitStateSourceHeaderUnit { - clear(units) - return units[:0] -} - func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( ctx context.Context, unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, elcClientID string, includeState bool, signer []byte, -) ([]*elcupdater_storage.UpdateClientResult, []*ExplicitStateSourceHeaderUnit, error) { +) ([]*elcupdater_storage.UpdateClientResult, error) { var results []*elcupdater_storage.UpdateClientResult - var fallbackUnits []*ExplicitStateSourceHeaderUnit maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() maxBatchBytes := pr.config.GetMaxSpeculativeBatchBytesPerRequest() @@ -105,44 +99,29 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( for item := range unitStream { sourceUnit, err := explicitStateSourceHeaderUnitFromStreamItemOrError(item, unitIndex) if err != nil { - return nil, fallbackUnits, err + return nil, err } if !hasCanonicalExplicitStatePayload(sourceUnit.BaseState) { - fallbackUnits = append(fallbackUnits, sourceUnit) - if err := flushBatch(); err != nil { - return results, fallbackUnits, err - } - serialResults, err := pr.executeELCUpdateHeaderUnits( - ctx, - []*ExplicitStateSourceHeaderUnit{sourceUnit}, - elcClientID, - includeState, - signer, + return nil, fmt.Errorf( + "explicit-state source header unit missing complete base state: index=%d unit_id=%q", + unitIndex, + buildSpeculativeUnitID(unitIndex), ) - if err != nil { - return results, fallbackUnits, err - } - results = append(results, serialResults...) - fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) - unitIndex++ - continue } headerBytes := 0 if sourceUnit.AnyHeader != nil { headerBytes = len(sourceUnit.AnyHeader.Value) } // Flush the in-flight batch before appending a unit whose header would - // push aggregate retention past the byte cap. The retention is bounded - // per batch (cleared in flushBatch's success path), so this caps peak - // fallback memory by batch boundary rather than maxUnits * header_size. + // push aggregate streamed payload past the byte cap. This keeps peak + // request memory bounded by batch boundary rather than + // maxUnits * header_size. if sender != nil && headerBytes > 0 && batchBytes+headerBytes > maxBatchBytes { if err := flushBatch(); err != nil { - return results, fallbackUnits, err + return nil, err } - fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) } - fallbackUnits = append(fallbackUnits, sourceUnit) baseState := cloneExplicitStateRef(sourceUnit.BaseState) unitID := buildSpeculativeUnitID(unitIndex) @@ -153,7 +132,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( Signer: signer, } if err := openBatch(); err != nil { - return results, fallbackUnits, err + return nil, err } logExplicitStateUnitSend(ctx, pr, elcClientID, unitID, batchIndex, len(batchSigners), unitIndex, includeState, update) if err := sender.Send(&SpeculativeUpdateClientUnit{ @@ -162,16 +141,12 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( BaseState: baseState, }); err != nil { err, _ = sender.enrichSendError(err) - return results, fallbackUnits, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) + return nil, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) } // The gRPC layer has already marshalled and queued the header bytes, so - // release our in-process copy when the typed header is available for - // serial fallback repacking. Providers may supply only AnyHeader; keep it - // in that case because ensureAnyHeaderForSourceUnit cannot repack without - // sourceUnit.Header. - if sourceUnit.Header != nil { - sourceUnit.AnyHeader = nil - } + // release our in-process copy. Batch failures are surfaced as errors + // instead of draining the source stream for serial fallback. + sourceUnit.AnyHeader = nil update.Header = nil batchSigners = append(batchSigners, update.Signer) batchBytes += headerBytes @@ -179,16 +154,14 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if len(batchSigners) == maxUnits { if err := flushBatch(); err != nil { - return results, fallbackUnits, err + return nil, err } - fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) } } if err := flushBatch(); err != nil { - return results, fallbackUnits, err + return nil, err } - fallbackUnits = clearExplicitStateSourceHeaderUnits(fallbackUnits) - return results, fallbackUnits, nil + return results, nil } func logExplicitStateUnitSend( diff --git a/relay/explicit_state_update_client_test.go b/relay/explicit_state_update_client_test.go index ab2257b8..45606668 100644 --- a/relay/explicit_state_update_client_test.go +++ b/relay/explicit_state_update_client_test.go @@ -1,79 +1,11 @@ package relay import ( - "fmt" "testing" - "time" gogoproto "github.com/cosmos/gogoproto/proto" - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" - ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" - tmclienttypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" ) -func TestBuildExplicitStateRefFromCanonicalState(t *testing.T) { - ref, err := buildExplicitStateRefFromCanonicalState( - &lcptypes.ClientState{LatestHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 11}}, - &lcptypes.ConsensusState{StateId: []byte("post-0")}, - ) - if err != nil { - t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) - } - if ref.PrevHeight == nil || ref.PrevHeight.RevisionHeight != 11 { - t.Fatalf("unexpected prev height: %+v", ref.PrevHeight) - } - if string(ref.PrevStateId) != "post-0" { - t.Fatalf("unexpected prev state id: %q", string(ref.PrevStateId)) - } -} - -func TestBuildExplicitStateRefFromCanonicalStateZeroHeight(t *testing.T) { - ref, err := buildExplicitStateRefFromCanonicalState( - &lcptypes.ClientState{}, - &lcptypes.ConsensusState{}, - ) - if err != nil { - t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) - } - if ref.PrevHeight != nil { - t.Fatalf("expected nil prev height for zero canonical height: %+v", ref.PrevHeight) - } - if len(ref.PrevStateId) != 0 { - t.Fatalf("expected empty prev state id: %x", ref.PrevStateId) - } -} - -func TestBuildExplicitStateRefFromCanonicalStateTendermint(t *testing.T) { - clientState := tmclienttypes.NewClientState( - "ibc0", - tmclienttypes.DefaultTrustLevel, - 14*24*time.Hour, - 21*24*time.Hour, - 10*time.Second, - clienttypes.NewHeight(0, 21), - nil, - []string{"upgrade", "upgradedIBCState"}, - ) - consensusState := tmclienttypes.NewConsensusState( - time.Unix(1773717522, 0), - commitmenttypes.NewMerkleRoot([]byte("apphash")), - []byte("next-validators"), - ) - - ref, err := buildExplicitStateRefFromCanonicalState(clientState, consensusState) - if err != nil { - t.Fatalf("buildExplicitStateRefFromCanonicalState() error = %v", err) - } - if ref.PrevHeight == nil || ref.PrevHeight.RevisionHeight != 21 { - t.Fatalf("unexpected prev height: %+v", ref.PrevHeight) - } - if len(ref.PrevStateId) != 0 { - t.Fatalf("expected tendermint explicit state ref to omit prev state id: %x", ref.PrevStateId) - } -} - func TestShouldUseExplicitStateUpdateClientUsesProverConfig(t *testing.T) { if got := (&Prover{}).shouldUseExplicitStateUpdateClient(); !got { t.Fatal("expected explicit-state update client to be enabled by default") @@ -101,38 +33,3 @@ func TestProverConfigDisableExplicitStateUpdateClientRoundTrip(t *testing.T) { t.Fatal("expected disable_explicit_state_update_client to round-trip") } } - -func buildExplicitStateRefFromCanonicalState( - clientState ibcexported.ClientState, - consensusState ibcexported.ConsensusState, -) (*ExplicitStateRef, error) { - ref := &ExplicitStateRef{} - if clientState == nil { - return nil, fmt.Errorf("client state must not be nil") - } - latestHeight, ok := clientState.GetLatestHeight().(clienttypes.Height) - if !ok { - return nil, fmt.Errorf("unsupported latest height type for explicit state ref: %T", clientState.GetLatestHeight()) - } - if !latestHeight.IsZero() { - h := latestHeight - ref.PrevHeight = &h - } - - if cs, ok := consensusState.(*lcptypes.ConsensusState); ok && cs != nil && len(cs.StateId) > 0 { - ref.PrevStateId = append([]byte(nil), cs.StateId...) - } - - anyClientState, err := clienttypes.PackClientState(clientState) - if err != nil { - return nil, fmt.Errorf("failed to pack client state for explicit state payload: %w", err) - } - ref.ClientState = anyClientState - - anyConsensusState, err := clienttypes.PackConsensusState(consensusState) - if err != nil { - return nil, fmt.Errorf("failed to pack consensus state for explicit state ref: %w", err) - } - ref.ConsensusState = anyConsensusState - return ref, nil -} diff --git a/relay/lcp.go b/relay/lcp.go index f52dd255..34263d01 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -397,7 +397,9 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() if pr.shouldUseExplicitStateUpdateClient() { - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(ctx, sourceChain, latestHeader) + explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) + defer cancelExplicitState() + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, sourceChain, latestHeader) if err != nil { return nil, err } @@ -408,7 +410,7 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat elcClientID, includeState, signer, - explicitStateFallbackOperationEnclaveKeyUpdate, + cancelExplicitState, ) if err != nil { return nil, err diff --git a/relay/prover.go b/relay/prover.go index 95e3d7b5..0a9e480c 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -3,10 +3,8 @@ package relay import ( "bytes" "context" - "errors" "fmt" "os" - "strings" "time" "github.com/cosmos/cosmos-sdk/codec" @@ -26,9 +24,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "google.golang.org/grpc" - "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" - grpcstatus "google.golang.org/grpc/status" ) type Prover struct { @@ -56,6 +52,10 @@ type Prover struct { } type ExplicitStateChunkProvider interface { + // SetupExplicitStateChunksForUpdate returns source header units for the + // explicit-state update-client path. Each emitted Unit must include a non-nil + // AnyHeader; incomplete explicit BaseState values are rejected by the + // consumer instead of falling back to serial update-client. SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) } @@ -63,11 +63,6 @@ var ( _ core.Prover = (*Prover)(nil) ) -const ( - explicitStateFallbackOperationUpdateClient = "update_client" - explicitStateFallbackOperationEnclaveKeyUpdate = "enclave_key_update" -) - func NewProver(config ProverConfig, originChain core.Chain, originProver core.Prover) (*Prover, error) { conn, err := grpc.Dial( config.LcpServiceAddress, @@ -298,7 +293,9 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { if pr.shouldUseExplicitStateUpdateClient() { - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(ctx, dstChain, latestFinalizedHeader) + explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) + defer cancelExplicitState() + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader) if err != nil { return nil, err } @@ -310,7 +307,7 @@ func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.Fi pr.config.ElcClientId, false, signer, - explicitStateFallbackOperationUpdateClient, + cancelExplicitState, ) if err != nil { return nil, err @@ -395,41 +392,15 @@ func (pr *Prover) executeELCUpdateHeaderStream( return results, nil } -func (pr *Prover) executeELCUpdateHeaderUnits( - ctx context.Context, - sourceHeaderUnits []*ExplicitStateSourceHeaderUnit, - elcClientID string, - includeState bool, - signer []byte, -) ([]*elcupdater_storage.UpdateClientResult, error) { - anyHeaders, err := extractAnyHeadersFromSourceUnits(sourceHeaderUnits) - if err != nil { - return nil, err - } - var results []*elcupdater_storage.UpdateClientResult - for _, anyHeader := range anyHeaders { - res, err := updateClient(ctx, pr.config.GetMaxChunkSizeForUpdateClient(), pr.lcpServiceClient, anyHeader, elcClientID, includeState, signer) - if err != nil { - return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) - } - results = append(results, &elcupdater_storage.UpdateClientResult{ - Message: res.Message, - Signature: res.Signature, - Signer: signer, - }) - } - return results, nil -} - func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( ctx context.Context, sourceHeaderUnitStream <-chan *ExplicitStateSourceHeaderUnitOrError, elcClientID string, includeState bool, signer []byte, - operation string, + cancelSource context.CancelFunc, ) ([]*elcupdater_storage.UpdateClientResult, error) { - results, fallbackUnits, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( + results, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( ctx, sourceHeaderUnitStream, elcClientID, @@ -439,79 +410,15 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( if err == nil { return results, nil } - if !shouldFallbackToSerialUpdateClient(err) { - return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) - } - if shouldLogSerialUpdateClientFallback(err) { - pr.getLogger().InfoContext( - ctx, - "fall back to serial update client after explicit-state batch failure", - "operation", operation, - "client_id", elcClientID, - "error", err.Error(), - ) - } - remainingUnits, collectErr := drainExplicitStateSourceHeaderUnitStream(sourceHeaderUnitStream) - if collectErr != nil { - return nil, collectErr - } - fallbackUnits = append(fallbackUnits, remainingUnits...) - serialResults, serialErr := pr.executeELCUpdateHeaderUnits(ctx, fallbackUnits, elcClientID, includeState, signer) - if serialErr != nil { - return nil, serialErr - } - return append(results, serialResults...), nil -} - -const ( - speculativeBatchFailureKindBaseStateMismatch = "BaseStateMismatch" - speculativeBatchFailureKindDependencyStateMismatch = "DependencyStateMismatch" -) - -// Serial fallback is allowed only for capability failures and explicit-state -// prediction mismatches that indicate this ELC cannot provide a stable ordered -// chain base state for the current attempt. Ordered-chain merge/write-set -// failures must stay visible so we do not silently mask explicit-state bugs as -// a successful serial update-client. -func shouldFallbackToSerialUpdateClient(err error) bool { - for current := err; current != nil; current = errors.Unwrap(current) { - if grpcstatus.Code(current) == codes.Unimplemented { - return true - } - if kind, ok := speculativeBatchFailureKindFromError(current); ok { - switch kind { - case speculativeBatchFailureKindBaseStateMismatch, speculativeBatchFailureKindDependencyStateMismatch: - return true - } - } - } - return false -} - -func shouldLogSerialUpdateClientFallback(err error) bool { - for current := err; current != nil; current = errors.Unwrap(current) { - if grpcstatus.Code(current) == codes.Unimplemented { - return false - } - } - return true -} - -func speculativeBatchFailureKindFromError(err error) (string, bool) { - statusErr, ok := grpcstatus.FromError(err) - if !ok || statusErr.Code() != codes.Aborted { - return "", false - } - message := statusErr.Message() - for _, kind := range []string{ - speculativeBatchFailureKindBaseStateMismatch, - speculativeBatchFailureKindDependencyStateMismatch, - } { - if strings.HasPrefix(message, kind+": ") { - return kind, true - } + // The source stream may be backed by an unbuffered producer goroutine. + // Cancel its context and discard the remaining stream items so that a producer + // blocked in send can observe cancellation or complete its send and exit. This + // intentionally does not retain units or run serial update-client fallback. + if cancelSource != nil { + cancelSource() } - return "", false + drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) + return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) } func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( From f27bd8820c71b8a1b367c1d74bf9f0259f91b107 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 4 Jun 2026 14:09:11 +0900 Subject: [PATCH 12/29] relay: make explicit-state update client opt-in - replace disable_explicit_state_update_client with enable_explicit_state_update_client - keep the legacy serial path as the default after upgrades - enable the explicit-state path explicitly in tests that exercise it --- proto/relayer/provers/lcp/config/config.proto | 6 ++- relay/config.pb.go | 14 +++--- relay/explicit_state_plan_test.go | 43 ++++++++++++------- relay/explicit_state_update_client.go | 2 +- relay/explicit_state_update_client_test.go | 18 ++++---- 5 files changed, 49 insertions(+), 34 deletions(-) diff --git a/proto/relayer/provers/lcp/config/config.proto b/proto/relayer/provers/lcp/config/config.proto index b8afb2c2..122e483f 100644 --- a/proto/relayer/provers/lcp/config/config.proto +++ b/proto/relayer/provers/lcp/config/config.proto @@ -60,8 +60,10 @@ message ProverConfig { // protocol limit are rejected by config validation. uint32 max_speculative_batch_units_per_request = 35; - // Disable the explicit-state UpdateClient path for this prover. - bool disable_explicit_state_update_client = 36; + // Enable the explicit-state UpdateClient path for this prover. + // Disabled by default so upgrades keep using the legacy serial path unless + // this experimental path is explicitly enabled. + bool enable_explicit_state_update_client = 36; } message Fraction { diff --git a/relay/config.pb.go b/relay/config.pb.go index 961ebb2f..3227a08b 100644 --- a/relay/config.pb.go +++ b/relay/config.pb.go @@ -72,8 +72,8 @@ type ProverConfig struct { ElcUpdaterGrpcAddress string `protobuf:"bytes,34,opt,name=elc_updater_grpc_address,json=elcUpdaterGrpcAddress,proto3" json:"elc_updater_grpc_address,omitempty"` // Max speculative UpdateClient units per request. MaxSpeculativeBatchUnitsPerRequest uint32 `protobuf:"varint,35,opt,name=max_speculative_batch_units_per_request,json=maxSpeculativeBatchUnitsPerRequest,proto3" json:"max_speculative_batch_units_per_request,omitempty"` - // Disable the explicit-state UpdateClient path for this prover. - DisableExplicitStateUpdateClient bool `protobuf:"varint,36,opt,name=disable_explicit_state_update_client,json=disableExplicitStateUpdateClient,proto3" json:"disable_explicit_state_update_client,omitempty"` + // Enable the explicit-state UpdateClient path for this prover. + EnableExplicitStateUpdateClient bool `protobuf:"varint,36,opt,name=enable_explicit_state_update_client,json=enableExplicitStateUpdateClient,proto3" json:"enable_explicit_state_update_client,omitempty"` } func (m *ProverConfig) Reset() { *m = ProverConfig{} } @@ -445,9 +445,9 @@ func (m *ProverConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x98 } - if m.DisableExplicitStateUpdateClient { + if m.EnableExplicitStateUpdateClient { i-- - if m.DisableExplicitStateUpdateClient { + if m.EnableExplicitStateUpdateClient { dAtA[i] = 1 } else { dAtA[i] = 0 @@ -932,7 +932,7 @@ func (m *ProverConfig) Size() (n int) { if m.MaxSpeculativeBatchUnitsPerRequest != 0 { n += 2 + sovConfig(uint64(m.MaxSpeculativeBatchUnitsPerRequest)) } - if m.DisableExplicitStateUpdateClient { + if m.EnableExplicitStateUpdateClient { n += 3 } return n @@ -1701,7 +1701,7 @@ func (m *ProverConfig) Unmarshal(dAtA []byte) error { } case 36: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DisableExplicitStateUpdateClient", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field EnableExplicitStateUpdateClient", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -1718,7 +1718,7 @@ func (m *ProverConfig) Unmarshal(dAtA []byte) error { break } } - m.DisableExplicitStateUpdateClient = bool(v != 0) + m.EnableExplicitStateUpdateClient = bool(v != 0) default: iNdEx = preIndex skippy, err := skipConfig(dAtA[iNdEx:]) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index db98eb7b..0c40b4a4 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -921,8 +921,11 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) } explicitResults, err := (&Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - codec: coreCodec, + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, originProver: fakeOriginProver{ headers: headers, explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), @@ -940,8 +943,7 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) legacyResults, err := (&Prover{ config: ProverConfig{ - ElcClientId: "07-tendermint-11", - DisableExplicitStateUpdateClient: true, + ElcClientId: "07-tendermint-11", }, codec: coreCodec, originProver: fakeOriginProver{ @@ -1029,8 +1031,11 @@ func TestUpdateELCForUpdateClientKeepsTendermintHeadersOrdered(t *testing.T) { } explicitStateChunks := mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...) pr := &Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - codec: coreCodec, + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, originProver: fakeOriginProver{ headers: headers, explicitStateChunks: explicitStateChunks, @@ -1151,8 +1156,11 @@ func TestUpdateELCForEnclaveKeyUpdateUsesSpeculativeBatchStream(t *testing.T) { }, } pr := &Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - codec: coreCodec, + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, originProver: fakeOriginProver{ headers: headers, explicitStateChunks: []*ExplicitStateSourceHeaderUnit{ @@ -1243,8 +1251,11 @@ func TestUpdateELCForUpdateClientSingleHeaderStaysSingleUnitBatch(t *testing.T) &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, } pr := &Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - codec: coreCodec, + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, originProver: fakeOriginProver{ headers: headers, explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), @@ -1315,8 +1326,11 @@ func TestUpdateELCForUpdateClientReturnsErrorWhenBatchRPCUnavailable(t *testing. anyHeader := mustPackTMHeaderForExplicitStateTest(t, 10) pr := &Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - codec: coreCodec, + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, originProver: fakeOriginProver{ explicitStateChunks: []*ExplicitStateSourceHeaderUnit{ { @@ -1456,7 +1470,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamCancelsBlockedSourceProducerO } } -func TestUpdateELCForUpdateClientDisablesExplicitStateWhenConfigDisabled(t *testing.T) { +func TestUpdateELCForUpdateClientUsesLegacyWhenExplicitStateNotEnabled(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) } @@ -1494,8 +1508,7 @@ func TestUpdateELCForUpdateClientDisablesExplicitStateWhenConfigDisabled(t *test header := &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}} pr := &Prover{ config: ProverConfig{ - ElcClientId: "07-tendermint-11", - DisableExplicitStateUpdateClient: true, + ElcClientId: "07-tendermint-11", }, codec: coreCodec, originProver: fakeOriginProver{ diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index fe726981..db571bc0 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -9,7 +9,7 @@ import ( ) func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { - return !pr.config.DisableExplicitStateUpdateClient + return pr.config.EnableExplicitStateUpdateClient } func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { diff --git a/relay/explicit_state_update_client_test.go b/relay/explicit_state_update_client_test.go index 45606668..8419f068 100644 --- a/relay/explicit_state_update_client_test.go +++ b/relay/explicit_state_update_client_test.go @@ -7,20 +7,20 @@ import ( ) func TestShouldUseExplicitStateUpdateClientUsesProverConfig(t *testing.T) { - if got := (&Prover{}).shouldUseExplicitStateUpdateClient(); !got { - t.Fatal("expected explicit-state update client to be enabled by default") + if got := (&Prover{}).shouldUseExplicitStateUpdateClient(); got { + t.Fatal("expected explicit-state update client to be disabled by default") } pr := &Prover{ - config: ProverConfig{DisableExplicitStateUpdateClient: true}, + config: ProverConfig{EnableExplicitStateUpdateClient: true}, } - if got := pr.shouldUseExplicitStateUpdateClient(); got { - t.Fatal("expected explicit-state update client to be disabled by config") + if got := pr.shouldUseExplicitStateUpdateClient(); !got { + t.Fatal("expected explicit-state update client to be enabled by config") } } -func TestProverConfigDisableExplicitStateUpdateClientRoundTrip(t *testing.T) { - bz, err := gogoproto.Marshal(&ProverConfig{DisableExplicitStateUpdateClient: true}) +func TestProverConfigEnableExplicitStateUpdateClientRoundTrip(t *testing.T) { + bz, err := gogoproto.Marshal(&ProverConfig{EnableExplicitStateUpdateClient: true}) if err != nil { t.Fatalf("Marshal() error = %v", err) } @@ -29,7 +29,7 @@ func TestProverConfigDisableExplicitStateUpdateClientRoundTrip(t *testing.T) { if err := gogoproto.Unmarshal(bz, &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } - if !got.DisableExplicitStateUpdateClient { - t.Fatal("expected disable_explicit_state_update_client to round-trip") + if !got.EnableExplicitStateUpdateClient { + t.Fatal("expected enable_explicit_state_update_client to round-trip") } } From b0779386bfad01addfbd1c6b7ff903f4cb4b6db1 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 4 Jun 2026 16:58:29 +0900 Subject: [PATCH 13/29] relay: fall back on explicit-state base mismatch --- relay/explicit_state_plan_test.go | 106 ++++++++++++++++++++++++++ relay/explicit_state_update_client.go | 10 +++ relay/prover.go | 13 +++- 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 0c40b4a4..e3239f56 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -389,6 +389,37 @@ func (s *explicitStateParityTestServer) UpdateClientStream(stream elc.Msg_Update } } +type explicitStateBaseMismatchThenLegacyServer struct { + elc.UnimplementedMsgServer + batchCalls int + updateCalls int +} + +func (s *explicitStateBaseMismatchThenLegacyServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + if _, err := recvSpeculativeBatchStreamRequest(stream); err != nil { + return err + } + s.batchCalls++ + return status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch: client_id=07-tendermint-11") +} + +func (s *explicitStateBaseMismatchThenLegacyServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { + for { + chunk, err := stream.Recv() + if err == io.EOF { + resp := makeExplicitStateParityResponse(s.updateCalls) + s.updateCalls++ + return stream.SendAndClose(&resp) + } + if err != nil { + return err + } + if chunk == nil { + return fmt.Errorf("received nil update client stream chunk") + } + } +} + func makeExplicitStateParityResponse(i int) elc.MsgUpdateClientResponse { return elc.MsgUpdateClientResponse{ Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(11+i), byte(i+1)), @@ -969,6 +1000,81 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) assertUpdateClientResultsEqual(t, explicitResults, legacyResults) } +func TestUpdateELCForUpdateClientFallsBackOnExplicitStateBaseMismatch(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + svc := &explicitStateBaseMismatchThenLegacyServer{} + elc.RegisterMsgServer(server, svc) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + headers := []core.Header{ + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 11}}, + } + results, err := (&Prover{ + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, + originProver: fakeOriginProver{ + headers: headers, + explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), + }, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + }).updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), + headers[len(headers)-1], + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if svc.batchCalls != 1 { + t.Fatalf("expected one failed explicit-state batch call, got %d", svc.batchCalls) + } + if svc.updateCalls != len(headers) { + t.Fatalf("expected %d legacy fallback update-client calls, got %d", len(headers), svc.updateCalls) + } + if len(results) != len(headers) { + t.Fatalf("unexpected result count: got=%d want=%d", len(results), len(headers)) + } +} + func assertUpdateClientResultsEqual(t *testing.T, got, want []*elcupdater_storage.UpdateClientResult) { t.Helper() if len(got) != len(want) { diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index db571bc0..f527a019 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -3,6 +3,7 @@ package relay import ( "context" "fmt" + "strings" "github.com/datachainlab/lcp-go/relay/elc" elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" @@ -12,6 +13,15 @@ func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { return pr.config.EnableExplicitStateUpdateClient } +func isExplicitStateBaseStateMismatchError(err error) bool { + if err == nil { + return false + } + msg := err.Error() + return strings.Contains(msg, "BaseStateMismatch") || + strings.Contains(msg, "canonical speculative base") +} + func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { return baseState != nil && baseState.PrevHeight != nil && diff --git a/relay/prover.go b/relay/prover.go index 0a9e480c..8392cf55 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -310,9 +310,18 @@ func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.Fi cancelExplicitState, ) if err != nil { - return nil, err + if !isExplicitStateBaseStateMismatchError(err) { + return nil, err + } + pr.getLogger().WarnContext( + ctx, + "explicit-state update client base state mismatch; falling back to legacy update client", + "client_id", pr.config.ElcClientId, + "error", err, + ) + } else { + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) } - return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) } } From d0a794e098d9850166be75895827ee8e95dc4153 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 4 Jun 2026 17:01:28 +0900 Subject: [PATCH 14/29] Revert "relay: fall back on explicit-state base mismatch" This reverts commit d0121f1b9a5843ef04a2e852a5a371dde26fe6d2. --- relay/explicit_state_plan_test.go | 106 -------------------------- relay/explicit_state_update_client.go | 10 --- relay/prover.go | 13 +--- 3 files changed, 2 insertions(+), 127 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index e3239f56..0c40b4a4 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -389,37 +389,6 @@ func (s *explicitStateParityTestServer) UpdateClientStream(stream elc.Msg_Update } } -type explicitStateBaseMismatchThenLegacyServer struct { - elc.UnimplementedMsgServer - batchCalls int - updateCalls int -} - -func (s *explicitStateBaseMismatchThenLegacyServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { - if _, err := recvSpeculativeBatchStreamRequest(stream); err != nil { - return err - } - s.batchCalls++ - return status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch: client_id=07-tendermint-11") -} - -func (s *explicitStateBaseMismatchThenLegacyServer) UpdateClientStream(stream elc.Msg_UpdateClientStreamServer) error { - for { - chunk, err := stream.Recv() - if err == io.EOF { - resp := makeExplicitStateParityResponse(s.updateCalls) - s.updateCalls++ - return stream.SendAndClose(&resp) - } - if err != nil { - return err - } - if chunk == nil { - return fmt.Errorf("received nil update client stream chunk") - } - } -} - func makeExplicitStateParityResponse(i int) elc.MsgUpdateClientResponse { return elc.MsgUpdateClientResponse{ Message: mustMakeExplicitStateTestHeaderedUpdateStateMessage(uint64(11+i), byte(i+1)), @@ -1000,81 +969,6 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) assertUpdateClientResultsEqual(t, explicitResults, legacyResults) } -func TestUpdateELCForUpdateClientFallsBackOnExplicitStateBaseMismatch(t *testing.T) { - if err := ylog.InitLogger("error", "text", "null", false); err != nil { - t.Fatalf("InitLogger() error = %v", err) - } - - lis := bufconn.Listen(1024 * 1024) - server := grpc.NewServer() - svc := &explicitStateBaseMismatchThenLegacyServer{} - elc.RegisterMsgServer(server, svc) - defer server.Stop() - go func() { - if err := server.Serve(lis); err != nil { - panic(err) - } - }() - - conn, err := grpc.DialContext( - context.Background(), - "bufnet", - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return lis.Dial() - }), - grpc.WithTransportCredentials(insecure.NewCredentials()), - ) - if err != nil { - t.Fatalf("grpc.DialContext() error = %v", err) - } - defer conn.Close() - - interfaceRegistry := codectypes.NewInterfaceRegistry() - std.RegisterInterfaces(interfaceRegistry) - lcptypes.RegisterInterfaces(interfaceRegistry) - coreCodec := codec.NewProtoCodec(interfaceRegistry) - - headers := []core.Header{ - &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, - &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 11}}, - } - results, err := (&Prover{ - config: ProverConfig{ - ElcClientId: "07-tendermint-11", - EnableExplicitStateUpdateClient: true, - }, - codec: coreCodec, - originProver: fakeOriginProver{ - headers: headers, - explicitStateChunks: mustExplicitStateSourceUnitsWithBaseStatesFromHeaders(t, headers...), - }, - lcpServiceClient: NewLCPServiceClient(conn), - activeEnclaveKey: &enclave.EnclaveKeyInfo{ - KeyInfo: &enclave.EnclaveKeyInfo_Ias{ - Ias: &enclave.IASEnclaveKeyInfo{ - EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), - }, - }, - }, - }).updateELCForUpdateClient( - context.Background(), - elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), - headers[len(headers)-1], - ) - if err != nil { - t.Fatalf("updateELCForUpdateClient() error = %v", err) - } - if svc.batchCalls != 1 { - t.Fatalf("expected one failed explicit-state batch call, got %d", svc.batchCalls) - } - if svc.updateCalls != len(headers) { - t.Fatalf("expected %d legacy fallback update-client calls, got %d", len(headers), svc.updateCalls) - } - if len(results) != len(headers) { - t.Fatalf("unexpected result count: got=%d want=%d", len(results), len(headers)) - } -} - func assertUpdateClientResultsEqual(t *testing.T, got, want []*elcupdater_storage.UpdateClientResult) { t.Helper() if len(got) != len(want) { diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index f527a019..db571bc0 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -3,7 +3,6 @@ package relay import ( "context" "fmt" - "strings" "github.com/datachainlab/lcp-go/relay/elc" elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" @@ -13,15 +12,6 @@ func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { return pr.config.EnableExplicitStateUpdateClient } -func isExplicitStateBaseStateMismatchError(err error) bool { - if err == nil { - return false - } - msg := err.Error() - return strings.Contains(msg, "BaseStateMismatch") || - strings.Contains(msg, "canonical speculative base") -} - func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { return baseState != nil && baseState.PrevHeight != nil && diff --git a/relay/prover.go b/relay/prover.go index 8392cf55..0a9e480c 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -310,18 +310,9 @@ func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.Fi cancelExplicitState, ) if err != nil { - if !isExplicitStateBaseStateMismatchError(err) { - return nil, err - } - pr.getLogger().WarnContext( - ctx, - "explicit-state update client base state mismatch; falling back to legacy update client", - "client_id", pr.config.ElcClientId, - "error", err, - ) - } else { - return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) + return nil, err } + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) } } From 3a53bdd2fc0a6a81186a075bd622248a4f9860b2 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 4 Jun 2026 20:31:57 +0900 Subject: [PATCH 15/29] relay: retry explicit-state from canonical base --- relay/explicit_state_plan_test.go | 362 +++++++++++++++++++- relay/explicit_state_source_header_units.go | 7 + relay/explicit_state_update_client.go | 69 ++++ relay/lcp.go | 71 ++-- relay/prover.go | 73 ++-- 5 files changed, 540 insertions(+), 42 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 0c40b4a4..5f2153f2 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -396,6 +396,109 @@ func makeExplicitStateParityResponse(i int) elc.MsgUpdateClientResponse { } } +type explicitStateCanonicalRetryServer struct { + elc.UnimplementedQueryServer + elc.UnimplementedMsgServer + queryCalls int + batchCalls int +} + +func (s *explicitStateCanonicalRetryServer) Client(_ context.Context, req *elc.QueryClientRequest) (*elc.QueryClientResponse, error) { + s.queryCalls++ + if req.ClientId != "07-tendermint-11" { + return &elc.QueryClientResponse{Found: false}, nil + } + height := clienttypes.Height{RevisionHeight: uint64(6 + s.queryCalls)} + clientStateAny, err := clienttypes.PackClientState(&lcptypes.ClientState{ + LatestHeight: height, + }) + if err != nil { + return nil, err + } + consensusStateAny, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{ + StateId: []byte(fmt.Sprintf("state-%d", height.RevisionHeight)), + }) + if err != nil { + return nil, err + } + return &elc.QueryClientResponse{ + Found: true, + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + }, nil +} + +func (s *explicitStateCanonicalRetryServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { + req, err := recvSpeculativeBatchStreamRequest(stream) + if err != nil { + return err + } + s.batchCalls++ + if s.batchCalls == 1 { + return status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch: client_id=07-tendermint-11") + } + units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) + for i := range req.Units { + units = append(units, &elc.StitchedSpeculativeUpdateClientUnitResult{ + Response: makeExplicitStateParityResponse(i), + }) + } + return stream.SendAndClose(&elc.ExecuteSpeculativeUpdateClientBatchResponse{ + ClientId: req.ClientId, + Units: units, + }) +} + +type fakeOriginProverWithBase struct { + fakeOriginProver + bases []*ExplicitStateBase +} + +func (p *fakeOriginProverWithBase) SetupExplicitStateChunksForUpdate( + _ context.Context, + _ core.FinalityAwareChain, + _ core.Header, + base *ExplicitStateBase, +) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) { + p.bases = append(p.bases, base) + if base == nil { + height := clienttypes.Height{RevisionHeight: 1} + clientStateAny, err := clienttypes.PackClientState(&lcptypes.ClientState{ + LatestHeight: height, + }) + if err != nil { + return nil, err + } + consensusStateAny, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{ + StateId: []byte("state-1"), + }) + if err != nil { + return nil, err + } + base = &ExplicitStateBase{ + Height: height, + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + } + } + anyHeader, err := codectypes.NewAnyWithValue(&tmclienttypes.Header{ + TrustedHeight: base.Height, + }) + if err != nil { + return nil, err + } + return makeExplicitStateSourceHeaderUnitStream([]*ExplicitStateSourceHeaderUnit{ + { + AnyHeader: anyHeader, + BaseState: &ExplicitStateRef{ + PrevHeight: &base.Height, + ClientState: base.ClientState, + ConsensusState: base.ConsensusState, + }, + }, + }), nil +} + func recvSpeculativeBatchStreamRequest(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) (*ExecuteSpeculativeUpdateClientBatchRequest, error) { initChunk, err := stream.Recv() if err != nil { @@ -510,7 +613,7 @@ func (p fakeOriginProver) SetupHeadersForUpdate(context.Context, core.FinalityAw return core.MakeHeaderStream(p.headers...), nil } -func (p fakeOriginProver) SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) { +func (p fakeOriginProver) SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header, *ExplicitStateBase) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) { return makeExplicitStateSourceHeaderUnitStream(p.explicitStateChunks), nil } @@ -969,6 +1072,262 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) assertUpdateClientResultsEqual(t, explicitResults, legacyResults) } +func TestIsExplicitStateBaseStateMismatchError(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "kind", + err: fmt.Errorf("failed explicit-state update client batch: rpc error: code = Aborted desc = BaseStateMismatch: invalid argument"), + want: true, + }, + { + name: "grpc status", + err: status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch"), + want: true, + }, + { + name: "detail", + err: fmt.Errorf("canonical speculative base client_state mismatch: client_id=07-tendermint-11"), + want: true, + }, + { + name: "other", + err: fmt.Errorf("SpeculativeExecutionFailed"), + want: false, + }, + { + name: "nil", + err: nil, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isExplicitStateBaseStateMismatchError(tt.err); got != tt.want { + t.Fatalf("isExplicitStateBaseStateMismatchError() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestQueryLCPCanonicalExplicitStateBase(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + svc := &explicitStateCanonicalRetryServer{} + elc.RegisterQueryServer(server, svc) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + base, err := (&Prover{ + codec: coreCodec, + lcpServiceClient: NewLCPServiceClient(conn), + }).queryLCPCanonicalExplicitStateBase(context.Background(), "07-tendermint-11") + if err != nil { + t.Fatalf("queryLCPCanonicalExplicitStateBase() error = %v", err) + } + if base == nil || base.Height.RevisionHeight != 7 { + t.Fatalf("unexpected base height: %#v", base) + } + if base.ClientState == nil || base.ConsensusState == nil { + t.Fatalf("expected packed base states: %#v", base) + } + if svc.queryCalls != 1 { + t.Fatalf("unexpected query calls: %d", svc.queryCalls) + } +} + +func TestUpdateELCForUpdateClientRetriesExplicitStateFromCanonicalBase(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + svc := &explicitStateCanonicalRetryServer{} + elc.RegisterQueryServer(server, svc) + elc.RegisterMsgServer(server, svc) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + originProver := &fakeOriginProverWithBase{} + results, err := (&Prover{ + config: ProverConfig{ + ElcClientId: "07-tendermint-11", + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, + originProver: originProver, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + }).updateELCForUpdateClient( + context.Background(), + elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 1}), + &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 10}}, + ) + if err != nil { + t.Fatalf("updateELCForUpdateClient() error = %v", err) + } + if len(results) != 1 { + t.Fatalf("unexpected result count: %d", len(results)) + } + if svc.queryCalls != 1 { + t.Fatalf("expected canonical state to be re-queried on mismatch, got %d calls", svc.queryCalls) + } + if svc.batchCalls != 2 { + t.Fatalf("expected speculative batch to be retried once, got %d calls", svc.batchCalls) + } + if len(originProver.bases) != 2 { + t.Fatalf("expected two chunk provider calls, got %d", len(originProver.bases)) + } + if originProver.bases[0] != nil || originProver.bases[1].Height.RevisionHeight != 7 { + t.Fatalf("unexpected canonical base heights: %#v", originProver.bases) + } +} + +func TestUpdateELCRetriesExplicitStateFromCanonicalBase(t *testing.T) { + if err := ylog.InitLogger("error", "text", "null", false); err != nil { + t.Fatalf("InitLogger() error = %v", err) + } + + lis := bufconn.Listen(1024 * 1024) + server := grpc.NewServer() + svc := &explicitStateCanonicalRetryServer{} + elc.RegisterQueryServer(server, svc) + elc.RegisterMsgServer(server, svc) + defer server.Stop() + go func() { + if err := server.Serve(lis); err != nil { + panic(err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + t.Fatalf("grpc.DialContext() error = %v", err) + } + defer conn.Close() + + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + originProver := &fakeOriginProverWithBase{ + fakeOriginProver: fakeOriginProver{ + headers: []core.Header{ + &tmclienttypes.Header{ + SignedHeader: &tmproto.SignedHeader{ + Header: &tmproto.Header{ + Height: 10, + }, + }, + }, + }, + }, + } + responses, err := (&Prover{ + config: ProverConfig{ + EnableExplicitStateUpdateClient: true, + }, + codec: coreCodec, + originProver: originProver, + lcpServiceClient: NewLCPServiceClient(conn), + activeEnclaveKey: &enclave.EnclaveKeyInfo{ + KeyInfo: &enclave.EnclaveKeyInfo_Ias{ + Ias: &enclave.IASEnclaveKeyInfo{ + EnclaveKeyAddress: common.HexToAddress("0x1111111111111111111111111111111111111111").Bytes(), + }, + }, + }, + }).updateELC( + context.Background(), + "07-tendermint-11", + false, + ) + if err != nil { + t.Fatalf("updateELC() error = %v", err) + } + if len(responses) != 1 { + t.Fatalf("unexpected response count: %d", len(responses)) + } + if svc.queryCalls != 2 { + t.Fatalf("expected initial freshness query plus one canonical base query, got %d calls", svc.queryCalls) + } + if svc.batchCalls != 2 { + t.Fatalf("expected speculative batch to be retried once, got %d calls", svc.batchCalls) + } + if len(originProver.bases) != 2 { + t.Fatalf("expected two chunk provider calls, got %d", len(originProver.bases)) + } + if originProver.bases[0] != nil || originProver.bases[1].Height.RevisionHeight != 8 { + t.Fatalf("unexpected canonical base heights: %#v", originProver.bases) + } +} + func assertUpdateClientResultsEqual(t *testing.T, got, want []*elcupdater_storage.UpdateClientResult) { t.Helper() if len(got) != len(want) { @@ -1565,6 +1924,7 @@ func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvid context.Background(), elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 12}}, + nil, ) if err != nil { t.Fatalf("collectExplicitStateChunkSourceHeaderUnitStreamForUpdate() error = %v", err) diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go index 34d694df..32e5901d 100644 --- a/relay/explicit_state_source_header_units.go +++ b/relay/explicit_state_source_header_units.go @@ -4,9 +4,16 @@ import ( "fmt" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/hyperledger-labs/yui-relayer/core" ) +type ExplicitStateBase struct { + Height clienttypes.Height + ClientState *codectypes.Any + ConsensusState *codectypes.Any +} + type ExplicitStateSourceHeaderUnit struct { Header core.Header AnyHeader *codectypes.Any diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index db571bc0..544ddd6b 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -3,7 +3,11 @@ package relay import ( "context" "fmt" + "strings" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v8/modules/core/exported" "github.com/datachainlab/lcp-go/relay/elc" elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" ) @@ -12,6 +16,71 @@ func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { return pr.config.EnableExplicitStateUpdateClient } +func isExplicitStateBaseStateMismatchError(err error) bool { + if err == nil { + return false + } + // LCP currently returns speculative batch failures as plain gRPC Aborted + // status messages rather than typed error details: + // + // Status::aborted(format!("{:?}: {}", e.kind, e.detail)) + // + // The "BaseStateMismatch" substring is the Debug representation of + // SpeculativeBatchFailureKind::BaseStateMismatch emitted by the LCP service + // gRPC layer. The "canonical speculative base" substring is part of the + // lower-level enclave/store validation error when the provided explicit + // base client_state or consensus_state does not match the canonical LCP + // store. Keep both checks until the LCP service exposes a typed gRPC error + // detail or stable machine-readable error code for speculative failures. + msg := err.Error() + return strings.Contains(msg, "BaseStateMismatch") || + strings.Contains(msg, "canonical speculative base") +} + +func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcClientID string) (*ExplicitStateBase, error) { + res, err := pr.lcpServiceClient.Client(ctx, &elc.QueryClientRequest{ + ClientId: elcClientID, + }) + if err != nil { + return nil, fmt.Errorf("failed to query LCP ELC canonical state: client_id=%s %w", elcClientID, err) + } + if res == nil || !res.Found { + return nil, fmt.Errorf("LCP ELC canonical state not found: client_id=%s", elcClientID) + } + if res.ClientState == nil { + return nil, fmt.Errorf("LCP ELC canonical client_state is nil: client_id=%s", elcClientID) + } + if res.ConsensusState == nil { + return nil, fmt.Errorf("LCP ELC canonical consensus_state is nil: client_id=%s", elcClientID) + } + var clientState exported.ClientState + if err := pr.codec.UnpackAny(res.ClientState, &clientState); err != nil { + return nil, fmt.Errorf("failed to unpack LCP ELC canonical client_state: client_id=%s %w", elcClientID, err) + } + height, ok := clientState.GetLatestHeight().(clienttypes.Height) + if !ok { + return nil, fmt.Errorf("unsupported LCP ELC canonical latest height type: client_id=%s height_type=%T", elcClientID, clientState.GetLatestHeight()) + } + if height.IsZero() { + return nil, fmt.Errorf("LCP ELC canonical latest height is zero: client_id=%s", elcClientID) + } + return &ExplicitStateBase{ + Height: height, + ClientState: cloneExplicitStateAny(res.ClientState), + ConsensusState: cloneExplicitStateAny(res.ConsensusState), + }, nil +} + +func cloneExplicitStateAny(any *codectypes.Any) *codectypes.Any { + if any == nil { + return nil + } + return &codectypes.Any{ + TypeUrl: any.TypeUrl, + Value: append([]byte(nil), any.Value...), + } +} + func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { return baseState != nil && baseState.PrevHeight != nil && diff --git a/relay/lcp.go b/relay/lcp.go index 34263d01..4d20e699 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -397,32 +397,61 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() if pr.shouldUseExplicitStateUpdateClient() { - explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) - defer cancelExplicitState() - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, sourceChain, latestHeader) - if err != nil { - return nil, err + _, hasExplicitStateProvider := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider) + maxExplicitStateAttempts := 1 + if hasExplicitStateProvider { + maxExplicitStateAttempts = 2 } - if ok { - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( - ctx, - sourceHeaderUnitStream, - elcClientID, - includeState, - signer, - cancelExplicitState, - ) + for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { + var base *ExplicitStateBase + if attempt > 0 { + var err error + base, err = pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + if err != nil { + return nil, err + } + } + explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, sourceChain, latestHeader, base) if err != nil { + cancelExplicitState() return nil, err } - responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) - for _, result := range results { - responses = append(responses, &elc.MsgUpdateClientResponse{ - Message: result.Message, - Signature: result.Signature, - }) + if ok { + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx, + sourceHeaderUnitStream, + elcClientID, + includeState, + signer, + cancelExplicitState, + ) + cancelExplicitState() + if err != nil { + if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { + pr.getLogger().WarnContext( + ctx, + "explicit-state update client base state mismatch; retrying from LCP canonical state", + "client_id", elcClientID, + "attempt", attempt+1, + "max_attempts", maxExplicitStateAttempts, + "error", err, + ) + continue + } + return nil, err + } + responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) + for _, result := range results { + responses = append(responses, &elc.MsgUpdateClientResponse{ + Message: result.Message, + Signature: result.Signature, + }) + } + return responses, nil } - return responses, nil + cancelExplicitState() + break } } diff --git a/relay/prover.go b/relay/prover.go index 0a9e480c..21423129 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -55,8 +55,10 @@ type ExplicitStateChunkProvider interface { // SetupExplicitStateChunksForUpdate returns source header units for the // explicit-state update-client path. Each emitted Unit must include a non-nil // AnyHeader; incomplete explicit BaseState values are rejected by the - // consumer instead of falling back to serial update-client. - SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) + // consumer instead of falling back to serial update-client. If base is non-nil, + // implementations must build all headers/chunks from base.Height and must use + // base.ClientState and base.ConsensusState as the first unit's BaseState. + SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header, *ExplicitStateBase) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) } var ( @@ -293,26 +295,55 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { if pr.shouldUseExplicitStateUpdateClient() { - explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) - defer cancelExplicitState() - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader) - if err != nil { - return nil, err + _, hasExplicitStateProvider := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider) + maxExplicitStateAttempts := 1 + if hasExplicitStateProvider { + maxExplicitStateAttempts = 2 } - if ok { - signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( - ctx, - sourceHeaderUnitStream, - pr.config.ElcClientId, - false, - signer, - cancelExplicitState, - ) + for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { + var base *ExplicitStateBase + if attempt > 0 { + var err error + base, err = pr.queryLCPCanonicalExplicitStateBase(ctx, pr.config.ElcClientId) + if err != nil { + return nil, err + } + } + explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) if err != nil { + cancelExplicitState() return nil, err } - return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) + if ok { + signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx, + sourceHeaderUnitStream, + pr.config.ElcClientId, + false, + signer, + cancelExplicitState, + ) + cancelExplicitState() + if err != nil { + if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { + pr.getLogger().WarnContext( + ctx, + "explicit-state update client base state mismatch; retrying from LCP canonical state", + "client_id", pr.config.ElcClientId, + "attempt", attempt+1, + "max_attempts", maxExplicitStateAttempts, + "error", err, + ) + continue + } + return nil, err + } + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) + } + cancelExplicitState() + break } } @@ -425,9 +456,11 @@ func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header, + base *ExplicitStateBase, ) (<-chan *ExplicitStateSourceHeaderUnitOrError, bool, error) { - if provider, ok := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider); ok { - unitStream, err := provider.SetupExplicitStateChunksForUpdate(ctx, dstChain, latestFinalizedHeader) + originProver := unwrapExplicitStateOriginProver(pr.originProver) + if provider, ok := originProver.(ExplicitStateChunkProvider); ok { + unitStream, err := provider.SetupExplicitStateChunksForUpdate(ctx, dstChain, latestFinalizedHeader, base) if err != nil { return nil, true, err } From 0757055d58c7b15db8ef5dfe777151675a9961cd Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Fri, 5 Jun 2026 11:39:36 +0900 Subject: [PATCH 16/29] relay: log canonical explicit-state base --- relay/explicit_state_update_client.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 544ddd6b..405de0b6 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -64,6 +64,14 @@ func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcCli if height.IsZero() { return nil, fmt.Errorf("LCP ELC canonical latest height is zero: client_id=%s", elcClientID) } + pr.getLogger().InfoContext( + ctx, + "queried LCP canonical explicit-state base", + "elc_client_id", elcClientID, + "base_height", height.String(), + "client_state_type", res.ClientState.TypeUrl, + "consensus_state_type", res.ConsensusState.TypeUrl, + ) return &ExplicitStateBase{ Height: height, ClientState: cloneExplicitStateAny(res.ClientState), From f451fb0fe19252cbcd163dab0f513762fed839ca Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Fri, 5 Jun 2026 16:17:01 +0900 Subject: [PATCH 17/29] relay: detect stored base mismatch retries --- relay/explicit_state_plan_test.go | 6 +++--- relay/explicit_state_update_client.go | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 5f2153f2..05ae6bf0 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -435,7 +435,7 @@ func (s *explicitStateCanonicalRetryServer) SpeculativeUpdateClientBatchStream(s } s.batchCalls++ if s.batchCalls == 1 { - return status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch: client_id=07-tendermint-11") + return status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=stored speculative base client_state mismatch: client_id=07-tendermint-11") } units := make([]*elc.StitchedSpeculativeUpdateClientUnitResult, 0, len(req.Units)) for i := range req.Units { @@ -1085,12 +1085,12 @@ func TestIsExplicitStateBaseStateMismatchError(t *testing.T) { }, { name: "grpc status", - err: status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=canonical speculative base client_state mismatch"), + err: status.Error(codes.Aborted, "BaseStateMismatch: invalid argument: descr=stored speculative base client_state mismatch"), want: true, }, { name: "detail", - err: fmt.Errorf("canonical speculative base client_state mismatch: client_id=07-tendermint-11"), + err: fmt.Errorf("stored speculative base client_state mismatch: client_id=07-tendermint-11"), want: true, }, { diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 405de0b6..10ef6de2 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -27,14 +27,15 @@ func isExplicitStateBaseStateMismatchError(err error) bool { // // The "BaseStateMismatch" substring is the Debug representation of // SpeculativeBatchFailureKind::BaseStateMismatch emitted by the LCP service - // gRPC layer. The "canonical speculative base" substring is part of the + // gRPC layer. The "stored speculative base" substring is part of the // lower-level enclave/store validation error when the provided explicit - // base client_state or consensus_state does not match the canonical LCP - // store. Keep both checks until the LCP service exposes a typed gRPC error - // detail or stable machine-readable error code for speculative failures. + // base client_state or consensus_state does not match the base state stored + // in LCP at the requested previous height. Keep both checks until the LCP + // service exposes a typed gRPC error detail or stable machine-readable error + // code for speculative failures. msg := err.Error() return strings.Contains(msg, "BaseStateMismatch") || - strings.Contains(msg, "canonical speculative base") + strings.Contains(msg, "stored speculative base") } func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcClientID string) (*ExplicitStateBase, error) { From 0b90dcda80242427b8e933982916dd1b92e1b92e Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 9 Jun 2026 11:42:55 +0900 Subject: [PATCH 18/29] Remove speculative batch byte split --- relay/config.go | 28 +--------- relay/explicit_state_plan_test.go | 78 --------------------------- relay/explicit_state_update_client.go | 18 ------- 3 files changed, 2 insertions(+), 122 deletions(-) diff --git a/relay/config.go b/relay/config.go index 013d8047..1eef4665 100644 --- a/relay/config.go +++ b/relay/config.go @@ -22,18 +22,12 @@ const ( // It is necessary to subtract from 4 MB to account for metadata size. DefaultMaxChunkSize = 4*1024*1024 - 1024 MaxSpeculativeBatchHeaderChunkSize = DefaultMaxChunkSize - DefaultMaxSpeculativeBatchUnits = 256 // MaxSpeculativeBatchUnitsLimit is the peer LCP service protocol limit, not // just a relayer default. Keep it in sync with LCP's // MAX_SPECULATIVE_BATCH_UNITS and raise both sides together before allowing // larger requests. - MaxSpeculativeBatchUnitsLimit = DefaultMaxSpeculativeBatchUnits - // DefaultMaxSpeculativeBatchBytes caps the per-batch in-flight header - // payload. The streaming worker flushes the current batch whenever appending - // another unit would exceed this limit, which keeps peak request memory - // bounded even when individual upstream headers or size-capped chunks are - // large. - DefaultMaxSpeculativeBatchBytes = 192 * 1024 * 1024 + MaxSpeculativeBatchUnitsLimit = 256 + DefaultMaxSpeculativeBatchUnits = MaxSpeculativeBatchUnitsLimit ) var _ core.ProverConfig = (*ProverConfig)(nil) @@ -120,24 +114,6 @@ func (pc ProverConfig) GetMaxSpeculativeBatchUnitsPerRequest() int { return int(pc.MaxSpeculativeBatchUnitsPerRequest) } -// speculativeBatchBytesPerRequestOverride lets tests dial the per-batch byte -// cap down to a value compatible with synthetic header sizes. Production code -// must not set this; the zero value yields the production default. -var speculativeBatchBytesPerRequestOverride int - -// GetMaxSpeculativeBatchBytesPerRequest returns the per-batch in-flight header -// payload cap. The streaming worker flushes the current speculative batch -// before adding another unit when the accumulated header bytes would exceed -// this value, bounding request memory independent of unit count. -func (pc ProverConfig) GetMaxSpeculativeBatchBytesPerRequest() int { - if speculativeBatchBytesPerRequestOverride > 0 { - return speculativeBatchBytesPerRequestOverride - } - // Reserved for a future proto-level setting; until then, the default - // captures the operational target on its own. - return DefaultMaxSpeculativeBatchBytes -} - func (pc ProverConfig) Validate() error { // origin prover config validation if err := pc.OriginProver.GetCachedValue().(core.ProverConfig).Validate(); err != nil { diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 05ae6bf0..bcd7193c 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -899,84 +899,6 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamRejectsNilAndIncompleteBaseSt } } -func TestExecuteExplicitStateSourceHeaderUnitStreamFlushesBatchOnByteBudget(t *testing.T) { - if err := ylog.InitLogger("error", "text", "null", false); err != nil { - t.Fatalf("InitLogger() error = %v", err) - } - - // Synthetic headers in this test are 8 bytes each ("header-N"). A budget of - // 12 bytes permits one unit per batch and forces a flush before the second. - t.Cleanup(func() { speculativeBatchBytesPerRequestOverride = 0 }) - speculativeBatchBytesPerRequestOverride = 12 - - listener := bufconn.Listen(1024 * 1024) - server := grpc.NewServer() - t.Cleanup(server.Stop) - - svc := &explicitStateSplitBoundaryServer{} - elc.RegisterQueryServer(server, svc) - elc.RegisterMsgServer(server, svc) - go func() { - _ = server.Serve(listener) - }() - - conn, err := grpc.NewClient( - "passthrough:///bufnet", - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return listener.Dial() - }), - ) - if err != nil { - t.Fatalf("grpc.NewClient() error = %v", err) - } - t.Cleanup(func() { _ = conn.Close() }) - - unitStream := make(chan *ExplicitStateSourceHeaderUnitOrError, 2) - unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ - AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 0).Header, - BaseState: &ExplicitStateRef{ - PrevHeight: &clienttypes.Height{RevisionHeight: 10}, - ClientState: &codectypes.Any{TypeUrl: "client/10", Value: []byte("client-10")}, - ConsensusState: &codectypes.Any{TypeUrl: "consensus/10", Value: []byte("consensus-10")}, - }, - }} - unitStream <- &ExplicitStateSourceHeaderUnitOrError{Unit: &ExplicitStateSourceHeaderUnit{ - AnyHeader: makeSpeculativeBatchTestUpdate("07-tendermint-11", []byte("signer"), 1).Header, - BaseState: &ExplicitStateRef{ - PrevHeight: &clienttypes.Height{RevisionHeight: 11}, - ClientState: &codectypes.Any{TypeUrl: "client/11", Value: []byte("client-11")}, - ConsensusState: &codectypes.Any{TypeUrl: "consensus/11", Value: []byte("consensus-11")}, - }, - }} - close(unitStream) - - pr := &Prover{ - config: ProverConfig{ElcClientId: "07-tendermint-11"}, - lcpServiceClient: NewLCPServiceClient(conn), - } - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( - context.Background(), - unitStream, - "07-tendermint-11", - false, - []byte("signer"), - nil, - ) - if err != nil { - t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) - } - if len(results) != 2 { - t.Fatalf("unexpected result count: %d", len(results)) - } - if svc.batchCalls != 2 { - t.Fatalf("expected byte budget to force two batches, got batch_calls=%d", svc.batchCalls) - } - if len(svc.batchUnitCounts) != 2 || svc.batchUnitCounts[0] != 1 || svc.batchUnitCounts[1] != 1 { - t.Fatalf("expected byte budget to keep batches at one unit each, got counts=%#v", svc.batchUnitCounts) - } -} - func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 10ef6de2..e16b2b1d 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -107,13 +107,11 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( var results []*elcupdater_storage.UpdateClientResult maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() - maxBatchBytes := pr.config.GetMaxSpeculativeBatchBytesPerRequest() var sender *speculativeBatchStreamSender closed := true batchSigners := make([][]byte, 0, maxUnits) batchIndex := 0 unitIndex := 0 - batchBytes := 0 defer func() { if !closed && sender != nil { _ = sender.CloseSend() @@ -170,7 +168,6 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( sender = nil batchSigners = batchSigners[:0] batchIndex++ - batchBytes = 0 return nil } @@ -186,20 +183,6 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( buildSpeculativeUnitID(unitIndex), ) } - headerBytes := 0 - if sourceUnit.AnyHeader != nil { - headerBytes = len(sourceUnit.AnyHeader.Value) - } - // Flush the in-flight batch before appending a unit whose header would - // push aggregate streamed payload past the byte cap. This keeps peak - // request memory bounded by batch boundary rather than - // maxUnits * header_size. - if sender != nil && headerBytes > 0 && batchBytes+headerBytes > maxBatchBytes { - if err := flushBatch(); err != nil { - return nil, err - } - } - baseState := cloneExplicitStateRef(sourceUnit.BaseState) unitID := buildSpeculativeUnitID(unitIndex) @@ -227,7 +210,6 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( sourceUnit.AnyHeader = nil update.Header = nil batchSigners = append(batchSigners, update.Signer) - batchBytes += headerBytes unitIndex++ if len(batchSigners) == maxUnits { From 2010de1c174ca611fbb7f063b25b012966ade571 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 9 Jun 2026 12:54:03 +0900 Subject: [PATCH 19/29] Query canonical base for explicit state updates --- relay/explicit_state_plan_test.go | 42 ++++++++++++++++++---- relay/explicit_state_update_client.go | 7 +++- relay/explicit_state_update_client_test.go | 16 ++++++--- relay/lcp.go | 16 +++------ relay/prover.go | 23 +++++------- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index bcd7193c..f0afc5e1 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -45,6 +45,10 @@ type unsupportedSpeculativeBatchServer struct { updateCalls int } +func (s *unsupportedSpeculativeBatchServer) Client(context.Context, *elc.QueryClientRequest) (*elc.QueryClientResponse, error) { + return makeExplicitStateQueryClientResponse(10) +} + func (s *unsupportedSpeculativeBatchServer) SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { s.batchCalls++ return status.Error(codes.Unimplemented, "method SpeculativeUpdateClientBatchStream not implemented") @@ -349,11 +353,16 @@ func (s explicitStateIntegrationTestServer) SpeculativeUpdateClientBatchStream(s } type explicitStateParityTestServer struct { + elc.UnimplementedQueryServer elc.UnimplementedMsgServer batchCalls int updateCalls int } +func (s *explicitStateParityTestServer) Client(context.Context, *elc.QueryClientRequest) (*elc.QueryClientResponse, error) { + return makeExplicitStateQueryClientResponse(10) +} + func (s *explicitStateParityTestServer) SpeculativeUpdateClientBatchStream(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) error { req, err := recvSpeculativeBatchStreamRequest(stream) if err != nil { @@ -396,6 +405,26 @@ func makeExplicitStateParityResponse(i int) elc.MsgUpdateClientResponse { } } +func makeExplicitStateQueryClientResponse(height uint64) (*elc.QueryClientResponse, error) { + clientStateAny, err := clienttypes.PackClientState(&lcptypes.ClientState{ + LatestHeight: clienttypes.Height{RevisionHeight: height}, + }) + if err != nil { + return nil, err + } + consensusStateAny, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{ + StateId: []byte(fmt.Sprintf("state-%d", height)), + }) + if err != nil { + return nil, err + } + return &elc.QueryClientResponse{ + Found: true, + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + }, nil +} + type explicitStateCanonicalRetryServer struct { elc.UnimplementedQueryServer elc.UnimplementedMsgServer @@ -907,6 +936,7 @@ func TestUpdateELCForUpdateClientExplicitStateMatchesLegacyResults(t *testing.T) lis := bufconn.Listen(1024 * 1024) server := grpc.NewServer() svc := &explicitStateParityTestServer{} + elc.RegisterQueryServer(server, svc) elc.RegisterMsgServer(server, svc) defer server.Stop() go func() { @@ -1149,8 +1179,8 @@ func TestUpdateELCForUpdateClientRetriesExplicitStateFromCanonicalBase(t *testin if len(results) != 1 { t.Fatalf("unexpected result count: %d", len(results)) } - if svc.queryCalls != 1 { - t.Fatalf("expected canonical state to be re-queried on mismatch, got %d calls", svc.queryCalls) + if svc.queryCalls != 2 { + t.Fatalf("expected canonical state to be queried before each explicit-state attempt, got %d calls", svc.queryCalls) } if svc.batchCalls != 2 { t.Fatalf("expected speculative batch to be retried once, got %d calls", svc.batchCalls) @@ -1158,7 +1188,7 @@ func TestUpdateELCForUpdateClientRetriesExplicitStateFromCanonicalBase(t *testin if len(originProver.bases) != 2 { t.Fatalf("expected two chunk provider calls, got %d", len(originProver.bases)) } - if originProver.bases[0] != nil || originProver.bases[1].Height.RevisionHeight != 7 { + if originProver.bases[0].Height.RevisionHeight != 7 || originProver.bases[1].Height.RevisionHeight != 8 { t.Fatalf("unexpected canonical base heights: %#v", originProver.bases) } } @@ -1236,8 +1266,8 @@ func TestUpdateELCRetriesExplicitStateFromCanonicalBase(t *testing.T) { if len(responses) != 1 { t.Fatalf("unexpected response count: %d", len(responses)) } - if svc.queryCalls != 2 { - t.Fatalf("expected initial freshness query plus one canonical base query, got %d calls", svc.queryCalls) + if svc.queryCalls != 3 { + t.Fatalf("expected initial freshness query plus one canonical base query per explicit-state attempt, got %d calls", svc.queryCalls) } if svc.batchCalls != 2 { t.Fatalf("expected speculative batch to be retried once, got %d calls", svc.batchCalls) @@ -1245,7 +1275,7 @@ func TestUpdateELCRetriesExplicitStateFromCanonicalBase(t *testing.T) { if len(originProver.bases) != 2 { t.Fatalf("expected two chunk provider calls, got %d", len(originProver.bases)) } - if originProver.bases[0] != nil || originProver.bases[1].Height.RevisionHeight != 8 { + if originProver.bases[0].Height.RevisionHeight != 8 || originProver.bases[1].Height.RevisionHeight != 9 { t.Fatalf("unexpected canonical base heights: %#v", originProver.bases) } } diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index e16b2b1d..d5285e22 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -13,7 +13,12 @@ import ( ) func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { - return pr.config.EnableExplicitStateUpdateClient + return pr.config.EnableExplicitStateUpdateClient && pr.hasExplicitStateChunkProvider() +} + +func (pr *Prover) hasExplicitStateChunkProvider() bool { + _, ok := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider) + return ok } func isExplicitStateBaseStateMismatchError(err error) bool { diff --git a/relay/explicit_state_update_client_test.go b/relay/explicit_state_update_client_test.go index 8419f068..c6ade6e4 100644 --- a/relay/explicit_state_update_client_test.go +++ b/relay/explicit_state_update_client_test.go @@ -6,16 +6,24 @@ import ( gogoproto "github.com/cosmos/gogoproto/proto" ) -func TestShouldUseExplicitStateUpdateClientUsesProverConfig(t *testing.T) { +func TestShouldUseExplicitStateUpdateClientRequiresConfigAndProvider(t *testing.T) { if got := (&Prover{}).shouldUseExplicitStateUpdateClient(); got { t.Fatal("expected explicit-state update client to be disabled by default") } - pr := &Prover{ + withoutProvider := &Prover{ config: ProverConfig{EnableExplicitStateUpdateClient: true}, } - if got := pr.shouldUseExplicitStateUpdateClient(); !got { - t.Fatal("expected explicit-state update client to be enabled by config") + if got := withoutProvider.shouldUseExplicitStateUpdateClient(); got { + t.Fatal("expected explicit-state update client to require an ExplicitStateChunkProvider") + } + + withProvider := &Prover{ + config: ProverConfig{EnableExplicitStateUpdateClient: true}, + originProver: fakeOriginProver{}, + } + if got := withProvider.shouldUseExplicitStateUpdateClient(); !got { + t.Fatal("expected explicit-state update client to be enabled when config and provider are present") } } diff --git a/relay/lcp.go b/relay/lcp.go index 4d20e699..877140b7 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -397,19 +397,11 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() if pr.shouldUseExplicitStateUpdateClient() { - _, hasExplicitStateProvider := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider) - maxExplicitStateAttempts := 1 - if hasExplicitStateProvider { - maxExplicitStateAttempts = 2 - } + const maxExplicitStateAttempts = 2 for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - var base *ExplicitStateBase - if attempt > 0 { - var err error - base, err = pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) - if err != nil { - return nil, err - } + base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + if err != nil { + return nil, err } explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, sourceChain, latestHeader, base) diff --git a/relay/prover.go b/relay/prover.go index 21423129..ae719d15 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -55,9 +55,10 @@ type ExplicitStateChunkProvider interface { // SetupExplicitStateChunksForUpdate returns source header units for the // explicit-state update-client path. Each emitted Unit must include a non-nil // AnyHeader; incomplete explicit BaseState values are rejected by the - // consumer instead of falling back to serial update-client. If base is non-nil, - // implementations must build all headers/chunks from base.Height and must use - // base.ClientState and base.ConsensusState as the first unit's BaseState. + // consumer instead of falling back to serial update-client. The caller passes + // the current LCP canonical base; implementations must build all + // headers/chunks from base.Height and must use base.ClientState and + // base.ConsensusState as the first unit's BaseState. SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header, *ExplicitStateBase) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) } @@ -295,19 +296,11 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { if pr.shouldUseExplicitStateUpdateClient() { - _, hasExplicitStateProvider := unwrapExplicitStateOriginProver(pr.originProver).(ExplicitStateChunkProvider) - maxExplicitStateAttempts := 1 - if hasExplicitStateProvider { - maxExplicitStateAttempts = 2 - } + const maxExplicitStateAttempts = 2 for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - var base *ExplicitStateBase - if attempt > 0 { - var err error - base, err = pr.queryLCPCanonicalExplicitStateBase(ctx, pr.config.ElcClientId) - if err != nil { - return nil, err - } + base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, pr.config.ElcClientId) + if err != nil { + return nil, err } explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) From aa355f402b366e6f4a64b7b4604fd7a875c8e2b5 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 9 Jun 2026 14:24:17 +0900 Subject: [PATCH 20/29] Simplify explicit-state update client retries --- relay/config.go | 4 +- relay/explicit_state_batch.go | 19 +--- relay/explicit_state_plan_test.go | 15 ++- relay/explicit_state_update_client.go | 31 ++++--- relay/lcp.go | 65 ++++--------- relay/prover.go | 126 +++++++++++++++----------- 6 files changed, 120 insertions(+), 140 deletions(-) diff --git a/relay/config.go b/relay/config.go index 1eef4665..1823ffff 100644 --- a/relay/config.go +++ b/relay/config.go @@ -27,7 +27,7 @@ const ( // MAX_SPECULATIVE_BATCH_UNITS and raise both sides together before allowing // larger requests. MaxSpeculativeBatchUnitsLimit = 256 - DefaultMaxSpeculativeBatchUnits = MaxSpeculativeBatchUnitsLimit + DefaultMaxSpeculativeBatchUnits = 64 ) var _ core.ProverConfig = (*ProverConfig)(nil) @@ -131,7 +131,7 @@ func (pc ProverConfig) Validate() error { if pc.MessageAggregation && pc.MessageAggregationBatchSize == 1 { return fmt.Errorf("MessageAggregationBatchSize must be greater than 1 if MessageAggregation is true and MessageAggregationBatchSize is set") } - if pc.MaxChunkSizeForUpdateClient > MaxSpeculativeBatchHeaderChunkSize { + if pc.EnableExplicitStateUpdateClient && pc.MaxChunkSizeForUpdateClient > MaxSpeculativeBatchHeaderChunkSize { return fmt.Errorf("MaxChunkSizeForUpdateClient must be less than or equal to %d", MaxSpeculativeBatchHeaderChunkSize) } if pc.MaxSpeculativeBatchUnitsPerRequest > MaxSpeculativeBatchUnitsLimit { diff --git a/relay/explicit_state_batch.go b/relay/explicit_state_batch.go index 68e15b7d..bd663137 100644 --- a/relay/explicit_state_batch.go +++ b/relay/explicit_state_batch.go @@ -70,25 +70,14 @@ func (s *speculativeBatchStreamSender) CloseAndRecv() (*ExecuteSpeculativeUpdate err, _ = s.enrichSendError(err) return nil, fmt.Errorf("failed to send speculative batch end: %w", err) } - return s.recvCloseStatus() -} - -func (s *speculativeBatchStreamSender) recvCloseStatus() (*ExecuteSpeculativeUpdateClientBatchResponse, error) { - if s == nil || s.stream == nil { - return nil, fmt.Errorf("speculative batch stream is not open") - } - resp, err := s.stream.CloseAndRecv() - if err != nil { - return nil, err - } - return resp, nil + return s.stream.CloseAndRecv() } func (s *speculativeBatchStreamSender) enrichSendError(sendErr error) (error, bool) { if !errors.Is(sendErr, io.EOF) { return sendErr, false } - _, closeErr := s.recvCloseStatus() + _, closeErr := s.stream.CloseAndRecv() if closeErr == nil { return sendErr, true } @@ -213,6 +202,6 @@ func cloneAny(any *codectypes.Any) *codectypes.Any { return gogoproto.Clone(any).(*codectypes.Any) } -func buildSpeculativeUnitID(i int) string { - return fmt.Sprintf("unit-%d", i) +func buildSpeculativeUnitID(batchIndex, batchUnitIndex int) string { + return fmt.Sprintf("batch-%d/unit-%d", batchIndex, batchUnitIndex) } diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index f0afc5e1..01a30509 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -135,7 +135,7 @@ func (s explicitStateSourceUnitStreamingObserveServer) SpeculativeUpdateClientBa switch c := chunk.GetChunk().(type) { case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_UnitEnd: unitCount++ - if c.UnitEnd.UnitId == "unit-0" { + if c.UnitEnd.UnitId == "batch-0/unit-0" { close(s.firstUnitEnd) } case *elc.MsgSpeculativeUpdateClientBatchStreamChunk_BatchEnd: @@ -807,7 +807,6 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn "07-tendermint-11", false, []byte("signer"), - nil, ) if err != nil { done <- err @@ -915,7 +914,6 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamRejectsNilAndIncompleteBaseSt "07-tendermint-11", false, []byte("signer"), - nil, ) if err == nil { t.Fatal("expected incomplete base-state unit to fail") @@ -1031,8 +1029,8 @@ func TestIsExplicitStateBaseStateMismatchError(t *testing.T) { want bool }{ { - name: "kind", - err: fmt.Errorf("failed explicit-state update client batch: rpc error: code = Aborted desc = BaseStateMismatch: invalid argument"), + name: "wrapped grpc status", + err: fmt.Errorf("failed explicit-state update client batch: %w", status.Error(codes.Aborted, "BaseStateMismatch: invalid argument")), want: true, }, { @@ -1041,9 +1039,9 @@ func TestIsExplicitStateBaseStateMismatchError(t *testing.T) { want: true, }, { - name: "detail", + name: "detail without aborted status", err: fmt.Errorf("stored speculative base client_state mismatch: client_id=07-tendermint-11"), - want: true, + want: false, }, { name: "other", @@ -1765,8 +1763,9 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamCancelsBlockedSourceProducerO "07-tendermint-11", false, []byte("signer"), - cancelAndWaitForProducer, ) + cancelAndWaitForProducer() + drainExplicitStateSourceHeaderUnitStreamDiscard(unitStream) if err == nil { t.Fatal("expected speculative batch error") } diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index d5285e22..a28d584d 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -10,6 +10,8 @@ import ( "github.com/cosmos/ibc-go/v8/modules/core/exported" "github.com/datachainlab/lcp-go/relay/elc" elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func (pr *Prover) shouldUseExplicitStateUpdateClient() bool { @@ -38,6 +40,9 @@ func isExplicitStateBaseStateMismatchError(err error) bool { // in LCP at the requested previous height. Keep both checks until the LCP // service exposes a typed gRPC error detail or stable machine-readable error // code for speculative failures. + if status.Code(err) != codes.Aborted { + return false + } msg := err.Error() return strings.Contains(msg, "BaseStateMismatch") || strings.Contains(msg, "stored speculative base") @@ -114,7 +119,7 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( maxUnits := pr.config.GetMaxSpeculativeBatchUnitsPerRequest() var sender *speculativeBatchStreamSender closed := true - batchSigners := make([][]byte, 0, maxUnits) + batchUnitCount := 0 batchIndex := 0 unitIndex := 0 defer func() { @@ -157,8 +162,8 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if err != nil { return fmt.Errorf("failed explicit-state update client batch: %w", err) } - if len(resp.Units) != len(batchSigners) { - return fmt.Errorf("unexpected speculative batch response shape: units=%d sent=%d", len(resp.Units), len(batchSigners)) + if len(resp.Units) != batchUnitCount { + return fmt.Errorf("unexpected speculative batch response shape: units=%d sent=%d", len(resp.Units), batchUnitCount) } for i, unit := range resp.Units { if unit == nil { @@ -167,11 +172,11 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( results = append(results, &elcupdater_storage.UpdateClientResult{ Message: unit.Response.Message, Signature: unit.Response.Signature, - Signer: batchSigners[i], + Signer: signer, }) } sender = nil - batchSigners = batchSigners[:0] + batchUnitCount = 0 batchIndex++ return nil } @@ -185,12 +190,10 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( return nil, fmt.Errorf( "explicit-state source header unit missing complete base state: index=%d unit_id=%q", unitIndex, - buildSpeculativeUnitID(unitIndex), + buildSpeculativeUnitID(batchIndex, batchUnitCount), ) } - baseState := cloneExplicitStateRef(sourceUnit.BaseState) - - unitID := buildSpeculativeUnitID(unitIndex) + unitID := buildSpeculativeUnitID(batchIndex, batchUnitCount) update := &elc.MsgUpdateClient{ ClientId: elcClientID, Header: sourceUnit.AnyHeader, @@ -200,24 +203,24 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( if err := openBatch(); err != nil { return nil, err } - logExplicitStateUnitSend(ctx, pr, elcClientID, unitID, batchIndex, len(batchSigners), unitIndex, includeState, update) + logExplicitStateUnitSend(ctx, pr, elcClientID, unitID, batchIndex, batchUnitCount, unitIndex, includeState, update) if err := sender.Send(&SpeculativeUpdateClientUnit{ UnitId: unitID, Update: update, - BaseState: baseState, + BaseState: sourceUnit.BaseState, }); err != nil { err, _ = sender.enrichSendError(err) - return nil, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", len(batchSigners), unitID, err) + return nil, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", batchUnitCount, unitID, err) } // The gRPC layer has already marshalled and queued the header bytes, so // release our in-process copy. Batch failures are surfaced as errors // instead of draining the source stream for serial fallback. sourceUnit.AnyHeader = nil update.Header = nil - batchSigners = append(batchSigners, update.Signer) + batchUnitCount++ unitIndex++ - if len(batchSigners) == maxUnits { + if batchUnitCount == maxUnits { if err := flushBatch(); err != nil { return nil, err } diff --git a/relay/lcp.go b/relay/lcp.go index 877140b7..256d3ed5 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -396,55 +396,24 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() - if pr.shouldUseExplicitStateUpdateClient() { - const maxExplicitStateAttempts = 2 - for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) - if err != nil { - return nil, err - } - explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, sourceChain, latestHeader, base) - if err != nil { - cancelExplicitState() - return nil, err - } - if ok { - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( - ctx, - sourceHeaderUnitStream, - elcClientID, - includeState, - signer, - cancelExplicitState, - ) - cancelExplicitState() - if err != nil { - if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { - pr.getLogger().WarnContext( - ctx, - "explicit-state update client base state mismatch; retrying from LCP canonical state", - "client_id", elcClientID, - "attempt", attempt+1, - "max_attempts", maxExplicitStateAttempts, - "error", err, - ) - continue - } - return nil, err - } - responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) - for _, result := range results { - responses = append(responses, &elc.MsgUpdateClientResponse{ - Message: result.Message, - Signature: result.Signature, - }) - } - return responses, nil - } - cancelExplicitState() - break + if results, ok, err := pr.tryExplicitStateUpdateClient( + ctx, + sourceChain, + latestHeader, + elcClientID, + includeState, + signer, + ); err != nil { + return nil, err + } else if ok { + responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) + for _, result := range results { + responses = append(responses, &elc.MsgUpdateClientResponse{ + Message: result.Message, + Signature: result.Signature, + }) } + return responses, nil } // 2. query the header from the upstream chain. diff --git a/relay/prover.go b/relay/prover.go index ae719d15..f4584a6c 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -58,7 +58,10 @@ type ExplicitStateChunkProvider interface { // consumer instead of falling back to serial update-client. The caller passes // the current LCP canonical base; implementations must build all // headers/chunks from base.Height and must use base.ClientState and - // base.ConsensusState as the first unit's BaseState. + // base.ConsensusState as the first unit's BaseState. Implementations must + // close the returned channel after emitting all units, including when ctx is + // cancelled or after emitting a terminal ExplicitStateSourceHeaderUnitOrError + // with Error set. SetupExplicitStateChunksForUpdate(context.Context, core.FinalityAwareChain, core.Header, *ExplicitStateBase) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) } @@ -295,49 +298,17 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // updateELCForUpdateClient performs the initial setup and updateClient calls // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { - if pr.shouldUseExplicitStateUpdateClient() { - const maxExplicitStateAttempts = 2 - for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, pr.config.ElcClientId) - if err != nil { - return nil, err - } - explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) - if err != nil { - cancelExplicitState() - return nil, err - } - if ok { - signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( - ctx, - sourceHeaderUnitStream, - pr.config.ElcClientId, - false, - signer, - cancelExplicitState, - ) - cancelExplicitState() - if err != nil { - if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { - pr.getLogger().WarnContext( - ctx, - "explicit-state update client base state mismatch; retrying from LCP canonical state", - "client_id", pr.config.ElcClientId, - "attempt", attempt+1, - "max_attempts", maxExplicitStateAttempts, - "error", err, - ) - continue - } - return nil, err - } - return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) - } - cancelExplicitState() - break - } + if results, ok, err := pr.tryExplicitStateUpdateClient( + ctx, + dstChain, + latestFinalizedHeader, + pr.config.ElcClientId, + false, + pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes(), + ); err != nil { + return nil, err + } else if ok { + return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) } headerStream, err := pr.originProver.SetupHeadersForUpdate(ctx, dstChain, latestFinalizedHeader) @@ -422,7 +393,6 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( elcClientID string, includeState bool, signer []byte, - cancelSource context.CancelFunc, ) ([]*elcupdater_storage.UpdateClientResult, error) { results, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( ctx, @@ -434,17 +404,67 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( if err == nil { return results, nil } - // The source stream may be backed by an unbuffered producer goroutine. - // Cancel its context and discard the remaining stream items so that a producer - // blocked in send can observe cancellation or complete its send and exit. This - // intentionally does not retain units or run serial update-client fallback. - if cancelSource != nil { - cancelSource() - } - drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) } +func (pr *Prover) tryExplicitStateUpdateClient( + ctx context.Context, + dstChain core.FinalityAwareChain, + latestFinalizedHeader core.Header, + elcClientID string, + includeState bool, + signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, bool, error) { + if !pr.shouldUseExplicitStateUpdateClient() { + return nil, false, nil + } + const maxExplicitStateAttempts = 2 + for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { + base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + if err != nil { + return nil, true, err + } + explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) + sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) + if err != nil { + cancelExplicitState() + return nil, true, err + } + if !ok { + cancelExplicitState() + return nil, false, nil + } + results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + ctx, + sourceHeaderUnitStream, + elcClientID, + includeState, + signer, + ) + cancelExplicitState() + if err != nil { + drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) + if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { + pr.getLogger().WarnContext( + ctx, + "explicit-state update client base state mismatch; retrying from LCP canonical state", + "client_id", elcClientID, + "attempt", attempt+1, + "max_attempts", maxExplicitStateAttempts, + "error", err, + ) + continue + } + return nil, true, err + } + return results, true, nil + } + // Unreachable while maxExplicitStateAttempts is positive: the final attempt + // returns the concrete execution error instead of retrying. Keep this guard so + // the function still fails closed if the attempt count is changed. + return nil, true, fmt.Errorf("explicit-state update client exhausted retries: client_id=%s", elcClientID) +} + func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( ctx context.Context, dstChain core.FinalityAwareChain, From 0d47e0744e5de97f866cade217942e258f8ac4a2 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Tue, 9 Jun 2026 18:42:46 +0900 Subject: [PATCH 21/29] Use on-chain LCP state for explicit-state base --- relay/explicit_state_plan_test.go | 81 +++++++++++++++++++ relay/explicit_state_update_client.go | 110 ++++++++++++++++++++++++++ relay/prover.go | 4 +- 3 files changed, 193 insertions(+), 2 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 01a30509..8f8291e0 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -528,6 +528,49 @@ func (p *fakeOriginProverWithBase) SetupExplicitStateChunksForUpdate( }), nil } +type fakeOriginProverWithInitialState struct { + fakeOriginProver + requestedHeights []ibcexported.Height +} + +func (p *fakeOriginProverWithInitialState) CreateInitialLightClientState(_ context.Context, height ibcexported.Height) (ibcexported.ClientState, ibcexported.ConsensusState, error) { + p.requestedHeights = append(p.requestedHeights, height) + h, ok := height.(clienttypes.Height) + if !ok { + return nil, nil, fmt.Errorf("unexpected height type: %T", height) + } + return &lcptypes.ClientState{LatestHeight: h}, &lcptypes.ConsensusState{StateId: []byte(fmt.Sprintf("elc-state-%d", h.RevisionHeight))}, nil +} + +type fakeOnChainLCPChain struct { + core.FinalityAwareChain + queryHeight ibcexported.Height + clientStateHeight clienttypes.Height + stateID []byte + consensusQueries []ibcexported.Height +} + +func (c *fakeOnChainLCPChain) LatestHeight(context.Context) (ibcexported.Height, error) { + return c.queryHeight, nil +} + +func (c *fakeOnChainLCPChain) QueryClientState(core.QueryContext) (*clienttypes.QueryClientStateResponse, error) { + any, err := clienttypes.PackClientState(&lcptypes.ClientState{LatestHeight: c.clientStateHeight}) + if err != nil { + return nil, err + } + return &clienttypes.QueryClientStateResponse{ClientState: any}, nil +} + +func (c *fakeOnChainLCPChain) QueryClientConsensusState(_ core.QueryContext, height ibcexported.Height) (*clienttypes.QueryConsensusStateResponse, error) { + c.consensusQueries = append(c.consensusQueries, height) + any, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{StateId: append([]byte(nil), c.stateID...)}) + if err != nil { + return nil, err + } + return &clienttypes.QueryConsensusStateResponse{ConsensusState: any}, nil +} + func recvSpeculativeBatchStreamRequest(stream elc.Msg_SpeculativeUpdateClientBatchStreamServer) (*ExecuteSpeculativeUpdateClientBatchRequest, error) { initChunk, err := stream.Recv() if err != nil { @@ -1115,6 +1158,44 @@ func TestQueryLCPCanonicalExplicitStateBase(t *testing.T) { } } +func TestQueryExplicitStateBaseUsesOnChainCommittedHeight(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + lcptypes.RegisterInterfaces(interfaceRegistry) + coreCodec := codec.NewProtoCodec(interfaceRegistry) + + onChainHeight := clienttypes.Height{RevisionHeight: 9} + originProver := &fakeOriginProverWithInitialState{} + chain := &fakeOnChainLCPChain{ + queryHeight: clienttypes.Height{RevisionHeight: 100}, + clientStateHeight: onChainHeight, + stateID: []byte("on-chain-state-9"), + } + base, err := (&Prover{ + codec: coreCodec, + originProver: originProver, + }).queryExplicitStateBase(context.Background(), chain, "07-tendermint-11") + if err != nil { + t.Fatalf("queryExplicitStateBase() error = %v", err) + } + if base == nil || base.Height.RevisionHeight != onChainHeight.RevisionHeight { + t.Fatalf("unexpected base height: %#v", base) + } + if len(originProver.requestedHeights) != 1 || originProver.requestedHeights[0].GetRevisionHeight() != onChainHeight.RevisionHeight { + t.Fatalf("expected origin prover to build base at on-chain height, got %#v", originProver.requestedHeights) + } + if len(chain.consensusQueries) != 1 || chain.consensusQueries[0].GetRevisionHeight() != onChainHeight.RevisionHeight { + t.Fatalf("expected consensus query at on-chain height, got %#v", chain.consensusQueries) + } + var clientState ibcexported.ClientState + if err := coreCodec.UnpackAny(base.ClientState, &clientState); err != nil { + t.Fatalf("failed to unpack base client_state: %v", err) + } + if got := clientState.GetLatestHeight(); got.GetRevisionHeight() != onChainHeight.RevisionHeight { + t.Fatalf("unexpected packed base client_state height: %v", got) + } +} + func TestUpdateELCForUpdateClientRetriesExplicitStateFromCanonicalBase(t *testing.T) { if err := ylog.InitLogger("error", "text", "null", false); err != nil { t.Fatalf("InitLogger() error = %v", err) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index a28d584d..5f1b0084 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -8,8 +8,10 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/cosmos/ibc-go/v8/modules/core/exported" + lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" "github.com/datachainlab/lcp-go/relay/elc" elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" + "github.com/hyperledger-labs/yui-relayer/core" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -90,6 +92,114 @@ func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcCli }, nil } +func (pr *Prover) queryExplicitStateBase(ctx context.Context, dstChain core.FinalityAwareChain, elcClientID string) (*ExplicitStateBase, error) { + switch dstChain.(type) { + case LCPQuerier, *LCPQuerier: + pr.getLogger().InfoContext( + ctx, + "using LCP canonical explicit-state base for local LCP update", + "elc_client_id", elcClientID, + ) + return pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + } + base, ok, err := pr.queryOnChainCommittedExplicitStateBase(ctx, dstChain) + if err != nil { + return nil, err + } + if ok { + return base, nil + } + pr.getLogger().InfoContext( + ctx, + "on-chain LCP explicit-state base is unavailable; falling back to LCP canonical base", + "elc_client_id", elcClientID, + ) + return pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) +} + +func (pr *Prover) queryOnChainCommittedExplicitStateBase(ctx context.Context, dstChain core.FinalityAwareChain) (*ExplicitStateBase, bool, error) { + if dstChain == nil { + return nil, false, nil + } + queryHeight, err := dstChain.LatestHeight(ctx) + if err != nil { + return nil, true, fmt.Errorf("failed to query destination latest height for explicit-state base: %w", err) + } + clientRes, err := dstChain.QueryClientState(core.NewQueryContext(ctx, queryHeight)) + if err != nil { + return nil, true, fmt.Errorf("failed to query on-chain LCP client_state for explicit-state base: query_height=%v %w", queryHeight, err) + } + if clientRes == nil || clientRes.ClientState == nil { + return nil, true, fmt.Errorf("on-chain LCP client_state is nil for explicit-state base: query_height=%v", queryHeight) + } + var clientState exported.ClientState + if err := pr.codec.UnpackAny(clientRes.ClientState, &clientState); err != nil { + return nil, true, fmt.Errorf("failed to unpack on-chain client_state for explicit-state base: query_height=%v %w", queryHeight, err) + } + lcpClientState, ok := clientState.(*lcptypes.ClientState) + if !ok { + return nil, false, nil + } + baseHeight := lcpClientState.LatestHeight + if baseHeight.IsZero() { + return nil, true, fmt.Errorf("on-chain LCP latest height is zero for explicit-state base: query_height=%v", queryHeight) + } + consensusRes, err := dstChain.QueryClientConsensusState(core.NewQueryContext(ctx, queryHeight), baseHeight) + if err != nil { + return nil, true, fmt.Errorf("failed to query on-chain LCP consensus_state for explicit-state base: query_height=%v base_height=%v %w", queryHeight, baseHeight, err) + } + if consensusRes == nil || consensusRes.ConsensusState == nil { + return nil, true, fmt.Errorf("on-chain LCP consensus_state is nil for explicit-state base: query_height=%v base_height=%v", queryHeight, baseHeight) + } + var consensusState exported.ConsensusState + if err := pr.codec.UnpackAny(consensusRes.ConsensusState, &consensusState); err != nil { + return nil, true, fmt.Errorf("failed to unpack on-chain LCP consensus_state for explicit-state base: query_height=%v base_height=%v %w", queryHeight, baseHeight, err) + } + lcpConsensusState, ok := consensusState.(*lcptypes.ConsensusState) + if !ok { + return nil, true, fmt.Errorf("unexpected on-chain consensus_state type for explicit-state base: query_height=%v base_height=%v consensus_state_type=%T", queryHeight, baseHeight, consensusState) + } + if len(lcpConsensusState.StateId) == 0 { + return nil, true, fmt.Errorf("on-chain LCP consensus_state state_id is empty for explicit-state base: query_height=%v base_height=%v", queryHeight, baseHeight) + } + baseClientState, baseConsensusState, err := pr.originProver.CreateInitialLightClientState(ctx, baseHeight) + if err != nil { + return nil, true, fmt.Errorf("failed to build ELC explicit-state base from on-chain committed height: base_height=%v %w", baseHeight, err) + } + if baseClientState == nil { + return nil, true, fmt.Errorf("built ELC explicit-state base client_state is nil: base_height=%v", baseHeight) + } + if baseConsensusState == nil { + return nil, true, fmt.Errorf("built ELC explicit-state base consensus_state is nil: base_height=%v", baseHeight) + } + if latest := baseClientState.GetLatestHeight(); latest.GetRevisionNumber() != baseHeight.GetRevisionNumber() || + latest.GetRevisionHeight() != baseHeight.GetRevisionHeight() { + return nil, true, fmt.Errorf("built ELC explicit-state base height mismatch: base_height=%v client_state_latest_height=%v", baseHeight, latest) + } + baseClientStateAny, err := clienttypes.PackClientState(baseClientState) + if err != nil { + return nil, true, fmt.Errorf("failed to pack ELC explicit-state base client_state: base_height=%v %w", baseHeight, err) + } + baseConsensusStateAny, err := clienttypes.PackConsensusState(baseConsensusState) + if err != nil { + return nil, true, fmt.Errorf("failed to pack ELC explicit-state base consensus_state: base_height=%v %w", baseHeight, err) + } + pr.getLogger().InfoContext( + ctx, + "queried on-chain committed explicit-state base", + "query_height", queryHeight.String(), + "base_height", baseHeight.String(), + "on_chain_state_id", fmt.Sprintf("0x%x", lcpConsensusState.StateId), + "client_state_type", baseClientStateAny.TypeUrl, + "consensus_state_type", baseConsensusStateAny.TypeUrl, + ) + return &ExplicitStateBase{ + Height: baseHeight, + ClientState: cloneExplicitStateAny(baseClientStateAny), + ConsensusState: cloneExplicitStateAny(baseConsensusStateAny), + }, true, nil +} + func cloneExplicitStateAny(any *codectypes.Any) *codectypes.Any { if any == nil { return nil diff --git a/relay/prover.go b/relay/prover.go index f4584a6c..fe48eb5c 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -420,7 +420,7 @@ func (pr *Prover) tryExplicitStateUpdateClient( } const maxExplicitStateAttempts = 2 for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - base, err := pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + base, err := pr.queryExplicitStateBase(ctx, dstChain, elcClientID) if err != nil { return nil, true, err } @@ -447,7 +447,7 @@ func (pr *Prover) tryExplicitStateUpdateClient( if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { pr.getLogger().WarnContext( ctx, - "explicit-state update client base state mismatch; retrying from LCP canonical state", + "explicit-state update client base state mismatch; retrying from committed explicit-state base", "client_id", elcClientID, "attempt", attempt+1, "max_attempts", maxExplicitStateAttempts, From 6e833fe06cd4440095089404ffbd86c11e245646 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Wed, 10 Jun 2026 14:50:48 +0900 Subject: [PATCH 22/29] Bind explicit-state batches to committed base --- relay/explicit_state_plan_test.go | 10 +++- relay/explicit_state_source_header_units.go | 6 ++ relay/explicit_state_update_client.go | 53 ++++++++++++++++- relay/explicit_state_update_client_test.go | 66 +++++++++++++++++++++ relay/prover.go | 35 +++++++---- 5 files changed, 156 insertions(+), 14 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 8f8291e0..aa8eda81 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -312,14 +312,17 @@ func mustMakeExplicitStateTestHeaderedUpdateStateMessage(postHeight uint64, stat } func (s explicitStateIntegrationTestServer) Client(_ context.Context, req *elc.QueryClientRequest) (*elc.QueryClientResponse, error) { + // The canonical base height must match the first source unit's + // prev_height (10) used by the integration fixtures; the relayer now + // rejects providers whose first unit does not anchor at the queried base. clientStateAny, err := clienttypes.PackClientState(&lcptypes.ClientState{ - LatestHeight: clienttypes.Height{RevisionHeight: 7}, + LatestHeight: clienttypes.Height{RevisionHeight: 10}, }) if err != nil { return nil, err } consensusStateAny, err := clienttypes.PackConsensusState(&lcptypes.ConsensusState{ - StateId: []byte("state-7"), + StateId: []byte("state-10"), }) if err != nil { return nil, err @@ -847,6 +850,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( context.Background(), unitStream, + nil, "07-tendermint-11", false, []byte("signer"), @@ -954,6 +958,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamRejectsNilAndIncompleteBaseSt results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( context.Background(), unitStream, + nil, "07-tendermint-11", false, []byte("signer"), @@ -1841,6 +1846,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamCancelsBlockedSourceProducerO results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( context.Background(), unitStream, + nil, "07-tendermint-11", false, []byte("signer"), diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go index 32e5901d..81c32e84 100644 --- a/relay/explicit_state_source_header_units.go +++ b/relay/explicit_state_source_header_units.go @@ -12,6 +12,12 @@ type ExplicitStateBase struct { Height clienttypes.Height ClientState *codectypes.Any ConsensusState *codectypes.Any + // StateId is the state ID committed for this base when known (e.g. taken + // from the on-chain LCP consensus state). Empty when only the LCP canonical + // base is available. When set, the relayer threads it into the first unit's + // prev_state_id so LCP verifies the speculative chain anchors at exactly + // this state. + StateId []byte } type ExplicitStateSourceHeaderUnit struct { diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index 5f1b0084..fffa1011 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -1,6 +1,7 @@ package relay import ( + "bytes" "context" "fmt" "strings" @@ -197,6 +198,7 @@ func (pr *Prover) queryOnChainCommittedExplicitStateBase(ctx context.Context, ds Height: baseHeight, ClientState: cloneExplicitStateAny(baseClientStateAny), ConsensusState: cloneExplicitStateAny(baseConsensusStateAny), + StateId: append([]byte(nil), lcpConsensusState.StateId...), }, true, nil } @@ -217,9 +219,47 @@ func hasCanonicalExplicitStatePayload(baseState *ExplicitStateRef) bool { baseState.ConsensusState != nil } -func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( +// bindFirstUnitToExplicitStateBase pins the first source unit to the queried +// explicit-state base so a provider/base divergence fails fast in the relayer +// with a precise error instead of a generic LCP-side BaseStateMismatch. When +// the base carries a committed state ID, it is threaded into the unit's +// prev_state_id so LCP additionally verifies that the enclave-observed +// transition anchors at exactly that state. +func bindFirstUnitToExplicitStateBase(unit *ExplicitStateSourceHeaderUnit, base *ExplicitStateBase) error { + if base == nil { + return nil + } + prevHeight := unit.BaseState.PrevHeight + if prevHeight == nil || *prevHeight != base.Height { + return fmt.Errorf( + "first explicit-state unit base height mismatch: unit_prev_height=%v base_height=%v", + prevHeight, + base.Height, + ) + } + if len(base.StateId) == 0 { + return nil + } + if len(unit.BaseState.PrevStateId) == 0 { + boundBaseState := cloneExplicitStateRef(unit.BaseState) + boundBaseState.PrevStateId = append([]byte(nil), base.StateId...) + unit.BaseState = boundBaseState + return nil + } + if !bytes.Equal(unit.BaseState.PrevStateId, base.StateId) { + return fmt.Errorf( + "first explicit-state unit prev_state_id mismatch: unit_prev_state_id=0x%x base_state_id=0x%x", + unit.BaseState.PrevStateId, + base.StateId, + ) + } + return nil +} + +func (pr *Prover) executeExplicitStateSourceHeaderUnitStream( ctx context.Context, unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, + base *ExplicitStateBase, elcClientID string, includeState bool, signer []byte, @@ -303,6 +343,11 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( buildSpeculativeUnitID(batchIndex, batchUnitCount), ) } + if unitIndex == 0 { + if err := bindFirstUnitToExplicitStateBase(sourceUnit, base); err != nil { + return nil, err + } + } unitID := buildSpeculativeUnitID(batchIndex, batchUnitCount) update := &elc.MsgUpdateClient{ ClientId: elcClientID, @@ -319,7 +364,11 @@ func (pr *Prover) executeExplicitStateSourceHeaderUnitStreamWithResolver( Update: update, BaseState: sourceUnit.BaseState, }); err != nil { - err, _ = sender.enrichSendError(err) + var streamClosed bool + err, streamClosed = sender.enrichSendError(err) + if streamClosed { + closed = true + } return nil, fmt.Errorf("failed to send speculative batch unit: index=%d unit_id=%q, %w", batchUnitCount, unitID, err) } // The gRPC layer has already marshalled and queued the header bytes, so diff --git a/relay/explicit_state_update_client_test.go b/relay/explicit_state_update_client_test.go index c6ade6e4..8e5edba5 100644 --- a/relay/explicit_state_update_client_test.go +++ b/relay/explicit_state_update_client_test.go @@ -1,9 +1,13 @@ package relay import ( + "bytes" + "strings" "testing" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" gogoproto "github.com/cosmos/gogoproto/proto" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" ) func TestShouldUseExplicitStateUpdateClientRequiresConfigAndProvider(t *testing.T) { @@ -27,6 +31,68 @@ func TestShouldUseExplicitStateUpdateClientRequiresConfigAndProvider(t *testing. } } +func TestBindFirstUnitToExplicitStateBase(t *testing.T) { + baseHeight := clienttypes.Height{RevisionNumber: 0, RevisionHeight: 10} + mkUnit := func(prevHeight *clienttypes.Height, prevStateID []byte) *ExplicitStateSourceHeaderUnit { + return &ExplicitStateSourceHeaderUnit{ + BaseState: &ExplicitStateRef{ + PrevHeight: prevHeight, + PrevStateId: prevStateID, + ClientState: &codectypes.Any{TypeUrl: "client", Value: []byte("client-10")}, + ConsensusState: &codectypes.Any{TypeUrl: "consensus", Value: []byte("consensus-10")}, + }, + } + } + + t.Run("nil base is a no-op", func(t *testing.T) { + unit := mkUnit(&baseHeight, nil) + if err := bindFirstUnitToExplicitStateBase(unit, nil); err != nil { + t.Fatalf("bindFirstUnitToExplicitStateBase() error = %v", err) + } + if unit.BaseState.PrevStateId != nil { + t.Fatal("expected prev_state_id to stay empty for nil base") + } + }) + + t.Run("injects committed state id into first unit", func(t *testing.T) { + unit := mkUnit(&baseHeight, nil) + base := &ExplicitStateBase{Height: baseHeight, StateId: []byte("state-10")} + if err := bindFirstUnitToExplicitStateBase(unit, base); err != nil { + t.Fatalf("bindFirstUnitToExplicitStateBase() error = %v", err) + } + if !bytes.Equal(unit.BaseState.PrevStateId, []byte("state-10")) { + t.Fatalf("expected injected prev_state_id, got %q", unit.BaseState.PrevStateId) + } + }) + + t.Run("keeps matching provider prev_state_id", func(t *testing.T) { + unit := mkUnit(&baseHeight, []byte("state-10")) + base := &ExplicitStateBase{Height: baseHeight, StateId: []byte("state-10")} + if err := bindFirstUnitToExplicitStateBase(unit, base); err != nil { + t.Fatalf("bindFirstUnitToExplicitStateBase() error = %v", err) + } + }) + + t.Run("rejects prev_state_id mismatch", func(t *testing.T) { + unit := mkUnit(&baseHeight, []byte("state-other")) + base := &ExplicitStateBase{Height: baseHeight, StateId: []byte("state-10")} + err := bindFirstUnitToExplicitStateBase(unit, base) + if err == nil || !strings.Contains(err.Error(), "prev_state_id mismatch") { + t.Fatalf("expected prev_state_id mismatch error, got %v", err) + } + }) + + t.Run("rejects base height mismatch", func(t *testing.T) { + otherHeight := clienttypes.Height{RevisionNumber: 0, RevisionHeight: 11} + unit := mkUnit(&otherHeight, nil) + base := &ExplicitStateBase{Height: baseHeight} + err := bindFirstUnitToExplicitStateBase(unit, base) + if err == nil || !strings.Contains(err.Error(), "base height mismatch") { + t.Fatalf("expected base height mismatch error, got %v", err) + } + }) +} + func TestProverConfigEnableExplicitStateUpdateClientRoundTrip(t *testing.T) { bz, err := gogoproto.Marshal(&ProverConfig{EnableExplicitStateUpdateClient: true}) if err != nil { diff --git a/relay/prover.go b/relay/prover.go index fe48eb5c..a39722bf 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -390,13 +390,15 @@ func (pr *Prover) executeELCUpdateHeaderStream( func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( ctx context.Context, sourceHeaderUnitStream <-chan *ExplicitStateSourceHeaderUnitOrError, + base *ExplicitStateBase, elcClientID string, includeState bool, signer []byte, ) ([]*elcupdater_storage.UpdateClientResult, error) { - results, err := pr.executeExplicitStateSourceHeaderUnitStreamWithResolver( + results, err := pr.executeExplicitStateSourceHeaderUnitStream( ctx, sourceHeaderUnitStream, + base, elcClientID, includeState, signer, @@ -437,6 +439,7 @@ func (pr *Prover) tryExplicitStateUpdateClient( results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( ctx, sourceHeaderUnitStream, + base, elcClientID, includeState, signer, @@ -444,16 +447,28 @@ func (pr *Prover) tryExplicitStateUpdateClient( cancelExplicitState() if err != nil { drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) - if isExplicitStateBaseStateMismatchError(err) && attempt+1 < maxExplicitStateAttempts { - pr.getLogger().WarnContext( - ctx, - "explicit-state update client base state mismatch; retrying from committed explicit-state base", - "client_id", elcClientID, - "attempt", attempt+1, - "max_attempts", maxExplicitStateAttempts, - "error", err, + if isExplicitStateBaseStateMismatchError(err) { + if attempt+1 < maxExplicitStateAttempts { + pr.getLogger().WarnContext( + ctx, + "explicit-state update client base state mismatch; retrying from committed explicit-state base", + "client_id", elcClientID, + "attempt", attempt+1, + "max_attempts", maxExplicitStateAttempts, + "error", err, + ) + continue + } + // A mismatch that survives a fresh base query usually means the LCP + // canonical state is ahead of the on-chain committed state (e.g. a + // previous update was executed in LCP but never landed on-chain). + // The explicit-state path cannot anchor at a non-latest canonical + // state, so it cannot heal this divergence by itself. + return nil, true, fmt.Errorf( + "explicit-state base state mismatch persisted after %d attempts; if the LCP canonical state is ahead of the on-chain committed state, disable enable_explicit_state_update_client for one update cycle so the serial path can heal the gap: %w", + maxExplicitStateAttempts, + err, ) - continue } return nil, true, err } From dba1bc3e078010788a6c8140752cf8b3531cee34 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Wed, 10 Jun 2026 14:51:00 +0900 Subject: [PATCH 23/29] Update lcp submodule --- lcp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lcp b/lcp index 3832bdeb..485e3488 160000 --- a/lcp +++ b/lcp @@ -1 +1 @@ -Subproject commit 3832bdebd4998e0d8b90d65544a428ba536d3f63 +Subproject commit 485e3488221050a4ac0f4ca1d254acb10957d745 From 90e42c8c879dbcbb3b783f364cc484a4aceacf4b Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Wed, 10 Jun 2026 17:31:49 +0900 Subject: [PATCH 24/29] Separate explicit-state gate from execution --- relay/explicit_state_plan_test.go | 5 +-- relay/lcp.go | 22 +++++----- relay/prover.go | 73 +++++++++++++++---------------- 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index aa8eda81..186dadaa 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -1958,7 +1958,7 @@ func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvid }, } - unitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( + unitStream, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( context.Background(), elcupdater.NewMockChain("counterparty", clienttypes.Height{RevisionHeight: 7}), &tmclienttypes.Header{TrustedHeight: clienttypes.Height{RevisionHeight: 12}}, @@ -1967,9 +1967,6 @@ func TestCollectExplicitStateChunkSourceHeaderUnitStreamForUpdateUsesChunkProvid if err != nil { t.Fatalf("collectExplicitStateChunkSourceHeaderUnitStreamForUpdate() error = %v", err) } - if !ok { - t.Fatal("expected chunk provider to be used") - } item, ok := <-unitStream if !ok { t.Fatal("expected one source unit") diff --git a/relay/lcp.go b/relay/lcp.go index 256d3ed5..f4357ec5 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -396,16 +396,18 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() - if results, ok, err := pr.tryExplicitStateUpdateClient( - ctx, - sourceChain, - latestHeader, - elcClientID, - includeState, - signer, - ); err != nil { - return nil, err - } else if ok { + if pr.shouldUseExplicitStateUpdateClient() { + results, err := pr.executeExplicitStateUpdateClient( + ctx, + sourceChain, + latestHeader, + elcClientID, + includeState, + signer, + ) + if err != nil { + return nil, err + } responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) for _, result := range results { responses = append(responses, &elc.MsgUpdateClientResponse{ diff --git a/relay/prover.go b/relay/prover.go index a39722bf..9c9170b3 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -298,16 +298,18 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // updateELCForUpdateClient performs the initial setup and updateClient calls // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { - if results, ok, err := pr.tryExplicitStateUpdateClient( - ctx, - dstChain, - latestFinalizedHeader, - pr.config.ElcClientId, - false, - pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes(), - ); err != nil { - return nil, err - } else if ok { + if pr.shouldUseExplicitStateUpdateClient() { + results, err := pr.executeExplicitStateUpdateClient( + ctx, + dstChain, + latestFinalizedHeader, + pr.config.ElcClientId, + false, + pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes(), + ) + if err != nil { + return nil, err + } return pr.validateUpdateELCResults(ctx, latestFinalizedHeader, results) } @@ -409,32 +411,28 @@ func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) } -func (pr *Prover) tryExplicitStateUpdateClient( +// executeExplicitStateUpdateClient runs the explicit-state update client path. +// Callers must gate it with shouldUseExplicitStateUpdateClient; once entered, +// failures are returned as-is and never fall back to the serial path. +func (pr *Prover) executeExplicitStateUpdateClient( ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header, elcClientID string, includeState bool, signer []byte, -) ([]*elcupdater_storage.UpdateClientResult, bool, error) { - if !pr.shouldUseExplicitStateUpdateClient() { - return nil, false, nil - } +) ([]*elcupdater_storage.UpdateClientResult, error) { const maxExplicitStateAttempts = 2 for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { base, err := pr.queryExplicitStateBase(ctx, dstChain, elcClientID) if err != nil { - return nil, true, err + return nil, err } explicitStateCtx, cancelExplicitState := context.WithCancel(ctx) - sourceHeaderUnitStream, ok, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) + sourceHeaderUnitStream, err := pr.collectExplicitStateChunkSourceHeaderUnitStreamForUpdate(explicitStateCtx, dstChain, latestFinalizedHeader, base) if err != nil { cancelExplicitState() - return nil, true, err - } - if !ok { - cancelExplicitState() - return nil, false, nil + return nil, err } results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( ctx, @@ -464,20 +462,20 @@ func (pr *Prover) tryExplicitStateUpdateClient( // previous update was executed in LCP but never landed on-chain). // The explicit-state path cannot anchor at a non-latest canonical // state, so it cannot heal this divergence by itself. - return nil, true, fmt.Errorf( + return nil, fmt.Errorf( "explicit-state base state mismatch persisted after %d attempts; if the LCP canonical state is ahead of the on-chain committed state, disable enable_explicit_state_update_client for one update cycle so the serial path can heal the gap: %w", maxExplicitStateAttempts, err, ) } - return nil, true, err + return nil, err } - return results, true, nil + return results, nil } // Unreachable while maxExplicitStateAttempts is positive: the final attempt // returns the concrete execution error instead of retrying. Keep this guard so // the function still fails closed if the attempt count is changed. - return nil, true, fmt.Errorf("explicit-state update client exhausted retries: client_id=%s", elcClientID) + return nil, fmt.Errorf("explicit-state update client exhausted retries: client_id=%s", elcClientID) } func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( @@ -485,19 +483,20 @@ func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header, base *ExplicitStateBase, -) (<-chan *ExplicitStateSourceHeaderUnitOrError, bool, error) { +) (<-chan *ExplicitStateSourceHeaderUnitOrError, error) { originProver := unwrapExplicitStateOriginProver(pr.originProver) - if provider, ok := originProver.(ExplicitStateChunkProvider); ok { - unitStream, err := provider.SetupExplicitStateChunksForUpdate(ctx, dstChain, latestFinalizedHeader, base) - if err != nil { - return nil, true, err - } - if unitStream == nil { - return nil, true, fmt.Errorf("explicit-state chunk provider returned nil source header unit stream") - } - return unitStream, true, nil + provider, ok := originProver.(ExplicitStateChunkProvider) + if !ok { + return nil, fmt.Errorf("origin prover %T does not implement ExplicitStateChunkProvider", originProver) + } + unitStream, err := provider.SetupExplicitStateChunksForUpdate(ctx, dstChain, latestFinalizedHeader, base) + if err != nil { + return nil, err + } + if unitStream == nil { + return nil, fmt.Errorf("explicit-state chunk provider returned nil source header unit stream") } - return nil, false, nil + return unitStream, nil } func unwrapExplicitStateOriginProver(prover core.Prover) core.Prover { From 5e3763e54119e3b5252e20ca5068040789c9d16a Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 11 Jun 2026 10:14:30 +0900 Subject: [PATCH 25/29] Remove explicit-state ELC update wrapper --- relay/explicit_state_plan_test.go | 8 ++++---- relay/prover.go | 25 ++----------------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 186dadaa..e895ff38 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -847,7 +847,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn lcpServiceClient: NewLCPServiceClient(conn), } go func() { - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + results, err := pr.executeExplicitStateSourceHeaderUnitStream( context.Background(), unitStream, nil, @@ -890,7 +890,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamSendsUnitBeforeReceivingAllUn close(unitStream) if err := <-done; err != nil { - t.Fatalf("executeExplicitStateELCUpdateSourceHeaderUnitStream() error = %v", err) + t.Fatalf("executeExplicitStateSourceHeaderUnitStream() error = %v", err) } } @@ -955,7 +955,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamRejectsNilAndIncompleteBaseSt config: ProverConfig{ElcClientId: "07-tendermint-11"}, lcpServiceClient: NewLCPServiceClient(conn), } - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + results, err := pr.executeExplicitStateSourceHeaderUnitStream( context.Background(), unitStream, nil, @@ -1843,7 +1843,7 @@ func TestExecuteExplicitStateSourceHeaderUnitStreamCancelsBlockedSourceProducerO }, lcpServiceClient: NewLCPServiceClient(conn), } - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + results, err := pr.executeExplicitStateSourceHeaderUnitStream( context.Background(), unitStream, nil, diff --git a/relay/prover.go b/relay/prover.go index 9c9170b3..9fb64ab4 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -389,28 +389,6 @@ func (pr *Prover) executeELCUpdateHeaderStream( return results, nil } -func (pr *Prover) executeExplicitStateELCUpdateSourceHeaderUnitStream( - ctx context.Context, - sourceHeaderUnitStream <-chan *ExplicitStateSourceHeaderUnitOrError, - base *ExplicitStateBase, - elcClientID string, - includeState bool, - signer []byte, -) ([]*elcupdater_storage.UpdateClientResult, error) { - results, err := pr.executeExplicitStateSourceHeaderUnitStream( - ctx, - sourceHeaderUnitStream, - base, - elcClientID, - includeState, - signer, - ) - if err == nil { - return results, nil - } - return nil, fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) -} - // executeExplicitStateUpdateClient runs the explicit-state update client path. // Callers must gate it with shouldUseExplicitStateUpdateClient; once entered, // failures are returned as-is and never fall back to the serial path. @@ -434,7 +412,7 @@ func (pr *Prover) executeExplicitStateUpdateClient( cancelExplicitState() return nil, err } - results, err := pr.executeExplicitStateELCUpdateSourceHeaderUnitStream( + results, err := pr.executeExplicitStateSourceHeaderUnitStream( ctx, sourceHeaderUnitStream, base, @@ -444,6 +422,7 @@ func (pr *Prover) executeExplicitStateUpdateClient( ) cancelExplicitState() if err != nil { + err = fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) if isExplicitStateBaseStateMismatchError(err) { if attempt+1 < maxExplicitStateAttempts { From 3fb53e4f690978578da95713f4f828426ae083ee Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 11 Jun 2026 11:28:08 +0900 Subject: [PATCH 26/29] Split explicit-state base paths by caller --- relay/explicit_state_plan_test.go | 6 +-- relay/explicit_state_update_client.go | 15 +++---- relay/lcp.go | 2 +- relay/prover.go | 62 ++++++++++++++++++++++++--- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index e895ff38..45073602 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -1163,7 +1163,7 @@ func TestQueryLCPCanonicalExplicitStateBase(t *testing.T) { } } -func TestQueryExplicitStateBaseUsesOnChainCommittedHeight(t *testing.T) { +func TestQueryOnChainExplicitStateBaseWithFallbackUsesOnChainCommittedHeight(t *testing.T) { interfaceRegistry := codectypes.NewInterfaceRegistry() std.RegisterInterfaces(interfaceRegistry) lcptypes.RegisterInterfaces(interfaceRegistry) @@ -1179,9 +1179,9 @@ func TestQueryExplicitStateBaseUsesOnChainCommittedHeight(t *testing.T) { base, err := (&Prover{ codec: coreCodec, originProver: originProver, - }).queryExplicitStateBase(context.Background(), chain, "07-tendermint-11") + }).queryOnChainExplicitStateBaseWithFallback(context.Background(), chain, "07-tendermint-11") if err != nil { - t.Fatalf("queryExplicitStateBase() error = %v", err) + t.Fatalf("queryOnChainExplicitStateBaseWithFallback() error = %v", err) } if base == nil || base.Height.RevisionHeight != onChainHeight.RevisionHeight { t.Fatalf("unexpected base height: %#v", base) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index fffa1011..f485b1e3 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -93,16 +93,11 @@ func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcCli }, nil } -func (pr *Prover) queryExplicitStateBase(ctx context.Context, dstChain core.FinalityAwareChain, elcClientID string) (*ExplicitStateBase, error) { - switch dstChain.(type) { - case LCPQuerier, *LCPQuerier: - pr.getLogger().InfoContext( - ctx, - "using LCP canonical explicit-state base for local LCP update", - "elc_client_id", elcClientID, - ) - return pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) - } +// queryOnChainExplicitStateBaseWithFallback prefers the on-chain committed +// base. It falls back to the LCP canonical base only when the destination +// does not host an LCP client (e.g. test/mock configurations); query +// failures are returned as errors instead of silently weakening the base. +func (pr *Prover) queryOnChainExplicitStateBaseWithFallback(ctx context.Context, dstChain core.FinalityAwareChain, elcClientID string) (*ExplicitStateBase, error) { base, ok, err := pr.queryOnChainCommittedExplicitStateBase(ctx, dstChain) if err != nil { return nil, err diff --git a/relay/lcp.go b/relay/lcp.go index f4357ec5..6fe034ab 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -397,7 +397,7 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat sourceChain := NewLCPQuerier(pr.lcpServiceClient, elcClientID) signer := pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes() if pr.shouldUseExplicitStateUpdateClient() { - results, err := pr.executeExplicitStateUpdateClient( + results, err := pr.executeCanonicalBaseExplicitStateUpdateClient( ctx, sourceChain, latestHeader, diff --git a/relay/prover.go b/relay/prover.go index 9fb64ab4..bd104f37 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -299,7 +299,7 @@ func (pr *Prover) SetupHeadersForUpdate(ctx context.Context, dstChain core.Final // Returns the processed updateClient results for aggregation func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header) ([]*elcupdater_storage.UpdateClientResult, error) { if pr.shouldUseExplicitStateUpdateClient() { - results, err := pr.executeExplicitStateUpdateClient( + results, err := pr.executeOnChainBaseExplicitStateUpdateClient( ctx, dstChain, latestFinalizedHeader, @@ -389,20 +389,70 @@ func (pr *Prover) executeELCUpdateHeaderStream( return results, nil } -// executeExplicitStateUpdateClient runs the explicit-state update client path. -// Callers must gate it with shouldUseExplicitStateUpdateClient; once entered, -// failures are returned as-is and never fall back to the serial path. -func (pr *Prover) executeExplicitStateUpdateClient( +// executeOnChainBaseExplicitStateUpdateClient runs the explicit-state update +// client path anchored at the on-chain committed base. Used by the relayer +// path where the resulting messages must connect to the on-chain state. +func (pr *Prover) executeOnChainBaseExplicitStateUpdateClient( ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header, elcClientID string, includeState bool, signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, error) { + return pr.executeExplicitStateUpdateClientWithBase( + ctx, + dstChain, + latestFinalizedHeader, + elcClientID, + includeState, + signer, + func(ctx context.Context) (*ExplicitStateBase, error) { + return pr.queryOnChainExplicitStateBaseWithFallback(ctx, dstChain, elcClientID) + }, + ) +} + +// executeCanonicalBaseExplicitStateUpdateClient runs the explicit-state update +// client path anchored at the LCP canonical base. Used by local LCP update +// flows (updateELC) where no on-chain committed base exists. +func (pr *Prover) executeCanonicalBaseExplicitStateUpdateClient( + ctx context.Context, + sourceChain core.FinalityAwareChain, + latestFinalizedHeader core.Header, + elcClientID string, + includeState bool, + signer []byte, +) ([]*elcupdater_storage.UpdateClientResult, error) { + return pr.executeExplicitStateUpdateClientWithBase( + ctx, + sourceChain, + latestFinalizedHeader, + elcClientID, + includeState, + signer, + func(ctx context.Context) (*ExplicitStateBase, error) { + return pr.queryLCPCanonicalExplicitStateBase(ctx, elcClientID) + }, + ) +} + +// executeExplicitStateUpdateClientWithBase runs the explicit-state update +// client path with the injected base query. Callers must gate it with +// shouldUseExplicitStateUpdateClient; once entered, failures are returned +// as-is and never fall back to the serial path. +func (pr *Prover) executeExplicitStateUpdateClientWithBase( + ctx context.Context, + dstChain core.FinalityAwareChain, + latestFinalizedHeader core.Header, + elcClientID string, + includeState bool, + signer []byte, + queryBase func(context.Context) (*ExplicitStateBase, error), ) ([]*elcupdater_storage.UpdateClientResult, error) { const maxExplicitStateAttempts = 2 for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { - base, err := pr.queryExplicitStateBase(ctx, dstChain, elcClientID) + base, err := queryBase(ctx) if err != nil { return nil, err } From 75619d720c5be367db7ff11362ef67768e1bdef8 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 11 Jun 2026 16:50:55 +0900 Subject: [PATCH 27/29] Simplify explicit-state update client internals --- relay/explicit_state_plan_test.go | 29 ++++++++------------- relay/explicit_state_source_header_units.go | 11 -------- relay/lcp.go | 17 +++++------- relay/prover.go | 10 +++---- 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/relay/explicit_state_plan_test.go b/relay/explicit_state_plan_test.go index 45073602..c4e3978c 100644 --- a/relay/explicit_state_plan_test.go +++ b/relay/explicit_state_plan_test.go @@ -34,10 +34,6 @@ import ( "google.golang.org/grpc/test/bufconn" ) -type explicitStateBatchTestService interface { - SpeculativeUpdateClientBatchStream(elc.Msg_SpeculativeUpdateClientBatchStreamServer) error -} - type unsupportedSpeculativeBatchServer struct { elc.UnimplementedQueryServer elc.UnimplementedMsgServer @@ -531,6 +527,17 @@ func (p *fakeOriginProverWithBase) SetupExplicitStateChunksForUpdate( }), nil } +func makeExplicitStateSourceHeaderUnitStream( + units []*ExplicitStateSourceHeaderUnit, +) <-chan *ExplicitStateSourceHeaderUnitOrError { + ch := make(chan *ExplicitStateSourceHeaderUnitOrError, len(units)) + for _, unit := range units { + ch <- &ExplicitStateSourceHeaderUnitOrError{Unit: unit} + } + close(ch) + return ch +} + type fakeOriginProverWithInitialState struct { fakeOriginProver requestedHeights []ibcexported.Height @@ -768,20 +775,6 @@ func mustPackTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) *c return anyHeader } -// mustBuildTMHeaderForExplicitStateTest returns both the typed header and its -// packed Any form. Use it for tests that need both representations. -func mustBuildTMHeaderForExplicitStateTest(t *testing.T, trustedHeight uint64) (*tmclienttypes.Header, *codectypes.Any) { - t.Helper() - header := &tmclienttypes.Header{ - TrustedHeight: clienttypes.Height{RevisionHeight: trustedHeight}, - } - anyHeader, err := codectypes.NewAnyWithValue(header) - if err != nil { - t.Fatalf("failed to pack tendermint header: %v", err) - } - return header, anyHeader -} - func (p fakeOriginProver) CheckRefreshRequired(context.Context, core.ChainInfoICS02Querier) (bool, error) { return false, nil } diff --git a/relay/explicit_state_source_header_units.go b/relay/explicit_state_source_header_units.go index 81c32e84..2ecb4222 100644 --- a/relay/explicit_state_source_header_units.go +++ b/relay/explicit_state_source_header_units.go @@ -31,17 +31,6 @@ type ExplicitStateSourceHeaderUnitOrError struct { Error error } -func makeExplicitStateSourceHeaderUnitStream( - units []*ExplicitStateSourceHeaderUnit, -) <-chan *ExplicitStateSourceHeaderUnitOrError { - ch := make(chan *ExplicitStateSourceHeaderUnitOrError, len(units)) - for _, unit := range units { - ch <- &ExplicitStateSourceHeaderUnitOrError{Unit: unit} - } - close(ch) - return ch -} - func drainExplicitStateSourceHeaderUnitStreamDiscard( unitStream <-chan *ExplicitStateSourceHeaderUnitOrError, ) { diff --git a/relay/lcp.go b/relay/lcp.go index 6fe034ab..98875cc7 100644 --- a/relay/lcp.go +++ b/relay/lcp.go @@ -25,6 +25,7 @@ import ( lcptypes "github.com/datachainlab/lcp-go/light-clients/lcp/types" "github.com/datachainlab/lcp-go/relay/elc" + elcupdater_storage "github.com/datachainlab/lcp-go/relay/elcupdater/storage" "github.com/datachainlab/lcp-go/relay/enclave" "github.com/datachainlab/lcp-go/sgx" "github.com/datachainlab/lcp-go/sgx/dcap" @@ -408,14 +409,7 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat if err != nil { return nil, err } - responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) - for _, result := range results { - responses = append(responses, &elc.MsgUpdateClientResponse{ - Message: result.Message, - Signature: result.Signature, - }) - } - return responses, nil + return toMsgUpdateClientResponses(results), nil } // 2. query the header from the upstream chain. @@ -434,6 +428,10 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat if err != nil { return nil, err } + return toMsgUpdateClientResponses(results), nil +} + +func toMsgUpdateClientResponses(results []*elcupdater_storage.UpdateClientResult) []*elc.MsgUpdateClientResponse { responses := make([]*elc.MsgUpdateClientResponse, 0, len(results)) for _, result := range results { responses = append(responses, &elc.MsgUpdateClientResponse{ @@ -441,8 +439,7 @@ func (pr *Prover) updateELC(ctx context.Context, elcClientID string, includeStat Signature: result.Signature, }) } - - return responses, nil + return responses } func (pr *Prover) registerEnclaveKey(ctx context.Context, counterparty core.FinalityAwareChain, eki *enclave.EnclaveKeyInfo) (core.MsgID, error) { diff --git a/relay/prover.go b/relay/prover.go index bd104f37..8716e70a 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -451,7 +451,7 @@ func (pr *Prover) executeExplicitStateUpdateClientWithBase( queryBase func(context.Context) (*ExplicitStateBase, error), ) ([]*elcupdater_storage.UpdateClientResult, error) { const maxExplicitStateAttempts = 2 - for attempt := 0; attempt < maxExplicitStateAttempts; attempt++ { + for attempt := 1; ; attempt++ { base, err := queryBase(ctx) if err != nil { return nil, err @@ -475,12 +475,12 @@ func (pr *Prover) executeExplicitStateUpdateClientWithBase( err = fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) if isExplicitStateBaseStateMismatchError(err) { - if attempt+1 < maxExplicitStateAttempts { + if attempt < maxExplicitStateAttempts { pr.getLogger().WarnContext( ctx, "explicit-state update client base state mismatch; retrying from committed explicit-state base", "client_id", elcClientID, - "attempt", attempt+1, + "attempt", attempt, "max_attempts", maxExplicitStateAttempts, "error", err, ) @@ -501,10 +501,6 @@ func (pr *Prover) executeExplicitStateUpdateClientWithBase( } return results, nil } - // Unreachable while maxExplicitStateAttempts is positive: the final attempt - // returns the concrete execution error instead of retrying. Keep this guard so - // the function still fails closed if the attempt count is changed. - return nil, fmt.Errorf("explicit-state update client exhausted retries: client_id=%s", elcClientID) } func (pr *Prover) collectExplicitStateChunkSourceHeaderUnitStreamForUpdate( From 482289535713ea421564cf2c37d2e28797171f2f Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Thu, 11 Jun 2026 19:10:33 +0900 Subject: [PATCH 28/29] Drop constant includeState from on-chain base path --- relay/prover.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/relay/prover.go b/relay/prover.go index 8716e70a..f2bf25a1 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -304,7 +304,6 @@ func (pr *Prover) updateELCForUpdateClient(ctx context.Context, dstChain core.Fi dstChain, latestFinalizedHeader, pr.config.ElcClientId, - false, pr.activeEnclaveKey.GetEnclaveKeyAddress().Bytes(), ) if err != nil { @@ -392,12 +391,13 @@ func (pr *Prover) executeELCUpdateHeaderStream( // executeOnChainBaseExplicitStateUpdateClient runs the explicit-state update // client path anchored at the on-chain committed base. Used by the relayer // path where the resulting messages must connect to the on-chain state. +// include_state is always false here: this path only runs against an +// already-activated on-chain client, which never needs emitted states. func (pr *Prover) executeOnChainBaseExplicitStateUpdateClient( ctx context.Context, dstChain core.FinalityAwareChain, latestFinalizedHeader core.Header, elcClientID string, - includeState bool, signer []byte, ) ([]*elcupdater_storage.UpdateClientResult, error) { return pr.executeExplicitStateUpdateClientWithBase( @@ -405,7 +405,7 @@ func (pr *Prover) executeOnChainBaseExplicitStateUpdateClient( dstChain, latestFinalizedHeader, elcClientID, - includeState, + false, signer, func(ctx context.Context) (*ExplicitStateBase, error) { return pr.queryOnChainExplicitStateBaseWithFallback(ctx, dstChain, elcClientID) From f73c0b4308fa0b580181d0b4838dea272a8393b4 Mon Sep 17 00:00:00 2001 From: Kiyoshi Nakao Date: Wed, 17 Jun 2026 12:42:43 +0900 Subject: [PATCH 29/29] relay: retry explicit-state batch on transient stream failure io.EOF, gRPC DeadlineExceeded/Unavailable, and LCP idle/memory timeouts are now treated as transient and reuse the existing maxExplicitStateAttempts retry budget alongside base-state mismatch. --- relay/explicit_state_update_client.go | 23 +++++++++++++++++++++++ relay/prover.go | 25 +++++++++++++++++-------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/relay/explicit_state_update_client.go b/relay/explicit_state_update_client.go index f485b1e3..bf96daa2 100644 --- a/relay/explicit_state_update_client.go +++ b/relay/explicit_state_update_client.go @@ -3,7 +3,9 @@ package relay import ( "bytes" "context" + "errors" "fmt" + "io" "strings" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -51,6 +53,27 @@ func isExplicitStateBaseStateMismatchError(err error) bool { strings.Contains(msg, "stored speculative base") } +// isStreamTransientError reports whether a speculative batch stream error +// looks like a transient transport / upstream-pacing failure that can be +// recovered by re-sending the batch on a new stream. +func isStreamTransientError(err error) bool { + if err == nil { + return false + } + if errors.Is(err, io.EOF) { + return true + } + if s, ok := status.FromError(err); ok { + switch s.Code() { + case codes.DeadlineExceeded, codes.Unavailable: + return true + } + } + msg := err.Error() + return strings.Contains(msg, "stream idle timeout") || + strings.Contains(msg, "speculative header memory budget") +} + func (pr *Prover) queryLCPCanonicalExplicitStateBase(ctx context.Context, elcClientID string) (*ExplicitStateBase, error) { res, err := pr.lcpServiceClient.Client(ctx, &elc.QueryClientRequest{ ClientId: elcClientID, diff --git a/relay/prover.go b/relay/prover.go index f2bf25a1..5b2e7adf 100644 --- a/relay/prover.go +++ b/relay/prover.go @@ -474,25 +474,34 @@ func (pr *Prover) executeExplicitStateUpdateClientWithBase( if err != nil { err = fmt.Errorf("failed to update ELC: elc_client_id=%v %w", elcClientID, err) drainExplicitStateSourceHeaderUnitStreamDiscard(sourceHeaderUnitStream) - if isExplicitStateBaseStateMismatchError(err) { + isBSM := isExplicitStateBaseStateMismatchError(err) + isTransient := !isBSM && isStreamTransientError(err) + if isBSM || isTransient { if attempt < maxExplicitStateAttempts { + kind := "stream_transient" + if isBSM { + kind = "base_state_mismatch" + } pr.getLogger().WarnContext( ctx, - "explicit-state update client base state mismatch; retrying from committed explicit-state base", + "explicit-state update client batch failed transiently; retrying", "client_id", elcClientID, "attempt", attempt, "max_attempts", maxExplicitStateAttempts, + "kind", kind, "error", err, ) continue } - // A mismatch that survives a fresh base query usually means the LCP - // canonical state is ahead of the on-chain committed state (e.g. a - // previous update was executed in LCP but never landed on-chain). - // The explicit-state path cannot anchor at a non-latest canonical - // state, so it cannot heal this divergence by itself. + if isBSM { + return nil, fmt.Errorf( + "explicit-state base state mismatch persisted after %d attempts; if the LCP canonical state is ahead of the on-chain committed state, disable enable_explicit_state_update_client for one update cycle so the serial path can heal the gap: %w", + maxExplicitStateAttempts, + err, + ) + } return nil, fmt.Errorf( - "explicit-state base state mismatch persisted after %d attempts; if the LCP canonical state is ahead of the on-chain committed state, disable enable_explicit_state_update_client for one update cycle so the serial path can heal the gap: %w", + "explicit-state batch failed after %d attempts due to transient stream errors: %w", maxExplicitStateAttempts, err, )