From 1635faf0625e2d72f8e42451f66a50e038029971 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 26 May 2026 12:17:05 +0530 Subject: [PATCH 01/17] feat: added tss signing deadline in chainConfig and pendingOutboundEntry --- api/uexecutor/v1/query.pulsar.go | 351 +++++++++++++++----------- api/uregistry/v1/types.pulsar.go | 246 ++++++++++++------ proto/uexecutor/v1/query.proto | 1 + proto/uregistry/v1/types.proto | 2 + x/uexecutor/keeper/create_outbound.go | 15 +- x/uexecutor/types/query.pb.go | 180 +++++++------ x/uregistry/types/chain_config.go | 4 + x/uregistry/types/types.pb.go | 223 ++++++++++------ 8 files changed, 648 insertions(+), 374 deletions(-) diff --git a/api/uexecutor/v1/query.pulsar.go b/api/uexecutor/v1/query.pulsar.go index c1dd3ea3e..f81871dbb 100644 --- a/api/uexecutor/v1/query.pulsar.go +++ b/api/uexecutor/v1/query.pulsar.go @@ -7389,10 +7389,11 @@ func (x *fastReflection_QueryAllUniversalTxResponse) ProtoMethods() *protoiface. } var ( - md_PendingOutboundEntry protoreflect.MessageDescriptor - fd_PendingOutboundEntry_outbound_id protoreflect.FieldDescriptor - fd_PendingOutboundEntry_universal_tx_id protoreflect.FieldDescriptor - fd_PendingOutboundEntry_created_at protoreflect.FieldDescriptor + md_PendingOutboundEntry protoreflect.MessageDescriptor + fd_PendingOutboundEntry_outbound_id protoreflect.FieldDescriptor + fd_PendingOutboundEntry_universal_tx_id protoreflect.FieldDescriptor + fd_PendingOutboundEntry_created_at protoreflect.FieldDescriptor + fd_PendingOutboundEntry_signing_deadline protoreflect.FieldDescriptor ) func init() { @@ -7401,6 +7402,7 @@ func init() { fd_PendingOutboundEntry_outbound_id = md_PendingOutboundEntry.Fields().ByName("outbound_id") fd_PendingOutboundEntry_universal_tx_id = md_PendingOutboundEntry.Fields().ByName("universal_tx_id") fd_PendingOutboundEntry_created_at = md_PendingOutboundEntry.Fields().ByName("created_at") + fd_PendingOutboundEntry_signing_deadline = md_PendingOutboundEntry.Fields().ByName("signing_deadline") } var _ protoreflect.Message = (*fastReflection_PendingOutboundEntry)(nil) @@ -7486,6 +7488,12 @@ func (x *fastReflection_PendingOutboundEntry) Range(f func(protoreflect.FieldDes return } } + if x.SigningDeadline != int64(0) { + value := protoreflect.ValueOfInt64(x.SigningDeadline) + if !f(fd_PendingOutboundEntry_signing_deadline, value) { + return + } + } } // Has reports whether a field is populated. @@ -7507,6 +7515,8 @@ func (x *fastReflection_PendingOutboundEntry) Has(fd protoreflect.FieldDescripto return x.UniversalTxId != "" case "uexecutor.v1.PendingOutboundEntry.created_at": return x.CreatedAt != int64(0) + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + return x.SigningDeadline != int64(0) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7529,6 +7539,8 @@ func (x *fastReflection_PendingOutboundEntry) Clear(fd protoreflect.FieldDescrip x.UniversalTxId = "" case "uexecutor.v1.PendingOutboundEntry.created_at": x.CreatedAt = int64(0) + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + x.SigningDeadline = int64(0) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7554,6 +7566,9 @@ func (x *fastReflection_PendingOutboundEntry) Get(descriptor protoreflect.FieldD case "uexecutor.v1.PendingOutboundEntry.created_at": value := x.CreatedAt return protoreflect.ValueOfInt64(value) + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + value := x.SigningDeadline + return protoreflect.ValueOfInt64(value) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7580,6 +7595,8 @@ func (x *fastReflection_PendingOutboundEntry) Set(fd protoreflect.FieldDescripto x.UniversalTxId = value.Interface().(string) case "uexecutor.v1.PendingOutboundEntry.created_at": x.CreatedAt = value.Int() + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + x.SigningDeadline = value.Int() default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7606,6 +7623,8 @@ func (x *fastReflection_PendingOutboundEntry) Mutable(fd protoreflect.FieldDescr panic(fmt.Errorf("field universal_tx_id of message uexecutor.v1.PendingOutboundEntry is not mutable")) case "uexecutor.v1.PendingOutboundEntry.created_at": panic(fmt.Errorf("field created_at of message uexecutor.v1.PendingOutboundEntry is not mutable")) + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + panic(fmt.Errorf("field signing_deadline of message uexecutor.v1.PendingOutboundEntry is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7625,6 +7644,8 @@ func (x *fastReflection_PendingOutboundEntry) NewField(fd protoreflect.FieldDesc return protoreflect.ValueOfString("") case "uexecutor.v1.PendingOutboundEntry.created_at": return protoreflect.ValueOfInt64(int64(0)) + case "uexecutor.v1.PendingOutboundEntry.signing_deadline": + return protoreflect.ValueOfInt64(int64(0)) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uexecutor.v1.PendingOutboundEntry")) @@ -7705,6 +7726,9 @@ func (x *fastReflection_PendingOutboundEntry) ProtoMethods() *protoiface.Methods if x.CreatedAt != 0 { n += 1 + runtime.Sov(uint64(x.CreatedAt)) } + if x.SigningDeadline != 0 { + n += 1 + runtime.Sov(uint64(x.SigningDeadline)) + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -7734,6 +7758,11 @@ func (x *fastReflection_PendingOutboundEntry) ProtoMethods() *protoiface.Methods i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if x.SigningDeadline != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.SigningDeadline)) + i-- + dAtA[i] = 0x20 + } if x.CreatedAt != 0 { i = runtime.EncodeVarint(dAtA, i, uint64(x.CreatedAt)) i-- @@ -7885,6 +7914,25 @@ func (x *fastReflection_PendingOutboundEntry) ProtoMethods() *protoiface.Methods break } } + case 4: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SigningDeadline", wireType) + } + x.SigningDeadline = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.SigningDeadline |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -10611,9 +10659,10 @@ type PendingOutboundEntry struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OutboundId string `protobuf:"bytes,1,opt,name=outbound_id,json=outboundId,proto3" json:"outbound_id,omitempty"` - UniversalTxId string `protobuf:"bytes,2,opt,name=universal_tx_id,json=universalTxId,proto3" json:"universal_tx_id,omitempty"` - CreatedAt int64 `protobuf:"varint,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + OutboundId string `protobuf:"bytes,1,opt,name=outbound_id,json=outboundId,proto3" json:"outbound_id,omitempty"` + UniversalTxId string `protobuf:"bytes,2,opt,name=universal_tx_id,json=universalTxId,proto3" json:"universal_tx_id,omitempty"` + CreatedAt int64 `protobuf:"varint,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + SigningDeadline int64 `protobuf:"varint,4,opt,name=signing_deadline,json=signingDeadline,proto3" json:"signing_deadline,omitempty"` // unix timestamp after which the TSS signature expires on the destination chain (0 = no expiry) } func (x *PendingOutboundEntry) Reset() { @@ -10657,6 +10706,13 @@ func (x *PendingOutboundEntry) GetCreatedAt() int64 { return 0 } +func (x *PendingOutboundEntry) GetSigningDeadline() int64 { + if x != nil { + return x.SigningDeadline + } + return 0 +} + type QueryGetPendingOutboundRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -10932,150 +10988,153 @@ var file_uexecutor_v1_query_proto_rawDesc = []byte{ 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7e, 0x0a, 0x14, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x6c, 0x5f, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x41, 0x0a, 0x1e, + 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa9, 0x01, 0x0a, + 0x14, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x6c, 0x5f, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x29, 0x0a, + 0x10, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x41, 0x0a, 0x1e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, + 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x22, 0x91, 0x01, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, - 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x64, 0x22, - 0x91, 0x01, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x34, 0x0a, - 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x22, 0x69, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xe1, - 0x01, 0x0a, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x12, 0x36, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x52, 0x09, - 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, 0x67, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x32, 0x8c, 0x0b, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, - 0x76, 0x31, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x99, 0x01, 0x0a, 0x12, 0x41, 0x6c, - 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, - 0x12, 0x2c, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, - 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, - 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x6e, 0x69, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x12, 0x28, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, - 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, - 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x74, - 0x78, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x55, - 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x12, 0x28, 0x2e, 0x75, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, - 0x6c, 0x6c, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x55, 0x6e, 0x69, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, - 0x5f, 0x74, 0x78, 0x73, 0x12, 0x7f, 0x0a, 0x08, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, - 0x12, 0x22, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x12, 0x22, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x2f, 0x7b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x81, 0x01, 0x0a, 0x0c, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x73, - 0x50, 0x72, 0x69, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x47, 0x61, - 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, - 0x18, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x23, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x75, + 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x38, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x08, 0x6f, 0x75, 0x74, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x75, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x54, 0x78, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22, + 0x69, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, + 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, + 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xe1, 0x01, 0x0a, 0x20, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, + 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3c, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x36, 0x0a, + 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x78, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x8c, + 0x0b, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x12, 0x20, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, + 0x14, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x99, 0x01, 0x0a, 0x12, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x2c, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x75, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x7b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x12, - 0x85, 0x01, 0x0a, 0x0d, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, - 0x73, 0x12, 0x27, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, - 0x74, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x75, 0x65, 0x78, + 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, - 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x75, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x73, 0x12, 0xa7, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2c, + 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x20, 0x12, 0x1e, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x6c, 0x54, 0x78, 0x12, 0x28, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x55, 0x6e, 0x69, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x75, + 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, + 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x22, 0x12, 0x20, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, + 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x74, 0x78, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x12, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x55, 0x6e, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x12, 0x28, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x55, 0x6e, + 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x6c, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, + 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x74, 0x78, 0x73, + 0x12, 0x7f, 0x0a, 0x08, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2e, 0x12, 0x2c, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, - 0x76, 0x31, 0x2f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, - 0x7d, 0x12, 0x9d, 0x01, 0x0a, 0x13, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x2d, 0x2e, 0x75, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, - 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, - 0x12, 0x1f, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x6f, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x55, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, + 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x61, 0x73, + 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x2f, 0x7b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, + 0x7d, 0x12, 0x81, 0x01, 0x0a, 0x0c, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x26, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x75, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, + 0x6c, 0x6c, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x75, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x61, 0x73, 0x5f, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x73, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, + 0x65, 0x74, 0x61, 0x12, 0x23, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, + 0x2f, 0x7b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x85, 0x01, 0x0a, 0x0d, + 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x12, 0x27, 0x2e, + 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x73, 0x12, 0xa7, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2c, 0x2e, 0x75, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, + 0x65, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x65, 0x74, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, + 0x2c, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2f, + 0x7b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x9d, 0x01, + 0x0a, 0x13, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x2d, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x75, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x42, 0xb2, 0x01, + 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, + 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, + 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, + 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x3a, 0x3a, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/uregistry/v1/types.pulsar.go b/api/uregistry/v1/types.pulsar.go index 50d04888d..ba34deb4f 100644 --- a/api/uregistry/v1/types.pulsar.go +++ b/api/uregistry/v1/types.pulsar.go @@ -2656,6 +2656,7 @@ var ( fd_ChainConfig_enabled protoreflect.FieldDescriptor fd_ChainConfig_gas_oracle_fetch_interval protoreflect.FieldDescriptor fd_ChainConfig_vault_methods protoreflect.FieldDescriptor + fd_ChainConfig_tss_signing_deadline protoreflect.FieldDescriptor ) func init() { @@ -2670,6 +2671,7 @@ func init() { fd_ChainConfig_enabled = md_ChainConfig.Fields().ByName("enabled") fd_ChainConfig_gas_oracle_fetch_interval = md_ChainConfig.Fields().ByName("gas_oracle_fetch_interval") fd_ChainConfig_vault_methods = md_ChainConfig.Fields().ByName("vault_methods") + fd_ChainConfig_tss_signing_deadline = md_ChainConfig.Fields().ByName("tss_signing_deadline") } var _ protoreflect.Message = (*fastReflection_ChainConfig)(nil) @@ -2791,6 +2793,12 @@ func (x *fastReflection_ChainConfig) Range(f func(protoreflect.FieldDescriptor, return } } + if x.TssSigningDeadline != nil { + value := protoreflect.ValueOfMessage(x.TssSigningDeadline.ProtoReflect()) + if !f(fd_ChainConfig_tss_signing_deadline, value) { + return + } + } } // Has reports whether a field is populated. @@ -2824,6 +2832,8 @@ func (x *fastReflection_ChainConfig) Has(fd protoreflect.FieldDescriptor) bool { return x.GasOracleFetchInterval != nil case "uregistry.v1.ChainConfig.vault_methods": return len(x.VaultMethods) != 0 + case "uregistry.v1.ChainConfig.tss_signing_deadline": + return x.TssSigningDeadline != nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uregistry.v1.ChainConfig")) @@ -2858,6 +2868,8 @@ func (x *fastReflection_ChainConfig) Clear(fd protoreflect.FieldDescriptor) { x.GasOracleFetchInterval = nil case "uregistry.v1.ChainConfig.vault_methods": x.VaultMethods = nil + case "uregistry.v1.ChainConfig.tss_signing_deadline": + x.TssSigningDeadline = nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uregistry.v1.ChainConfig")) @@ -2907,6 +2919,9 @@ func (x *fastReflection_ChainConfig) Get(descriptor protoreflect.FieldDescriptor } listValue := &_ChainConfig_9_list{list: &x.VaultMethods} return protoreflect.ValueOfList(listValue) + case "uregistry.v1.ChainConfig.tss_signing_deadline": + value := x.TssSigningDeadline + return protoreflect.ValueOfMessage(value.ProtoReflect()) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uregistry.v1.ChainConfig")) @@ -2949,6 +2964,8 @@ func (x *fastReflection_ChainConfig) Set(fd protoreflect.FieldDescriptor, value lv := value.List() clv := lv.(*_ChainConfig_9_list) x.VaultMethods = *clv.list + case "uregistry.v1.ChainConfig.tss_signing_deadline": + x.TssSigningDeadline = value.Message().Interface().(*durationpb.Duration) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uregistry.v1.ChainConfig")) @@ -2996,6 +3013,11 @@ func (x *fastReflection_ChainConfig) Mutable(fd protoreflect.FieldDescriptor) pr } value := &_ChainConfig_9_list{list: &x.VaultMethods} return protoreflect.ValueOfList(value) + case "uregistry.v1.ChainConfig.tss_signing_deadline": + if x.TssSigningDeadline == nil { + x.TssSigningDeadline = new(durationpb.Duration) + } + return protoreflect.ValueOfMessage(x.TssSigningDeadline.ProtoReflect()) case "uregistry.v1.ChainConfig.chain": panic(fmt.Errorf("field chain of message uregistry.v1.ChainConfig is not mutable")) case "uregistry.v1.ChainConfig.vm_type": @@ -3040,6 +3062,9 @@ func (x *fastReflection_ChainConfig) NewField(fd protoreflect.FieldDescriptor) p case "uregistry.v1.ChainConfig.vault_methods": list := []*VaultMethods{} return protoreflect.ValueOfList(&_ChainConfig_9_list{list: &list}) + case "uregistry.v1.ChainConfig.tss_signing_deadline": + m := new(durationpb.Duration) + return protoreflect.ValueOfMessage(m.ProtoReflect()) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: uregistry.v1.ChainConfig")) @@ -3148,6 +3173,10 @@ func (x *fastReflection_ChainConfig) ProtoMethods() *protoiface.Methods { n += 1 + l + runtime.Sov(uint64(l)) } } + if x.TssSigningDeadline != nil { + l = options.Size(x.TssSigningDeadline) + n += 1 + l + runtime.Sov(uint64(l)) + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -3177,6 +3206,20 @@ func (x *fastReflection_ChainConfig) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if x.TssSigningDeadline != nil { + encoded, err := options.Marshal(x.TssSigningDeadline) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x52 + } if len(x.VaultMethods) > 0 { for iNdEx := len(x.VaultMethods) - 1; iNdEx >= 0; iNdEx-- { encoded, err := options.Marshal(x.VaultMethods[iNdEx]) @@ -3617,6 +3660,42 @@ func (x *fastReflection_ChainConfig) ProtoMethods() *protoiface.Methods { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex + case 10: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field TssSigningDeadline", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.TssSigningDeadline == nil { + x.TssSigningDeadline = &durationpb.Duration{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.TssSigningDeadline); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -5492,6 +5571,7 @@ type ChainConfig struct { Enabled *ChainEnabled `protobuf:"bytes,7,opt,name=enabled,proto3" json:"enabled,omitempty"` // Whether this chain is currently enabled or not GasOracleFetchInterval *durationpb.Duration `protobuf:"bytes,8,opt,name=gas_oracle_fetch_interval,json=gasOracleFetchInterval,proto3" json:"gas_oracle_fetch_interval,omitempty"` // how often relayers should fetch gas prices VaultMethods []*VaultMethods `protobuf:"bytes,9,rep,name=vault_methods,json=vaultMethods,proto3" json:"vault_methods,omitempty"` // List of methods exposed by the vault contract (optional) + TssSigningDeadline *durationpb.Duration `protobuf:"bytes,10,opt,name=tss_signing_deadline,json=tssSigningDeadline,proto3" json:"tss_signing_deadline,omitempty"` // duration added to block time to compute the signature expiry deadline on the destination chain (zero = no expiry) } func (x *ChainConfig) Reset() { @@ -5577,6 +5657,13 @@ func (x *ChainConfig) GetVaultMethods() []*VaultMethods { return nil } +func (x *ChainConfig) GetTssSigningDeadline() *durationpb.Duration { + if x != nil { + return x.TssSigningDeadline + } + return nil +} + type NativeRepresentation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5779,7 +5866,7 @@ var file_uregistry_v1_types_proto_rawDesc = []byte{ 0x08, 0x52, 0x11, 0x69, 0x73, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x3a, 0x24, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x17, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xb4, 0x04, 0x0a, 0x0b, 0x43, + 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x87, 0x05, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x07, 0x76, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -5812,74 +5899,80 @@ var file_uregistry_v1_types_proto_rawDesc = []byte{ 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x0c, - 0x76, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x3a, 0x23, 0x98, 0xa0, - 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x16, 0x75, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x70, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, - 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, - 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x2c, 0x98, 0xa0, 0x1f, - 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x1f, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, - 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfa, 0x02, 0x0a, 0x0b, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, - 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x43, 0x61, 0x70, - 0x12, 0x36, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x6e, 0x61, 0x74, 0x69, - 0x76, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x70, - 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x14, 0x6e, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x3a, 0x23, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x16, - 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2a, 0x91, 0x01, 0x0a, 0x06, 0x56, 0x6d, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x56, 0x4d, 0x10, - 0x00, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x56, 0x4d, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x56, - 0x4d, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x56, 0x4d, 0x10, 0x03, - 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x53, 0x4d, 0x5f, 0x56, 0x4d, 0x10, 0x04, 0x12, 0x0c, 0x0a, - 0x08, 0x43, 0x41, 0x49, 0x52, 0x4f, 0x5f, 0x56, 0x4d, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x54, - 0x52, 0x4f, 0x4e, 0x5f, 0x56, 0x4d, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x54, 0x45, 0x4c, - 0x4c, 0x41, 0x52, 0x5f, 0x56, 0x4d, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x49, 0x54, 0x43, - 0x4f, 0x49, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x08, 0x12, 0x0c, 0x0a, 0x08, - 0x4f, 0x54, 0x48, 0x45, 0x52, 0x5f, 0x56, 0x4d, 0x10, 0x09, 0x2a, 0x4b, 0x0a, 0x09, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, - 0x57, 0x4e, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, - 0x43, 0x32, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x52, 0x43, 0x37, 0x32, 0x31, 0x10, - 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x52, 0x43, 0x31, 0x31, 0x35, 0x35, 0x10, 0x03, 0x12, 0x07, - 0x0a, 0x03, 0x53, 0x50, 0x4c, 0x10, 0x04, 0x2a, 0x68, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x43, - 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, - 0x41, 0x52, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x41, 0x53, 0x54, 0x10, - 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x55, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, 0x51, 0x0a, 0x14, + 0x74, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x04, 0x98, 0xdf, 0x1f, 0x01, 0x52, 0x12, 0x74, 0x73, 0x73, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x3a, + 0x23, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x16, 0x75, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, + 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, + 0x6e, 0x6f, 0x6d, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x2c, + 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x1f, 0x75, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x72, 0x65, + 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfa, 0x02, 0x0a, + 0x0b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, + 0x6d, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, + 0x6d, 0x61, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x6c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x61, 0x70, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, + 0x43, 0x61, 0x70, 0x12, 0x36, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x6e, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x75, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x14, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x23, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, + 0xb0, 0x2a, 0x16, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2a, 0x91, 0x01, 0x0a, 0x06, 0x56, 0x6d, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, + 0x56, 0x4d, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x56, 0x4d, 0x10, 0x01, 0x12, 0x07, 0x0a, + 0x03, 0x53, 0x56, 0x4d, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x56, + 0x4d, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x53, 0x4d, 0x5f, 0x56, 0x4d, 0x10, 0x04, + 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x49, 0x52, 0x4f, 0x5f, 0x56, 0x4d, 0x10, 0x05, 0x12, 0x0b, + 0x0a, 0x07, 0x54, 0x52, 0x4f, 0x4e, 0x5f, 0x56, 0x4d, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x53, + 0x54, 0x45, 0x4c, 0x4c, 0x41, 0x52, 0x5f, 0x56, 0x4d, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x42, + 0x49, 0x54, 0x43, 0x4f, 0x49, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x08, 0x12, + 0x0c, 0x0a, 0x08, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x5f, 0x56, 0x4d, 0x10, 0x09, 0x2a, 0x4b, 0x0a, + 0x09, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x45, 0x52, 0x43, 0x32, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x52, 0x43, 0x37, + 0x32, 0x31, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x52, 0x43, 0x31, 0x31, 0x35, 0x35, 0x10, + 0x03, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x50, 0x4c, 0x10, 0x04, 0x2a, 0x68, 0x0a, 0x10, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, + 0x0a, 0x14, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x52, 0x4d, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, + 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x52, 0x4d, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x41, + 0x53, 0x54, 0x10, 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, + 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x75, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x75, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, + 0xaa, 0x02, 0x0c, 0x55, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x0c, 0x55, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0xe2, 0x02, + 0x18, 0x55, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -5919,13 +6012,14 @@ var file_uregistry_v1_types_proto_depIdxs = []int32{ 7, // 5: uregistry.v1.ChainConfig.enabled:type_name -> uregistry.v1.ChainEnabled 11, // 6: uregistry.v1.ChainConfig.gas_oracle_fetch_interval:type_name -> google.protobuf.Duration 5, // 7: uregistry.v1.ChainConfig.vault_methods:type_name -> uregistry.v1.VaultMethods - 1, // 8: uregistry.v1.TokenConfig.token_type:type_name -> uregistry.v1.TokenType - 9, // 9: uregistry.v1.TokenConfig.native_representation:type_name -> uregistry.v1.NativeRepresentation - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 11, // 8: uregistry.v1.ChainConfig.tss_signing_deadline:type_name -> google.protobuf.Duration + 1, // 9: uregistry.v1.TokenConfig.token_type:type_name -> uregistry.v1.TokenType + 9, // 10: uregistry.v1.TokenConfig.native_representation:type_name -> uregistry.v1.NativeRepresentation + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_uregistry_v1_types_proto_init() } diff --git a/proto/uexecutor/v1/query.proto b/proto/uexecutor/v1/query.proto index e5e9eef50..8ff266276 100755 --- a/proto/uexecutor/v1/query.proto +++ b/proto/uexecutor/v1/query.proto @@ -146,6 +146,7 @@ message PendingOutboundEntry { string outbound_id = 1; string universal_tx_id = 2; int64 created_at = 3; + int64 signing_deadline = 4; // unix timestamp after which the TSS signature expires on the destination chain (0 = no expiry) } message QueryGetPendingOutboundRequest { diff --git a/proto/uregistry/v1/types.proto b/proto/uregistry/v1/types.proto index d0a7dcac6..9155f993b 100644 --- a/proto/uregistry/v1/types.proto +++ b/proto/uregistry/v1/types.proto @@ -114,6 +114,8 @@ message ChainConfig { google.protobuf.Duration gas_oracle_fetch_interval = 8 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; // how often relayers should fetch gas prices repeated VaultMethods vault_methods = 9; // List of methods exposed by the vault contract (optional) + + google.protobuf.Duration tss_signing_deadline = 10 [(gogoproto.stdduration) = true]; // duration added to block time to compute the signature expiry deadline on the destination chain (zero = no expiry) } message NativeRepresentation { diff --git a/x/uexecutor/keeper/create_outbound.go b/x/uexecutor/keeper/create_outbound.go index 16ca83a0a..6a3c5c7e3 100644 --- a/x/uexecutor/keeper/create_outbound.go +++ b/x/uexecutor/keeper/create_outbound.go @@ -352,11 +352,20 @@ func (k Keeper) attachOutboundsToUtx( utx.OutboundTx = append(utx.OutboundTx, outbound) + // Compute signature expiry deadline for the destination chain. + var signingDeadline int64 + if chainCfg, err := k.uregistryKeeper.GetChainConfig(ctx, outbound.DestinationChain); err == nil { + if chainCfg.TssSigningDeadline != nil && *chainCfg.TssSigningDeadline > 0 { + signingDeadline = ctx.BlockTime().Unix() + int64(chainCfg.TssSigningDeadline.Seconds()) + } + } + // Write to pending outbounds index (inside UpdateUniversalTx closure for atomicity) if err := k.PendingOutbounds.Set(ctx, outbound.Id, types.PendingOutboundEntry{ - OutboundId: outbound.Id, - UniversalTxId: utxId, - CreatedAt: ctx.BlockHeight(), + OutboundId: outbound.Id, + UniversalTxId: utxId, + CreatedAt: ctx.BlockHeight(), + SigningDeadline: signingDeadline, }); err != nil { return fmt.Errorf("failed to set pending outbound index for %s: %w", outbound.Id, err) } diff --git a/x/uexecutor/types/query.pb.go b/x/uexecutor/types/query.pb.go index 1f2d6c209..4e964cbed 100644 --- a/x/uexecutor/types/query.pb.go +++ b/x/uexecutor/types/query.pb.go @@ -768,9 +768,10 @@ func (m *QueryAllUniversalTxResponse) GetPagination() *query.PageResponse { // Pending outbound index entry type PendingOutboundEntry struct { - OutboundId string `protobuf:"bytes,1,opt,name=outbound_id,json=outboundId,proto3" json:"outbound_id,omitempty"` - UniversalTxId string `protobuf:"bytes,2,opt,name=universal_tx_id,json=universalTxId,proto3" json:"universal_tx_id,omitempty"` - CreatedAt int64 `protobuf:"varint,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + OutboundId string `protobuf:"bytes,1,opt,name=outbound_id,json=outboundId,proto3" json:"outbound_id,omitempty"` + UniversalTxId string `protobuf:"bytes,2,opt,name=universal_tx_id,json=universalTxId,proto3" json:"universal_tx_id,omitempty"` + CreatedAt int64 `protobuf:"varint,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + SigningDeadline int64 `protobuf:"varint,4,opt,name=signing_deadline,json=signingDeadline,proto3" json:"signing_deadline,omitempty"` } func (m *PendingOutboundEntry) Reset() { *m = PendingOutboundEntry{} } @@ -827,6 +828,13 @@ func (m *PendingOutboundEntry) GetCreatedAt() int64 { return 0 } +func (m *PendingOutboundEntry) GetSigningDeadline() int64 { + if m != nil { + return m.SigningDeadline + } + return 0 +} + type QueryGetPendingOutboundRequest struct { OutboundId string `protobuf:"bytes,1,opt,name=outbound_id,json=outboundId,proto3" json:"outbound_id,omitempty"` } @@ -1054,76 +1062,77 @@ func init() { func init() { proto.RegisterFile("uexecutor/v1/query.proto", fileDescriptor_94816af5d57d33a7) } var fileDescriptor_94816af5d57d33a7 = []byte{ - // 1090 bytes of a gzipped FileDescriptorProto + // 1117 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x97, 0xcf, 0x6f, 0xe3, 0x44, - 0x14, 0xc7, 0x3b, 0xa9, 0xb6, 0xdb, 0xbc, 0xfe, 0x40, 0x7a, 0x1b, 0x4a, 0xea, 0xb6, 0x69, 0xea, - 0x2e, 0x6d, 0x58, 0x5a, 0x5b, 0xe9, 0x2e, 0x15, 0x07, 0x84, 0xd4, 0x5d, 0x41, 0x15, 0x69, 0x11, - 0x21, 0x5a, 0x2e, 0x5c, 0xa2, 0x49, 0x3c, 0x4a, 0x2d, 0x5a, 0x3b, 0x1b, 0xdb, 0x55, 0xaa, 0xaa, - 0x20, 0x40, 0x5c, 0x00, 0x09, 0x10, 0x27, 0x84, 0x10, 0x37, 0xf8, 0x57, 0x38, 0xae, 0xc4, 0x85, - 0x23, 0xb4, 0xfc, 0x21, 0x28, 0xe3, 0x19, 0xc7, 0x76, 0xc6, 0x69, 0xb4, 0xca, 0xcd, 0x99, 0x79, - 0x6f, 0xde, 0xe7, 0xfb, 0x66, 0xe6, 0xcd, 0x0b, 0x14, 0x03, 0xd6, 0x67, 0xed, 0xc0, 0x77, 0x7b, - 0xe6, 0x79, 0xd5, 0x7c, 0x1e, 0xb0, 0xde, 0x85, 0xd1, 0xed, 0xb9, 0xbe, 0x8b, 0x8b, 0xd1, 0x8c, - 0x71, 0x5e, 0xd5, 0xd6, 0x3b, 0xae, 0xdb, 0x39, 0x65, 0x26, 0xed, 0xda, 0x26, 0x75, 0x1c, 0xd7, - 0xa7, 0xbe, 0xed, 0x3a, 0x5e, 0x68, 0xab, 0x25, 0x57, 0xf1, 0x2f, 0xba, 0x4c, 0xce, 0xac, 0x27, - 0x66, 0x3a, 0xd4, 0x6b, 0x76, 0x7b, 0x76, 0x9b, 0x89, 0xd9, 0x8d, 0xc4, 0x6c, 0xfb, 0x84, 0xda, - 0x4e, 0xf3, 0x8c, 0xf9, 0x54, 0x4c, 0x3f, 0x68, 0xbb, 0xde, 0x99, 0xeb, 0x99, 0x2d, 0xea, 0xb1, - 0x90, 0xcd, 0x3c, 0xaf, 0xb6, 0x98, 0x4f, 0xab, 0x66, 0x97, 0x76, 0x6c, 0x87, 0x33, 0x84, 0xb6, - 0x7a, 0x15, 0x0a, 0x1f, 0x0d, 0x2c, 0x8e, 0xa9, 0x57, 0x1f, 0x44, 0x68, 0xb0, 0xe7, 0x01, 0xf3, - 0x7c, 0x5c, 0x85, 0xf9, 0x70, 0x5d, 0xdb, 0x2a, 0x92, 0x32, 0xa9, 0xe4, 0x1b, 0x77, 0xf9, 0xef, - 0x9a, 0xa5, 0x3f, 0x85, 0x57, 0x53, 0x2e, 0x5e, 0xd7, 0x75, 0x3c, 0x86, 0x0f, 0x21, 0x1f, 0x91, - 0x72, 0xa7, 0x85, 0x83, 0x15, 0x23, 0x9e, 0x0e, 0x23, 0x72, 0x99, 0xef, 0x88, 0x2f, 0xbd, 0x05, - 0x45, 0xbe, 0xda, 0xd1, 0xe9, 0xa9, 0x9c, 0xf5, 0x24, 0xc4, 0xfb, 0x00, 0x43, 0x60, 0xb1, 0xe2, - 0x8e, 0x11, 0xaa, 0x33, 0x06, 0xea, 0x8c, 0x30, 0xf3, 0x42, 0x9d, 0x51, 0xa7, 0x1d, 0x29, 0xa0, - 0x11, 0xf3, 0xd4, 0x7f, 0x21, 0xb0, 0xaa, 0x08, 0x22, 0xb0, 0xdf, 0x02, 0x88, 0xb0, 0xbd, 0x22, - 0x29, 0xcf, 0x8e, 0xe1, 0xce, 0x4b, 0x6e, 0x0f, 0x8f, 0x13, 0x70, 0x39, 0x0e, 0xb7, 0x7b, 0x2b, - 0x5c, 0x18, 0x33, 0x41, 0x77, 0x20, 0xf2, 0xf9, 0x64, 0x90, 0xdf, 0x0f, 0x98, 0x4f, 0x27, 0xd8, - 0x83, 0x3a, 0xac, 0xa4, 0x7d, 0x84, 0x9a, 0x43, 0x80, 0xe1, 0x81, 0x10, 0x39, 0x7b, 0x2d, 0xa9, - 0x66, 0xe8, 0x94, 0x6f, 0xcb, 0x4f, 0xbd, 0x3d, 0x4c, 0x51, 0x34, 0x3f, 0xf5, 0x8d, 0xf8, 0x8d, - 0x80, 0xa6, 0x8a, 0x22, 0xd8, 0xdf, 0x86, 0x85, 0x21, 0xbb, 0xdc, 0x8a, 0x4c, 0x78, 0x88, 0xe0, - 0xa7, 0xb8, 0x19, 0x05, 0x40, 0x0e, 0x58, 0xa7, 0x3d, 0x7a, 0x26, 0xf5, 0xeb, 0x4f, 0xe0, 0x5e, - 0x62, 0x54, 0xf0, 0xee, 0xc1, 0x5c, 0x97, 0x8f, 0x88, 0x94, 0x14, 0x92, 0xa8, 0xc2, 0x5a, 0xd8, - 0xe8, 0x27, 0x50, 0x92, 0xda, 0xeb, 0xcc, 0xb1, 0x6c, 0xa7, 0x53, 0x73, 0x5a, 0x6e, 0xe0, 0x58, - 0x53, 0x4f, 0xf3, 0xb7, 0x04, 0x36, 0x33, 0x43, 0x09, 0xf6, 0x4d, 0x58, 0xb0, 0xc3, 0xb1, 0xa6, - 0x6d, 0x85, 0xb9, 0xce, 0x37, 0x40, 0x0c, 0xd5, 0xac, 0x29, 0xa6, 0x74, 0x4f, 0xec, 0xf9, 0x31, - 0xf3, 0x3f, 0x76, 0xec, 0x73, 0xd6, 0xf3, 0xe8, 0xe9, 0xb3, 0xbe, 0xd4, 0xbc, 0x0c, 0xb9, 0xe8, - 0x78, 0xe7, 0x6c, 0x4b, 0xa7, 0xb0, 0xa6, 0xb4, 0x16, 0xd8, 0x8f, 0x61, 0x31, 0x90, 0xc3, 0x4d, - 0xbf, 0x2f, 0x92, 0xb4, 0x99, 0x4c, 0x7c, 0xcc, 0xf1, 0x29, 0xeb, 0xd0, 0xf6, 0x45, 0x63, 0x21, - 0x18, 0x0e, 0xe9, 0xd6, 0xf0, 0x10, 0x2a, 0x80, 0xa6, 0xb5, 0x09, 0xbf, 0x13, 0xa1, 0x24, 0x1d, - 0x46, 0x28, 0x79, 0x17, 0x96, 0xe2, 0x4a, 0xe4, 0x71, 0x5f, 0xcd, 0x94, 0xd2, 0x58, 0x8c, 0x89, - 0x98, 0xe2, 0xfe, 0x7c, 0x06, 0x05, 0x71, 0x48, 0x3e, 0x0c, 0x7c, 0xbe, 0xfd, 0xef, 0x39, 0x7e, - 0xef, 0x62, 0x70, 0x42, 0x5c, 0x31, 0x30, 0xac, 0x40, 0x20, 0x87, 0x6a, 0x16, 0xee, 0xc0, 0x2b, - 0x71, 0x05, 0x03, 0xa3, 0x1c, 0x37, 0x5a, 0x8a, 0x81, 0xd6, 0x2c, 0xdc, 0x00, 0x68, 0xf7, 0x18, - 0xf5, 0x99, 0xd5, 0xa4, 0x7e, 0x71, 0xb6, 0x4c, 0x2a, 0xb3, 0x8d, 0xbc, 0x18, 0x39, 0xf2, 0xf5, - 0x23, 0x71, 0x2f, 0x8e, 0x99, 0x9f, 0xe2, 0x90, 0x5b, 0x72, 0x1b, 0x89, 0xfe, 0xa3, 0x3c, 0xf0, - 0xaa, 0x35, 0xa2, 0xe2, 0x72, 0x87, 0x0d, 0x74, 0x89, 0x2d, 0xd5, 0x53, 0x77, 0x55, 0x91, 0x81, - 0x46, 0xe8, 0x80, 0x8f, 0x60, 0x5e, 0xc6, 0x12, 0x79, 0x2e, 0x26, 0x9d, 0xa5, 0xd7, 0xb3, 0x7e, - 0x23, 0xb2, 0xd4, 0xed, 0x91, 0x3b, 0x28, 0xcd, 0xa6, 0x7e, 0xdf, 0xff, 0x25, 0x50, 0xce, 0x8e, - 0x25, 0xf4, 0xbf, 0x03, 0x77, 0x07, 0x72, 0xec, 0xe8, 0x8d, 0x9b, 0x24, 0x03, 0xd2, 0x05, 0x0f, - 0x21, 0x2f, 0x95, 0x79, 0xc5, 0x1c, 0xf7, 0xcf, 0x4e, 0xc2, 0xd0, 0x34, 0x75, 0x4a, 0x67, 0x5f, - 0xfa, 0x94, 0x1e, 0x7c, 0xb7, 0x00, 0x77, 0xb8, 0x46, 0xfc, 0x14, 0xe6, 0xc2, 0xca, 0x8a, 0xe5, - 0x24, 0xc1, 0x68, 0xe1, 0xd6, 0xb6, 0xc6, 0x58, 0x84, 0x41, 0xf4, 0xf5, 0x2f, 0xff, 0xfa, 0xef, - 0xa7, 0xdc, 0x0a, 0x16, 0xcc, 0x44, 0x57, 0x15, 0x16, 0x6d, 0xfc, 0x99, 0x00, 0x8e, 0x56, 0x51, - 0xdc, 0x53, 0xac, 0x9b, 0x59, 0xd7, 0xb5, 0xfd, 0x09, 0xad, 0x05, 0xd1, 0x0e, 0x27, 0x2a, 0x63, - 0x29, 0x45, 0x14, 0x9a, 0x37, 0x6d, 0x09, 0xf1, 0x3d, 0x81, 0xe5, 0x64, 0x99, 0xc4, 0x8a, 0x22, - 0x92, 0xb2, 0xee, 0x6a, 0x6f, 0x4c, 0x60, 0x29, 0x78, 0x2a, 0x9c, 0x47, 0xc7, 0x72, 0x92, 0x27, - 0x51, 0xbd, 0xcc, 0x4b, 0xdb, 0xba, 0xc2, 0x6f, 0x08, 0x2c, 0x27, 0xcb, 0x9d, 0x92, 0x48, 0x59, - 0x78, 0x95, 0x44, 0xea, 0xda, 0xa9, 0x6f, 0x73, 0xa2, 0x0d, 0x5c, 0x1b, 0x43, 0x84, 0x9f, 0xc3, - 0xbc, 0xec, 0xdb, 0x50, 0x57, 0xa9, 0x4d, 0xb6, 0xbc, 0xda, 0xf6, 0x58, 0x1b, 0x11, 0xf9, 0x01, - 0x8f, 0x7c, 0x1f, 0x75, 0x53, 0xdd, 0xa1, 0x9b, 0x97, 0xb2, 0x65, 0xbb, 0xc2, 0x2f, 0x08, 0x2c, - 0xc6, 0x3b, 0x4e, 0xdc, 0x51, 0x2b, 0x4c, 0xf7, 0xbd, 0xda, 0xee, 0xad, 0x76, 0x82, 0xa6, 0xcc, - 0x69, 0x34, 0x2c, 0x66, 0xd0, 0x78, 0xf8, 0x15, 0x81, 0x7c, 0xd4, 0x32, 0xa1, 0x4a, 0x62, 0xba, - 0xed, 0xd4, 0xee, 0x8f, 0x37, 0x12, 0xa1, 0xdf, 0xe4, 0xa1, 0x5f, 0xc7, 0x6d, 0x33, 0xe3, 0xcf, - 0x48, 0x3c, 0x13, 0x5f, 0x13, 0x58, 0x4a, 0xb4, 0x7c, 0x98, 0x21, 0x71, 0xa4, 0xf5, 0xd4, 0x2a, - 0xb7, 0x1b, 0x0a, 0xa2, 0x2d, 0x4e, 0xb4, 0x86, 0xab, 0x59, 0x44, 0x1e, 0xfe, 0x41, 0x00, 0x47, - 0x9f, 0x08, 0xe5, 0x6d, 0xce, 0x7c, 0x8d, 0x94, 0xb7, 0x39, 0xfb, 0xdd, 0xd1, 0x1f, 0x71, 0x2c, - 0x03, 0xf7, 0xd4, 0xb7, 0x59, 0x96, 0x4a, 0xf3, 0x32, 0xf6, 0xc4, 0x5d, 0xe1, 0xaf, 0x04, 0xee, - 0x29, 0xaa, 0x39, 0x8e, 0x2f, 0x25, 0xe9, 0x17, 0x46, 0x33, 0x26, 0x35, 0x17, 0xb0, 0xbb, 0x1c, - 0x76, 0x0b, 0x37, 0xc7, 0xc3, 0x7a, 0x8f, 0xeb, 0x7f, 0x5e, 0x97, 0xc8, 0x8b, 0xeb, 0x12, 0xf9, - 0xe7, 0xba, 0x44, 0x7e, 0xb8, 0x29, 0xcd, 0xbc, 0xb8, 0x29, 0xcd, 0xfc, 0x7d, 0x53, 0x9a, 0xf9, - 0xe4, 0xb0, 0x63, 0xfb, 0x27, 0x41, 0xcb, 0x68, 0xbb, 0x67, 0x66, 0x37, 0xf0, 0x4e, 0x78, 0xfe, - 0xf9, 0xd7, 0x3e, 0xff, 0xdc, 0x77, 0x5c, 0x8b, 0x99, 0xfd, 0x58, 0x00, 0xfe, 0xc7, 0xb7, 0x35, - 0xc7, 0xff, 0x90, 0x3e, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xa4, 0xe5, 0x73, 0xda, 0x5b, 0x0f, - 0x00, 0x00, + 0x14, 0xc7, 0x3b, 0x29, 0xdb, 0x6d, 0x5e, 0x7f, 0x2c, 0x9a, 0x0d, 0x25, 0x75, 0xdb, 0x34, 0x75, + 0x97, 0x36, 0xbb, 0xb4, 0xb6, 0xd2, 0x5d, 0x2a, 0x0e, 0x08, 0xa9, 0xbb, 0x40, 0x15, 0x69, 0x11, + 0x21, 0x5a, 0x2e, 0x5c, 0xa2, 0x49, 0x3c, 0x72, 0x2d, 0x52, 0x3b, 0x9b, 0xb1, 0xab, 0x54, 0x55, + 0x85, 0x00, 0x71, 0x01, 0x24, 0x40, 0x9c, 0x10, 0x42, 0xdc, 0x40, 0xfc, 0x27, 0x1c, 0x57, 0xe2, + 0xc2, 0x11, 0x5a, 0xfe, 0x10, 0x94, 0xf1, 0x8c, 0x63, 0x3b, 0xe3, 0x34, 0x42, 0xb9, 0x39, 0x33, + 0xef, 0xcd, 0xfb, 0x7c, 0xdf, 0xcc, 0xbc, 0x79, 0x81, 0x62, 0x40, 0xfb, 0xb4, 0x1d, 0xf8, 0x5e, + 0xcf, 0x3c, 0xab, 0x9a, 0xcf, 0x03, 0xda, 0x3b, 0x37, 0xba, 0x3d, 0xcf, 0xf7, 0xf0, 0x62, 0x34, + 0x63, 0x9c, 0x55, 0xb5, 0x75, 0xdb, 0xf3, 0xec, 0x0e, 0x35, 0x49, 0xd7, 0x31, 0x89, 0xeb, 0x7a, + 0x3e, 0xf1, 0x1d, 0xcf, 0x65, 0xa1, 0xad, 0x96, 0x5c, 0xc5, 0x3f, 0xef, 0x52, 0x39, 0xb3, 0x9e, + 0x98, 0xb1, 0x09, 0x6b, 0x76, 0x7b, 0x4e, 0x9b, 0x8a, 0xd9, 0x8d, 0xc4, 0x6c, 0xfb, 0x84, 0x38, + 0x6e, 0xf3, 0x94, 0xfa, 0x44, 0x4c, 0x3f, 0x68, 0x7b, 0xec, 0xd4, 0x63, 0x66, 0x8b, 0x30, 0x1a, + 0xb2, 0x99, 0x67, 0xd5, 0x16, 0xf5, 0x49, 0xd5, 0xec, 0x12, 0xdb, 0x71, 0x39, 0x43, 0x68, 0xab, + 0x57, 0xa1, 0xf0, 0xe1, 0xc0, 0xe2, 0x98, 0xb0, 0xfa, 0x20, 0x42, 0x83, 0x3e, 0x0f, 0x28, 0xf3, + 0xf1, 0x2a, 0xcc, 0x87, 0xeb, 0x3a, 0x56, 0x11, 0x95, 0x51, 0x25, 0xdf, 0xb8, 0xcd, 0x7f, 0xd7, + 0x2c, 0xfd, 0x29, 0xbc, 0x92, 0x72, 0x61, 0x5d, 0xcf, 0x65, 0x14, 0x3f, 0x84, 0x7c, 0x44, 0xca, + 0x9d, 0x16, 0x0e, 0x56, 0x8c, 0x78, 0x3a, 0x8c, 0xc8, 0x65, 0xde, 0x16, 0x5f, 0x7a, 0x0b, 0x8a, + 0x7c, 0xb5, 0xa3, 0x4e, 0x47, 0xce, 0x32, 0x09, 0xf1, 0x1e, 0xc0, 0x10, 0x58, 0xac, 0xb8, 0x63, + 0x84, 0xea, 0x8c, 0x81, 0x3a, 0x23, 0xcc, 0xbc, 0x50, 0x67, 0xd4, 0x89, 0x2d, 0x05, 0x34, 0x62, + 0x9e, 0xfa, 0x4f, 0x08, 0x56, 0x15, 0x41, 0x04, 0xf6, 0x1b, 0x00, 0x11, 0x36, 0x2b, 0xa2, 0xf2, + 0xec, 0x18, 0xee, 0xbc, 0xe4, 0x66, 0xf8, 0x38, 0x01, 0x97, 0xe3, 0x70, 0xbb, 0x37, 0xc2, 0x85, + 0x31, 0x13, 0x74, 0x07, 0x22, 0x9f, 0x4f, 0x06, 0xf9, 0x7d, 0x9f, 0xfa, 0x64, 0x82, 0x3d, 0xa8, + 0xc3, 0x4a, 0xda, 0x47, 0xa8, 0x39, 0x04, 0x18, 0x1e, 0x08, 0x91, 0xb3, 0x57, 0x93, 0x6a, 0x86, + 0x4e, 0xf9, 0xb6, 0xfc, 0xd4, 0xdb, 0xc3, 0x14, 0x45, 0xf3, 0x53, 0xdf, 0x88, 0x5f, 0x10, 0x68, + 0xaa, 0x28, 0x82, 0xfd, 0x4d, 0x58, 0x18, 0xb2, 0xcb, 0xad, 0xc8, 0x84, 0x87, 0x08, 0x7e, 0x8a, + 0x9b, 0x51, 0x00, 0xcc, 0x01, 0xeb, 0xa4, 0x47, 0x4e, 0xa5, 0x7e, 0xfd, 0x09, 0xdc, 0x4d, 0x8c, + 0x0a, 0xde, 0x3d, 0x98, 0xeb, 0xf2, 0x11, 0x91, 0x92, 0x42, 0x12, 0x55, 0x58, 0x0b, 0x1b, 0xfd, + 0x04, 0x4a, 0x52, 0x7b, 0x9d, 0xba, 0x96, 0xe3, 0xda, 0x35, 0xb7, 0xe5, 0x05, 0xae, 0x35, 0xf5, + 0x34, 0x7f, 0x8d, 0x60, 0x33, 0x33, 0x94, 0x60, 0xdf, 0x84, 0x05, 0x27, 0x1c, 0x6b, 0x3a, 0x56, + 0x98, 0xeb, 0x7c, 0x03, 0xc4, 0x50, 0xcd, 0x9a, 0x62, 0x4a, 0xf7, 0xc4, 0x9e, 0x1f, 0x53, 0xff, + 0x23, 0xd7, 0x39, 0xa3, 0x3d, 0x46, 0x3a, 0xcf, 0xfa, 0x52, 0xf3, 0x32, 0xe4, 0xa2, 0xe3, 0x9d, + 0x73, 0x2c, 0x9d, 0xc0, 0x9a, 0xd2, 0x5a, 0x60, 0x3f, 0x86, 0xc5, 0x40, 0x0e, 0x37, 0xfd, 0xbe, + 0x48, 0xd2, 0x66, 0x32, 0xf1, 0x31, 0xc7, 0xa7, 0xd4, 0x26, 0xed, 0xf3, 0xc6, 0x42, 0x30, 0x1c, + 0xd2, 0xad, 0xe1, 0x21, 0x54, 0x00, 0x4d, 0x6b, 0x13, 0x7e, 0x45, 0x42, 0x49, 0x3a, 0x8c, 0x50, + 0xf2, 0x36, 0x2c, 0xc5, 0x95, 0xc8, 0xe3, 0xbe, 0x9a, 0x29, 0xa5, 0xb1, 0x18, 0x13, 0x31, 0xc5, + 0xfd, 0xf9, 0x1d, 0x41, 0x41, 0x9c, 0x92, 0x0f, 0x02, 0x9f, 0xef, 0xff, 0xbb, 0xae, 0xdf, 0x3b, + 0x1f, 0x1c, 0x11, 0x4f, 0x0c, 0x0c, 0x4b, 0x10, 0xc8, 0xa1, 0x9a, 0x85, 0x77, 0xe0, 0x4e, 0x5c, + 0xc2, 0xc0, 0x28, 0xc7, 0x8d, 0x96, 0x62, 0xa4, 0x35, 0x0b, 0x6f, 0x00, 0xb4, 0x7b, 0x94, 0xf8, + 0xd4, 0x6a, 0x12, 0xbf, 0x38, 0x5b, 0x46, 0x95, 0xd9, 0x46, 0x5e, 0x8c, 0x1c, 0xf9, 0xf8, 0x3e, + 0xbc, 0xcc, 0x1c, 0xdb, 0x75, 0x5c, 0xbb, 0x69, 0x51, 0x62, 0x75, 0x1c, 0x97, 0x16, 0x5f, 0xe2, + 0x46, 0x77, 0xc4, 0xf8, 0x3b, 0x62, 0x58, 0x3f, 0x12, 0x77, 0xe8, 0x98, 0xfa, 0x29, 0x64, 0xb9, + 0x7d, 0x37, 0x41, 0xeb, 0xdf, 0xcb, 0xcb, 0xa1, 0x5a, 0x23, 0x2a, 0x44, 0xb7, 0xe8, 0x20, 0x05, + 0x62, 0xfb, 0xf5, 0xd4, 0xbd, 0x56, 0x24, 0xab, 0x11, 0x3a, 0xe0, 0x47, 0x30, 0x2f, 0x63, 0x89, + 0x3d, 0x29, 0x26, 0x9d, 0xa5, 0xd7, 0xb3, 0x7e, 0x23, 0xb2, 0xd4, 0x9d, 0x91, 0xfb, 0x2a, 0xcd, + 0xa6, 0x5e, 0x1b, 0xfe, 0x41, 0x50, 0xce, 0x8e, 0x25, 0xf4, 0xbf, 0x05, 0xb7, 0x07, 0x72, 0x9c, + 0xe8, 0x3d, 0x9c, 0x24, 0x03, 0xd2, 0x05, 0x1f, 0x42, 0x5e, 0x2a, 0x63, 0xc5, 0x1c, 0xf7, 0xcf, + 0x4e, 0xc2, 0xd0, 0x34, 0x75, 0xa2, 0x67, 0xff, 0xf7, 0x89, 0x3e, 0xf8, 0x66, 0x01, 0x6e, 0x71, + 0x8d, 0xf8, 0x13, 0x98, 0x0b, 0xab, 0x30, 0x2e, 0x27, 0x09, 0x46, 0x8b, 0xbc, 0xb6, 0x35, 0xc6, + 0x22, 0x0c, 0xa2, 0xaf, 0x7f, 0xfe, 0xe7, 0xbf, 0x3f, 0xe4, 0x56, 0x70, 0xc1, 0x4c, 0x74, 0x60, + 0x61, 0x81, 0xc7, 0x3f, 0x22, 0xc0, 0xa3, 0x15, 0x17, 0xef, 0x29, 0xd6, 0xcd, 0x7c, 0x03, 0xb4, + 0xfd, 0x09, 0xad, 0x05, 0xd1, 0x0e, 0x27, 0x2a, 0xe3, 0x52, 0x8a, 0x28, 0x34, 0x6f, 0x3a, 0x12, + 0xe2, 0x5b, 0x04, 0xcb, 0xc9, 0x92, 0x8a, 0x2b, 0x8a, 0x48, 0xca, 0x1a, 0xad, 0xdd, 0x9f, 0xc0, + 0x52, 0xf0, 0x54, 0x38, 0x8f, 0x8e, 0xcb, 0x49, 0x9e, 0x44, 0xa5, 0x33, 0x2f, 0x1c, 0xeb, 0x12, + 0x7f, 0x85, 0x60, 0x39, 0x59, 0x1a, 0x95, 0x44, 0xca, 0x22, 0xad, 0x24, 0x52, 0xd7, 0x59, 0x7d, + 0x9b, 0x13, 0x6d, 0xe0, 0xb5, 0x31, 0x44, 0xf8, 0x53, 0x98, 0x97, 0x3d, 0x1e, 0xd6, 0x55, 0x6a, + 0x93, 0xed, 0xb1, 0xb6, 0x3d, 0xd6, 0x46, 0x44, 0x7e, 0xc0, 0x23, 0xdf, 0xc3, 0xba, 0xa9, 0xee, + 0xe6, 0xcd, 0x0b, 0xd9, 0xde, 0x5d, 0xe2, 0xcf, 0x10, 0x2c, 0xc6, 0xbb, 0x53, 0xbc, 0xa3, 0x56, + 0x98, 0xee, 0x91, 0xb5, 0xdd, 0x1b, 0xed, 0x04, 0x4d, 0x99, 0xd3, 0x68, 0xb8, 0x98, 0x41, 0xc3, + 0xf0, 0x17, 0x08, 0xf2, 0x51, 0x7b, 0x85, 0x55, 0x12, 0xd3, 0x2d, 0xaa, 0x76, 0x6f, 0xbc, 0x91, + 0x08, 0xfd, 0x3a, 0x0f, 0xfd, 0x1a, 0xde, 0x36, 0x33, 0xfe, 0xb8, 0xc4, 0x33, 0xf1, 0x25, 0x82, + 0xa5, 0x44, 0x7b, 0x88, 0x33, 0x24, 0x8e, 0xb4, 0xa9, 0x5a, 0xe5, 0x66, 0x43, 0x41, 0xb4, 0xc5, + 0x89, 0xd6, 0xf0, 0x6a, 0x16, 0x11, 0xc3, 0xbf, 0x21, 0xc0, 0xa3, 0x4f, 0x84, 0xf2, 0x36, 0x67, + 0xbe, 0x46, 0xca, 0xdb, 0x9c, 0xfd, 0xee, 0xe8, 0x8f, 0x38, 0x96, 0x81, 0xf7, 0xd4, 0xb7, 0x59, + 0x96, 0x4a, 0xf3, 0x22, 0xf6, 0xc4, 0x5d, 0xe2, 0x9f, 0x11, 0xdc, 0x55, 0x54, 0x73, 0x3c, 0xbe, + 0x94, 0xa4, 0x5f, 0x18, 0xcd, 0x98, 0xd4, 0x5c, 0xc0, 0xee, 0x72, 0xd8, 0x2d, 0xbc, 0x39, 0x1e, + 0x96, 0x3d, 0xae, 0xff, 0x71, 0x55, 0x42, 0x2f, 0xae, 0x4a, 0xe8, 0xef, 0xab, 0x12, 0xfa, 0xee, + 0xba, 0x34, 0xf3, 0xe2, 0xba, 0x34, 0xf3, 0xd7, 0x75, 0x69, 0xe6, 0xe3, 0x43, 0xdb, 0xf1, 0x4f, + 0x82, 0x96, 0xd1, 0xf6, 0x4e, 0xcd, 0x6e, 0xc0, 0x4e, 0x78, 0xfe, 0xf9, 0xd7, 0x3e, 0xff, 0xdc, + 0x77, 0x3d, 0x8b, 0x9a, 0xfd, 0x58, 0x00, 0xfe, 0x27, 0xb9, 0x35, 0xc7, 0xff, 0xbc, 0x3e, 0xfc, + 0x2f, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x8b, 0xef, 0x08, 0x87, 0x0f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2154,6 +2163,11 @@ func (m *PendingOutboundEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.SigningDeadline != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.SigningDeadline)) + i-- + dAtA[i] = 0x20 + } if m.CreatedAt != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.CreatedAt)) i-- @@ -2607,6 +2621,9 @@ func (m *PendingOutboundEntry) Size() (n int) { if m.CreatedAt != 0 { n += 1 + sovQuery(uint64(m.CreatedAt)) } + if m.SigningDeadline != 0 { + n += 1 + sovQuery(uint64(m.SigningDeadline)) + } return n } @@ -4258,6 +4275,25 @@ func (m *PendingOutboundEntry) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SigningDeadline", wireType) + } + m.SigningDeadline = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SigningDeadline |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/uregistry/types/chain_config.go b/x/uregistry/types/chain_config.go index 1d8c1ae42..e9f6a6322 100644 --- a/x/uregistry/types/chain_config.go +++ b/x/uregistry/types/chain_config.go @@ -62,6 +62,10 @@ func (p ChainConfig) ValidateBasic() error { } } + if p.TssSigningDeadline != nil && *p.TssSigningDeadline < 0 { + return errors.Wrap(sdkerrors.ErrInvalidRequest, "tss_signing_deadline must not be negative") + } + if p.BlockConfirmation == nil { return errors.Wrap(sdkerrors.ErrInvalidRequest, "block_confirmation is required") } diff --git a/x/uregistry/types/types.pb.go b/x/uregistry/types/types.pb.go index 3d26d1d8c..3eed1c5bd 100644 --- a/x/uregistry/types/types.pb.go +++ b/x/uregistry/types/types.pb.go @@ -439,6 +439,7 @@ type ChainConfig struct { Enabled *ChainEnabled `protobuf:"bytes,7,opt,name=enabled,proto3" json:"enabled,omitempty"` GasOracleFetchInterval time.Duration `protobuf:"bytes,8,opt,name=gas_oracle_fetch_interval,json=gasOracleFetchInterval,proto3,stdduration" json:"gas_oracle_fetch_interval"` VaultMethods []*VaultMethods `protobuf:"bytes,9,rep,name=vault_methods,json=vaultMethods,proto3" json:"vault_methods,omitempty"` + TssSigningDeadline *time.Duration `protobuf:"bytes,10,opt,name=tss_signing_deadline,json=tssSigningDeadline,proto3,stdduration" json:"tss_signing_deadline,omitempty"` } func (m *ChainConfig) Reset() { *m = ChainConfig{} } @@ -536,6 +537,13 @@ func (m *ChainConfig) GetVaultMethods() []*VaultMethods { return nil } +func (m *ChainConfig) GetTssSigningDeadline() *time.Duration { + if m != nil { + return m.TssSigningDeadline + } + return nil +} + type NativeRepresentation struct { Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` @@ -711,78 +719,80 @@ func init() { func init() { proto.RegisterFile("uregistry/v1/types.proto", fileDescriptor_11eea54f17422d86) } var fileDescriptor_11eea54f17422d86 = []byte{ - // 1132 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xcd, 0x6e, 0xe3, 0xd4, - 0x17, 0x8f, 0x93, 0x36, 0x1f, 0x27, 0x69, 0xea, 0x5c, 0xe5, 0xdf, 0x71, 0xa3, 0xf9, 0x27, 0x25, - 0x33, 0x82, 0x4e, 0x35, 0x4d, 0x68, 0x61, 0x06, 0xa9, 0x12, 0x42, 0x69, 0x9a, 0x42, 0xd4, 0x26, - 0xa9, 0x6e, 0x3c, 0xa9, 0x60, 0x81, 0x75, 0x63, 0xdf, 0x26, 0xd6, 0xf8, 0x23, 0xd8, 0x4e, 0x20, - 0x0f, 0xc0, 0x06, 0x21, 0x04, 0xbb, 0x59, 0xce, 0x23, 0xb0, 0xe0, 0x21, 0x66, 0x39, 0x4b, 0x56, - 0x30, 0x6a, 0x91, 0xe0, 0x19, 0x58, 0x21, 0x5f, 0xdb, 0x89, 0xdd, 0x74, 0xd8, 0xb3, 0x69, 0xef, - 0xf9, 0x9d, 0xe3, 0xf3, 0xf9, 0x3b, 0xa7, 0x05, 0x61, 0x6a, 0xd1, 0x91, 0x6a, 0x3b, 0xd6, 0xbc, - 0x3e, 0x3b, 0xa8, 0x3b, 0xf3, 0x09, 0xb5, 0x6b, 0x13, 0xcb, 0x74, 0x4c, 0x94, 0x5b, 0x68, 0x6a, - 0xb3, 0x83, 0x52, 0x71, 0x64, 0x8e, 0x4c, 0xa6, 0xa8, 0xbb, 0x2f, 0xcf, 0xa6, 0x54, 0x20, 0xba, - 0x6a, 0x98, 0x75, 0xf6, 0xd3, 0x87, 0xca, 0x23, 0xd3, 0x1c, 0x69, 0xb4, 0xce, 0xa4, 0xe1, 0xf4, - 0xaa, 0xae, 0x4c, 0x2d, 0xe2, 0xa8, 0xa6, 0xe1, 0xe9, 0xab, 0x1f, 0x43, 0xf2, 0x82, 0x58, 0x44, - 0xb7, 0x51, 0x11, 0xd6, 0x89, 0xa2, 0xab, 0x86, 0xc0, 0xed, 0x70, 0xbb, 0x19, 0xec, 0x09, 0x47, - 0xff, 0x7f, 0xf1, 0xb2, 0x12, 0xfb, 0xeb, 0x65, 0x85, 0xfb, 0xee, 0xcf, 0x9f, 0xf7, 0xf8, 0x65, - 0x76, 0x13, 0xf6, 0x51, 0xf5, 0x0f, 0x0e, 0xf2, 0x9f, 0x12, 0x87, 0x7e, 0x4d, 0xe6, 0x1d, 0xea, - 0x8c, 0x4d, 0xc5, 0x46, 0x08, 0xd6, 0x0c, 0xa2, 0x53, 0xdf, 0x0d, 0x7b, 0xa3, 0x32, 0x80, 0xaa, - 0x50, 0xc3, 0x51, 0xaf, 0x54, 0x6a, 0x09, 0x71, 0xa6, 0x09, 0x21, 0xe8, 0x11, 0xf0, 0x74, 0x46, - 0x0d, 0x47, 0x0a, 0x59, 0x25, 0x98, 0xd5, 0x26, 0xc3, 0xdb, 0x4b, 0xd3, 0x33, 0x28, 0xc8, 0xa6, - 0x71, 0xa5, 0x5a, 0x3a, 0x2b, 0x43, 0x72, 0x7b, 0x24, 0xac, 0xed, 0x70, 0xbb, 0xf9, 0xc3, 0x72, - 0x2d, 0xdc, 0xa3, 0x5a, 0x33, 0x64, 0x26, 0xce, 0x27, 0x14, 0xf3, 0xf2, 0x2d, 0xe4, 0xe8, 0xdd, - 0x70, 0x75, 0xdb, 0xcb, 0xea, 0x46, 0x5e, 0x49, 0x92, 0xee, 0xd5, 0x54, 0x7d, 0xc3, 0x41, 0x6e, - 0x40, 0xa6, 0x9a, 0xf3, 0x5f, 0x2c, 0xf2, 0x61, 0xb8, 0xc8, 0x7b, 0x21, 0x82, 0xb9, 0x05, 0x2d, - 0x4a, 0xfc, 0x9e, 0x83, 0xc2, 0xb1, 0x66, 0xca, 0xcf, 0xc3, 0x1e, 0xd1, 0x3b, 0x90, 0xbb, 0x22, - 0xb6, 0x23, 0xa9, 0xc6, 0xd0, 0x9c, 0x1a, 0x0a, 0xab, 0x77, 0x03, 0x67, 0x5d, 0xac, 0xed, 0x41, - 0x6e, 0x59, 0xb6, 0x43, 0x0c, 0x85, 0x58, 0xca, 0xc2, 0x2c, 0xce, 0xcc, 0x36, 0x03, 0xdc, 0x37, - 0x3d, 0x7a, 0x14, 0xce, 0xe4, 0xfe, 0x32, 0x93, 0xa1, 0x1b, 0x57, 0x0a, 0x27, 0x5e, 0xfd, 0x81, - 0x83, 0x5c, 0x73, 0x4c, 0x54, 0xa3, 0x65, 0x90, 0xa1, 0x46, 0x15, 0xb4, 0x07, 0xbc, 0x6a, 0xfb, - 0x8e, 0x7c, 0x8c, 0x65, 0x93, 0xc6, 0x2b, 0x38, 0x7a, 0x0c, 0x05, 0xd5, 0xee, 0x4d, 0x9d, 0x88, - 0x71, 0x9c, 0x19, 0xaf, 0x2a, 0xde, 0xda, 0x1f, 0xd9, 0x0d, 0x2f, 0x51, 0xcf, 0xaa, 0xfa, 0xcb, - 0x1a, 0x64, 0x59, 0x42, 0xac, 0x3f, 0x23, 0x77, 0x5d, 0x98, 0x41, 0xb0, 0x2e, 0x4c, 0x40, 0xfb, - 0x90, 0x9a, 0xe9, 0xde, 0xb8, 0xe2, 0x6c, 0x5c, 0xc5, 0xe8, 0xb8, 0x06, 0x3a, 0x1b, 0x52, 0x72, - 0xc6, 0x7e, 0xa3, 0x87, 0x90, 0x9f, 0x4c, 0x87, 0x9a, 0x2a, 0x4b, 0xd6, 0x44, 0x96, 0xa6, 0x96, - 0xe6, 0x13, 0x22, 0xe7, 0xa1, 0x78, 0x22, 0x3f, 0xb3, 0x34, 0xf4, 0x1e, 0x6c, 0x06, 0x84, 0x24, - 0x8a, 0x62, 0x51, 0xdb, 0x66, 0x5c, 0xc8, 0xe0, 0xbc, 0x0f, 0x37, 0x3c, 0x14, 0x75, 0x01, 0xad, - 0xb6, 0x52, 0x58, 0xdf, 0xe1, 0x76, 0xb3, 0x87, 0x95, 0x68, 0x22, 0x2b, 0xa3, 0xc6, 0x85, 0xe1, - 0xca, 0xf4, 0x5b, 0xcb, 0xc0, 0x3e, 0x4d, 0x84, 0xe4, 0x4e, 0x62, 0x37, 0x7b, 0x78, 0x3f, 0xea, - 0x2c, 0x7a, 0x01, 0x16, 0x69, 0x05, 0xcb, 0xf2, 0x21, 0xa4, 0xfc, 0x2e, 0x0a, 0x29, 0x96, 0x4b, - 0xe9, 0x16, 0x87, 0x43, 0x73, 0xc6, 0x81, 0x29, 0xfa, 0x12, 0xb6, 0x47, 0xc4, 0x96, 0x4c, 0x8b, - 0xc8, 0x1a, 0x95, 0xae, 0xa8, 0x23, 0x8f, 0x25, 0xd5, 0x70, 0xa8, 0x35, 0x23, 0x9a, 0x90, 0x66, - 0x7e, 0xb6, 0x6b, 0xde, 0x75, 0xab, 0x05, 0xd7, 0xad, 0x76, 0xe2, 0x5f, 0xb7, 0xe3, 0xf4, 0xab, - 0xdf, 0x2a, 0xb1, 0x17, 0xbf, 0x57, 0x38, 0xbc, 0x35, 0x22, 0x76, 0x8f, 0x39, 0x39, 0x75, 0x7d, - 0xb4, 0x7d, 0x17, 0xe8, 0x13, 0xd8, 0x88, 0x6c, 0x80, 0x90, 0x61, 0xa5, 0xdd, 0xca, 0x2d, 0xbc, - 0xf5, 0x38, 0x37, 0x0b, 0x49, 0x47, 0x0f, 0xc2, 0xbc, 0xd9, 0xba, 0xcd, 0x1b, 0x36, 0x82, 0x51, - 0xf5, 0x5b, 0x0e, 0x8a, 0x5d, 0xe2, 0xa8, 0x33, 0x8a, 0xe9, 0xc4, 0xa2, 0x36, 0x35, 0x1c, 0xaf, - 0xb7, 0x45, 0x58, 0x57, 0xa8, 0x61, 0xea, 0x01, 0x7f, 0x98, 0xe0, 0x2e, 0x93, 0x6c, 0x1a, 0x8e, - 0x45, 0x64, 0x67, 0x31, 0x6b, 0xef, 0x92, 0x6c, 0x06, 0xb8, 0x3f, 0xec, 0xa3, 0xc7, 0xe1, 0xf0, - 0x95, 0x65, 0x78, 0x83, 0x45, 0x93, 0xac, 0x48, 0xb8, 0xea, 0xdf, 0x71, 0xc8, 0x8a, 0xe6, 0x73, - 0xfa, 0xef, 0xf4, 0x15, 0x20, 0x15, 0x8d, 0x1a, 0x88, 0x8b, 0x83, 0x97, 0x08, 0x1d, 0xbc, 0x2d, - 0x48, 0xda, 0x73, 0x7d, 0x68, 0x6a, 0x3e, 0x1d, 0x7d, 0x09, 0x95, 0x20, 0xad, 0x50, 0x59, 0xd5, - 0x89, 0x66, 0x33, 0xf2, 0x6d, 0xe0, 0x85, 0xec, 0x46, 0x08, 0xb8, 0x90, 0x64, 0x0b, 0xb9, 0x98, - 0xf7, 0x03, 0xd8, 0xd0, 0xd4, 0xaf, 0xa6, 0xaa, 0xa2, 0x3a, 0x73, 0x49, 0x26, 0x13, 0xc6, 0x95, - 0x0c, 0xce, 0x2d, 0xc0, 0x26, 0x99, 0xa0, 0xa7, 0x00, 0x8e, 0x5b, 0x85, 0xb7, 0x62, 0x69, 0xb6, - 0x62, 0xf7, 0xa2, 0x13, 0x63, 0x55, 0xb2, 0x2d, 0xcb, 0x38, 0xc1, 0x13, 0x5d, 0xc2, 0xff, 0xee, - 0xec, 0x8b, 0x90, 0x61, 0x44, 0xaa, 0x46, 0x5d, 0xdc, 0x35, 0x30, 0x5c, 0x34, 0xee, 0x40, 0xdf, - 0x4a, 0x02, 0x2f, 0x4b, 0x8f, 0x04, 0x7b, 0x3f, 0x71, 0x90, 0xf4, 0x36, 0x1f, 0xe5, 0x01, 0x9e, - 0x75, 0xcf, 0xba, 0xbd, 0xcb, 0xae, 0x34, 0xe8, 0xf0, 0x31, 0x94, 0x82, 0x44, 0x6b, 0xd0, 0xe1, - 0x39, 0xf7, 0xd1, 0x1f, 0x74, 0xf8, 0x38, 0xca, 0x42, 0xaa, 0xd3, 0x1b, 0xb4, 0x5c, 0x75, 0xc2, - 0x15, 0x2e, 0x1b, 0xfd, 0x8e, 0x2b, 0xac, 0xa1, 0x1c, 0xa4, 0x9b, 0x8d, 0x36, 0xee, 0xb9, 0xd2, - 0xba, 0xab, 0x12, 0x71, 0x8f, 0xb9, 0x49, 0xba, 0x6e, 0xfb, 0x62, 0xeb, 0xfc, 0xbc, 0x81, 0x5d, - 0x39, 0x85, 0x10, 0xe4, 0x8f, 0xdb, 0x62, 0xb3, 0xd7, 0xee, 0x4a, 0xfd, 0x26, 0x6e, 0x5f, 0x88, - 0x7c, 0xda, 0xfd, 0xbc, 0x27, 0x7e, 0xd6, 0x62, 0x16, 0x99, 0xbd, 0x33, 0xc8, 0x2c, 0x3a, 0x85, - 0x0a, 0xb0, 0x11, 0x64, 0x25, 0xf6, 0xce, 0x5a, 0x5d, 0x3e, 0x86, 0x32, 0xb0, 0xde, 0xc2, 0xcd, - 0xc3, 0xf7, 0x79, 0x0e, 0x01, 0x24, 0x5b, 0xb8, 0xf9, 0xd1, 0xe1, 0x81, 0x97, 0x5d, 0x0b, 0x37, - 0x0f, 0x0e, 0x9e, 0x3c, 0xe1, 0x13, 0x2c, 0xe7, 0x8b, 0x73, 0x7e, 0x6d, 0x6f, 0x0c, 0xfc, 0xed, - 0x3f, 0x44, 0x48, 0x80, 0x62, 0xb3, 0xd7, 0x3d, 0x6d, 0xe3, 0x4e, 0x43, 0x6c, 0xf7, 0xba, 0x92, - 0x1f, 0x80, 0x8f, 0xa1, 0x32, 0x94, 0x22, 0x1a, 0xf1, 0xf3, 0x8b, 0x96, 0xd4, 0x17, 0x1b, 0xdd, - 0x93, 0x06, 0x3e, 0xe1, 0x39, 0x54, 0x82, 0xad, 0x55, 0xfd, 0x69, 0xa3, 0x2f, 0xf2, 0xf1, 0xe3, - 0x8b, 0x57, 0xd7, 0x65, 0xee, 0xf5, 0x75, 0x99, 0x7b, 0x73, 0x5d, 0xe6, 0x7e, 0xbc, 0x29, 0xc7, - 0x5e, 0xdf, 0x94, 0x63, 0xbf, 0xde, 0x94, 0x63, 0x5f, 0x3c, 0x1d, 0xa9, 0xce, 0x78, 0x3a, 0xac, - 0xc9, 0xa6, 0x5e, 0x9f, 0x4c, 0xed, 0x31, 0x63, 0x34, 0x7b, 0xed, 0xb3, 0xe7, 0xbe, 0x61, 0x2a, - 0xb4, 0xfe, 0x4d, 0x3d, 0x34, 0x23, 0xf7, 0xdf, 0xab, 0x61, 0x92, 0x1d, 0x8f, 0x0f, 0xfe, 0x09, - 0x00, 0x00, 0xff, 0xff, 0x7e, 0xcd, 0xc1, 0xec, 0x7b, 0x09, 0x00, 0x00, + // 1167 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x5d, 0x6f, 0xdb, 0xe4, + 0x17, 0x8f, 0x93, 0xe6, 0xed, 0x24, 0x4d, 0x9d, 0x47, 0xf9, 0x77, 0x5e, 0xb4, 0x7f, 0x52, 0xb2, + 0x09, 0xba, 0x6a, 0x4b, 0x68, 0x61, 0x43, 0xaa, 0x84, 0x50, 0x9a, 0x66, 0x10, 0xb5, 0x49, 0x8a, + 0xe3, 0xa5, 0x82, 0x0b, 0xac, 0x27, 0xf6, 0x53, 0xc7, 0x9a, 0x5f, 0x82, 0xfd, 0x24, 0x90, 0x0f, + 0x80, 0x90, 0x10, 0x42, 0x70, 0xb7, 0xcb, 0x7d, 0x04, 0x3e, 0xc6, 0x2e, 0x77, 0xc9, 0x15, 0x4c, + 0x2d, 0x12, 0x7c, 0x06, 0xae, 0x90, 0x1f, 0xdb, 0x89, 0xd3, 0x74, 0x70, 0xcd, 0x4d, 0xfb, 0x9c, + 0x17, 0x9f, 0xf3, 0x3b, 0xe7, 0xfc, 0xce, 0x69, 0x41, 0x98, 0x3a, 0x44, 0xd3, 0x5d, 0xea, 0xcc, + 0x1b, 0xb3, 0xfd, 0x06, 0x9d, 0x4f, 0x88, 0x5b, 0x9f, 0x38, 0x36, 0xb5, 0x51, 0x7e, 0x61, 0xa9, + 0xcf, 0xf6, 0xcb, 0x25, 0xcd, 0xd6, 0x6c, 0x66, 0x68, 0x78, 0x2f, 0xdf, 0xa7, 0x5c, 0xc4, 0xa6, + 0x6e, 0xd9, 0x0d, 0xf6, 0x33, 0x50, 0x55, 0x34, 0xdb, 0xd6, 0x0c, 0xd2, 0x60, 0xd2, 0x68, 0x7a, + 0xd1, 0x50, 0xa7, 0x0e, 0xa6, 0xba, 0x6d, 0xf9, 0xf6, 0xda, 0x87, 0x90, 0x3a, 0xc3, 0x0e, 0x36, + 0x5d, 0x54, 0x82, 0x24, 0x56, 0x4d, 0xdd, 0x12, 0xb8, 0x1d, 0x6e, 0x37, 0x2b, 0xfa, 0xc2, 0xe1, + 0xff, 0x9f, 0xbf, 0xa8, 0xc6, 0xfe, 0x7c, 0x51, 0xe5, 0xbe, 0xfb, 0xe3, 0xe7, 0x3d, 0x7e, 0x89, + 0x6e, 0xc2, 0x3e, 0xaa, 0xfd, 0xce, 0x41, 0xe1, 0x63, 0x4c, 0xc9, 0x57, 0x78, 0xde, 0x25, 0x74, + 0x6c, 0xab, 0x2e, 0x42, 0xb0, 0x61, 0x61, 0x93, 0x04, 0x61, 0xd8, 0x1b, 0x55, 0x00, 0x74, 0x95, + 0x58, 0x54, 0xbf, 0xd0, 0x89, 0x23, 0xc4, 0x99, 0x25, 0xa2, 0x41, 0xf7, 0x81, 0x27, 0x33, 0x62, + 0x51, 0x39, 0xe2, 0x95, 0x60, 0x5e, 0x5b, 0x4c, 0xdf, 0x59, 0xba, 0x9e, 0x40, 0x51, 0xb1, 0xad, + 0x0b, 0xdd, 0x31, 0x59, 0x19, 0xb2, 0xd7, 0x23, 0x61, 0x63, 0x87, 0xdb, 0x2d, 0x1c, 0x54, 0xea, + 0xd1, 0x1e, 0xd5, 0x5b, 0x11, 0x37, 0x69, 0x3e, 0x21, 0x22, 0xaf, 0x5c, 0xd3, 0x1c, 0xbe, 0x1d, + 0xad, 0xee, 0xf6, 0xb2, 0x3a, 0xcd, 0x2f, 0x49, 0x36, 0xfd, 0x9a, 0x6a, 0xaf, 0x39, 0xc8, 0x0f, + 0xf1, 0xd4, 0xa0, 0xff, 0xc5, 0x22, 0xef, 0x45, 0x8b, 0xbc, 0x15, 0x21, 0x98, 0x57, 0xd0, 0xa2, + 0xc4, 0xef, 0x39, 0x28, 0x1e, 0x19, 0xb6, 0xf2, 0x2c, 0x1a, 0x11, 0xbd, 0x05, 0xf9, 0x0b, 0xec, + 0x52, 0x59, 0xb7, 0x46, 0xf6, 0xd4, 0x52, 0x59, 0xbd, 0x9b, 0x62, 0xce, 0xd3, 0x75, 0x7c, 0x95, + 0x57, 0x96, 0x4b, 0xb1, 0xa5, 0x62, 0x47, 0x5d, 0xb8, 0xc5, 0x99, 0xdb, 0x56, 0xa8, 0x0f, 0x5c, + 0x0f, 0xef, 0x47, 0x91, 0xdc, 0x59, 0x22, 0x19, 0x79, 0x79, 0xe5, 0x28, 0xf0, 0xda, 0x0f, 0x1c, + 0xe4, 0x5b, 0x63, 0xac, 0x5b, 0x6d, 0x0b, 0x8f, 0x0c, 0xa2, 0xa2, 0x3d, 0xe0, 0x75, 0x37, 0x08, + 0x14, 0xe8, 0x18, 0x9a, 0x8c, 0xb8, 0xa6, 0x47, 0x0f, 0xa0, 0xa8, 0xbb, 0xfd, 0x29, 0x5d, 0x71, + 0x8e, 0x33, 0xe7, 0x75, 0xc3, 0x1b, 0xfb, 0xa3, 0x78, 0xe9, 0x65, 0xe2, 0x7b, 0xd5, 0xbe, 0x4d, + 0x42, 0x8e, 0x01, 0x62, 0xfd, 0xd1, 0xbc, 0x75, 0x61, 0x0e, 0xe1, 0xba, 0x30, 0x01, 0x3d, 0x84, + 0xf4, 0xcc, 0xf4, 0xc7, 0x15, 0x67, 0xe3, 0x2a, 0xad, 0x8e, 0x6b, 0x68, 0xb2, 0x21, 0xa5, 0x66, + 0xec, 0x37, 0xba, 0x07, 0x85, 0xc9, 0x74, 0x64, 0xe8, 0x8a, 0xec, 0x4c, 0x14, 0x79, 0xea, 0x18, + 0x01, 0x21, 0xf2, 0xbe, 0x56, 0x9c, 0x28, 0x4f, 0x1d, 0x03, 0xbd, 0x03, 0x5b, 0x21, 0x21, 0xb1, + 0xaa, 0x3a, 0xc4, 0x75, 0x19, 0x17, 0xb2, 0x62, 0x21, 0x50, 0x37, 0x7d, 0x2d, 0xea, 0x01, 0x5a, + 0x6f, 0xa5, 0x90, 0xdc, 0xe1, 0x76, 0x73, 0x07, 0xd5, 0x55, 0x20, 0x6b, 0xa3, 0x16, 0x8b, 0xa3, + 0xb5, 0xe9, 0xb7, 0x97, 0x89, 0x03, 0x9a, 0x08, 0xa9, 0x9d, 0xc4, 0x6e, 0xee, 0xe0, 0xce, 0x6a, + 0xb0, 0xd5, 0x0b, 0xb0, 0x80, 0x15, 0x2e, 0xcb, 0xfb, 0x90, 0x0e, 0xba, 0x28, 0xa4, 0x19, 0x96, + 0xf2, 0x35, 0x0e, 0x47, 0xe6, 0x2c, 0x86, 0xae, 0xe8, 0x0b, 0xb8, 0xad, 0x61, 0x57, 0xb6, 0x1d, + 0xac, 0x18, 0x44, 0xbe, 0x20, 0x54, 0x19, 0xcb, 0xba, 0x45, 0x89, 0x33, 0xc3, 0x86, 0x90, 0x61, + 0x71, 0x6e, 0xd7, 0xfd, 0xeb, 0x56, 0x0f, 0xaf, 0x5b, 0xfd, 0x38, 0xb8, 0x6e, 0x47, 0x99, 0x97, + 0xbf, 0x56, 0x63, 0xcf, 0x7f, 0xab, 0x72, 0xe2, 0xb6, 0x86, 0xdd, 0x3e, 0x0b, 0xf2, 0xc4, 0x8b, + 0xd1, 0x09, 0x42, 0xa0, 0x8f, 0x60, 0x73, 0x65, 0x03, 0x84, 0x2c, 0x2b, 0xed, 0x1a, 0xb6, 0xe8, + 0xd6, 0x8b, 0xf9, 0x59, 0xf4, 0x06, 0x7c, 0x0a, 0x25, 0xea, 0xba, 0xb2, 0xab, 0x6b, 0x96, 0x6e, + 0x69, 0xb2, 0x4a, 0xb0, 0x6a, 0xe8, 0x16, 0x11, 0xe0, 0xdf, 0xb0, 0x6d, 0x30, 0x5c, 0x88, 0xba, + 0xee, 0xc0, 0xff, 0xf6, 0x38, 0xf8, 0xf4, 0xf0, 0x6e, 0x94, 0x8a, 0xdb, 0xd7, 0xa9, 0xc8, 0xa6, + 0xaa, 0xd5, 0xbe, 0xe1, 0xa0, 0xd4, 0xc3, 0x54, 0x9f, 0x11, 0x91, 0x4c, 0x1c, 0xe2, 0x12, 0x8b, + 0xfa, 0xe3, 0x2a, 0x41, 0x52, 0x25, 0x96, 0x6d, 0x86, 0x94, 0x64, 0x82, 0xb7, 0x9f, 0x8a, 0x6d, + 0x51, 0x07, 0x2b, 0x74, 0x41, 0x1f, 0xff, 0x38, 0x6d, 0x85, 0xfa, 0x80, 0x3f, 0x87, 0x0f, 0xa2, + 0xe9, 0xab, 0xcb, 0xf4, 0x16, 0xcb, 0x26, 0x3b, 0x2b, 0xe9, 0x6a, 0x7f, 0xc5, 0x21, 0x27, 0xd9, + 0xcf, 0xc8, 0x3f, 0x6f, 0x84, 0x00, 0xe9, 0xd5, 0xac, 0xa1, 0xb8, 0xb8, 0xa1, 0x89, 0xc8, 0x0d, + 0xdd, 0x86, 0x94, 0x3b, 0x37, 0x47, 0xb6, 0x11, 0x30, 0x3c, 0x90, 0x50, 0x19, 0x32, 0x2a, 0x51, + 0x74, 0x13, 0x1b, 0x2e, 0xe3, 0xf3, 0xa6, 0xb8, 0x90, 0xbd, 0x0c, 0x21, 0xbd, 0x52, 0x6c, 0xc7, + 0x17, 0x14, 0xba, 0x0b, 0x9b, 0x86, 0xfe, 0xe5, 0x54, 0x57, 0x75, 0x3a, 0x97, 0x15, 0x3c, 0x61, + 0xf4, 0xcb, 0x8a, 0xf9, 0x85, 0xb2, 0x85, 0x27, 0xe8, 0x31, 0x00, 0xf5, 0xaa, 0xf0, 0xb7, 0x36, + 0xc3, 0xb6, 0xf6, 0xd6, 0x2a, 0x09, 0x58, 0x95, 0x6c, 0x71, 0xb3, 0x34, 0x7c, 0xa2, 0x73, 0xf8, + 0xdf, 0x8d, 0x7d, 0x11, 0xb2, 0x6c, 0xfe, 0xb5, 0xd5, 0x10, 0x37, 0x0d, 0x4c, 0x2c, 0x59, 0x37, + 0x68, 0xdf, 0x48, 0x02, 0x1f, 0xa5, 0x4f, 0x82, 0xbd, 0x9f, 0x38, 0x48, 0xf9, 0xc7, 0x04, 0x15, + 0x00, 0x9e, 0xf6, 0x4e, 0x7a, 0xfd, 0xf3, 0x9e, 0x3c, 0xec, 0xf2, 0x31, 0x94, 0x86, 0x44, 0x7b, + 0xd8, 0xe5, 0x39, 0xef, 0x31, 0x18, 0x76, 0xf9, 0x38, 0xca, 0x41, 0xba, 0xdb, 0x1f, 0xb6, 0x3d, + 0x73, 0xc2, 0x13, 0xce, 0x9b, 0x83, 0xae, 0x27, 0x6c, 0xa0, 0x3c, 0x64, 0x5a, 0xcd, 0x8e, 0xd8, + 0xf7, 0xa4, 0xa4, 0x67, 0x92, 0xc4, 0x3e, 0x0b, 0x93, 0xf2, 0xc2, 0x0e, 0xa4, 0xf6, 0xe9, 0x69, + 0x53, 0xf4, 0xe4, 0x34, 0x42, 0x50, 0x38, 0xea, 0x48, 0xad, 0x7e, 0xa7, 0x27, 0x0f, 0x5a, 0x62, + 0xe7, 0x4c, 0xe2, 0x33, 0xde, 0xe7, 0x7d, 0xe9, 0x93, 0x36, 0xf3, 0xc8, 0xee, 0x9d, 0x40, 0x76, + 0xd1, 0x29, 0x54, 0x84, 0xcd, 0x10, 0x95, 0xd4, 0x3f, 0x69, 0xf7, 0xf8, 0x18, 0xca, 0x42, 0xb2, + 0x2d, 0xb6, 0x0e, 0xde, 0xe5, 0x39, 0x04, 0x90, 0x6a, 0x8b, 0xad, 0x0f, 0x0e, 0xf6, 0x7d, 0x74, + 0x6d, 0xb1, 0xb5, 0xbf, 0xff, 0xe8, 0x11, 0x9f, 0x60, 0x98, 0xcf, 0x4e, 0xf9, 0x8d, 0xbd, 0x31, + 0xf0, 0xd7, 0xff, 0xb6, 0x21, 0x01, 0x4a, 0xad, 0x7e, 0xef, 0x49, 0x47, 0xec, 0x36, 0xa5, 0x4e, + 0xbf, 0x27, 0x07, 0x09, 0xf8, 0x18, 0xaa, 0x40, 0x79, 0xc5, 0x22, 0x7d, 0x76, 0xd6, 0x96, 0x07, + 0x52, 0xb3, 0x77, 0xdc, 0x14, 0x8f, 0x79, 0x0e, 0x95, 0x61, 0x7b, 0xdd, 0xfe, 0xa4, 0x39, 0x90, + 0xf8, 0xf8, 0xd1, 0xd9, 0xcb, 0xcb, 0x0a, 0xf7, 0xea, 0xb2, 0xc2, 0xbd, 0xbe, 0xac, 0x70, 0x3f, + 0x5e, 0x55, 0x62, 0xaf, 0xae, 0x2a, 0xb1, 0x5f, 0xae, 0x2a, 0xb1, 0xcf, 0x1f, 0x6b, 0x3a, 0x1d, + 0x4f, 0x47, 0x75, 0xc5, 0x36, 0x1b, 0x93, 0xa9, 0x3b, 0x66, 0x8c, 0x66, 0xaf, 0x87, 0xec, 0xf9, + 0xd0, 0xb2, 0x55, 0xd2, 0xf8, 0xba, 0x11, 0x99, 0x91, 0xf7, 0x1f, 0xdb, 0x28, 0xc5, 0x76, 0xfe, + 0xbd, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x52, 0x86, 0x14, 0xc9, 0xce, 0x09, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -985,6 +995,15 @@ func (this *ChainConfig) Equal(that interface{}) bool { return false } } + if this.TssSigningDeadline != nil && that1.TssSigningDeadline != nil { + if *this.TssSigningDeadline != *that1.TssSigningDeadline { + return false + } + } else if this.TssSigningDeadline != nil { + return false + } else if that1.TssSigningDeadline != nil { + return false + } return true } func (this *NativeRepresentation) Equal(that interface{}) bool { @@ -1286,6 +1305,16 @@ func (m *ChainConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.TssSigningDeadline != nil { + n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(*m.TssSigningDeadline, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.TssSigningDeadline):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintTypes(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x52 + } if len(m.VaultMethods) > 0 { for iNdEx := len(m.VaultMethods) - 1; iNdEx >= 0; iNdEx-- { { @@ -1300,12 +1329,12 @@ func (m *ChainConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x4a } } - n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.GasOracleFetchInterval, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.GasOracleFetchInterval):]) - if err1 != nil { - return 0, err1 + n2, err2 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.GasOracleFetchInterval, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.GasOracleFetchInterval):]) + if err2 != nil { + return 0, err2 } - i -= n1 - i = encodeVarintTypes(dAtA, i, uint64(n1)) + i -= n2 + i = encodeVarintTypes(dAtA, i, uint64(n2)) i-- dAtA[i] = 0x42 if m.Enabled != nil { @@ -1647,6 +1676,10 @@ func (m *ChainConfig) Size() (n int) { n += 1 + l + sovTypes(uint64(l)) } } + if m.TssSigningDeadline != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.TssSigningDeadline) + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -2622,6 +2655,42 @@ func (m *ChainConfig) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TssSigningDeadline", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TssSigningDeadline == nil { + m.TssSigningDeadline = new(time.Duration) + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(m.TssSigningDeadline, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) From 18fee4f7608bbee006f1bccdd5ccfd95ee842331 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 26 May 2026 12:17:53 +0530 Subject: [PATCH 02/17] tests: added tests for deadline changes --- x/uexecutor/keeper/export_test.go | 10 ++ x/uexecutor/keeper/pending_outbound_test.go | 132 ++++++++++++++++++++ x/uregistry/types/chain_config_test.go | 49 ++++++++ 3 files changed, 191 insertions(+) create mode 100644 x/uexecutor/keeper/export_test.go diff --git a/x/uexecutor/keeper/export_test.go b/x/uexecutor/keeper/export_test.go new file mode 100644 index 000000000..2a4060035 --- /dev/null +++ b/x/uexecutor/keeper/export_test.go @@ -0,0 +1,10 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/pushchain/push-chain-node/x/uexecutor/types" +) + +func (k Keeper) TestAttachOutboundsToUtx(ctx sdk.Context, utxId string, outbounds []*types.OutboundTx, revertMsg string) error { + return k.attachOutboundsToUtx(ctx, utxId, outbounds, revertMsg) +} diff --git a/x/uexecutor/keeper/pending_outbound_test.go b/x/uexecutor/keeper/pending_outbound_test.go index b4ce8bbdc..263a41b0a 100644 --- a/x/uexecutor/keeper/pending_outbound_test.go +++ b/x/uexecutor/keeper/pending_outbound_test.go @@ -3,9 +3,11 @@ package keeper_test import ( "fmt" "testing" + "time" "github.com/golang/mock/gomock" "github.com/pushchain/push-chain-node/x/uexecutor/types" + uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" "github.com/stretchr/testify/require" ) @@ -235,3 +237,133 @@ func TestPendingOutbound_MultipleOutboundsPerUTX(t *testing.T) { require.Len(resp.Entries, 2) require.Len(resp.Outbounds, 2) } + +func TestPendingOutbound_SigningDeadline_Set(t *testing.T) { + f := setupPendingOutboundFixture(t) + require := require.New(t) + + tenMin := 10 * time.Minute + f.mockUregistryKeeper.EXPECT(). + GetChainConfig(gomock.Any(), "solana:devnet"). + Return(uregistrytypes.ChainConfig{ + Chain: "solana:devnet", + TssSigningDeadline: &tenMin, + }, nil).AnyTimes() + + // Seed UTX so attachOutboundsToUtx can find it. + utx := types.UniversalTx{Id: "utx-dl-1"} + require.NoError(f.k.UniversalTx.Set(f.ctx, "utx-dl-1", utx)) + + outbound := &types.OutboundTx{ + Id: "outbound-dl-1", + DestinationChain: "solana:devnet", + Recipient: "SomeRecipient", + Amount: "5000", + OutboundStatus: types.Status_PENDING, + } + + err := f.k.TestAttachOutboundsToUtx(f.ctx, "utx-dl-1", []*types.OutboundTx{outbound}, "") + require.NoError(err) + + entry, err := f.k.PendingOutbounds.Get(f.ctx, "outbound-dl-1") + require.NoError(err) + + expectedDeadline := f.ctx.BlockTime().Unix() + int64(tenMin.Seconds()) + require.Equal(expectedDeadline, entry.SigningDeadline, + "signing_deadline should be block_time + 10 minutes") +} + +func TestPendingOutbound_SigningDeadline_NilDuration(t *testing.T) { + f := setupPendingOutboundFixture(t) + require := require.New(t) + + f.mockUregistryKeeper.EXPECT(). + GetChainConfig(gomock.Any(), "eip155:1"). + Return(uregistrytypes.ChainConfig{ + Chain: "eip155:1", + TssSigningDeadline: nil, + }, nil).AnyTimes() + + utx := types.UniversalTx{Id: "utx-dl-2"} + require.NoError(f.k.UniversalTx.Set(f.ctx, "utx-dl-2", utx)) + + outbound := &types.OutboundTx{ + Id: "outbound-dl-2", + DestinationChain: "eip155:1", + Recipient: "0xRecipient", + Amount: "1000", + OutboundStatus: types.Status_PENDING, + } + + err := f.k.TestAttachOutboundsToUtx(f.ctx, "utx-dl-2", []*types.OutboundTx{outbound}, "") + require.NoError(err) + + entry, err := f.k.PendingOutbounds.Get(f.ctx, "outbound-dl-2") + require.NoError(err) + require.Equal(int64(0), entry.SigningDeadline, + "signing_deadline should be 0 when chain has no tss_signing_deadline") +} + +func TestPendingOutbound_SigningDeadline_ChainConfigNotFound(t *testing.T) { + f := setupPendingOutboundFixture(t) + require := require.New(t) + + f.mockUregistryKeeper.EXPECT(). + GetChainConfig(gomock.Any(), "eip155:999"). + Return(uregistrytypes.ChainConfig{}, fmt.Errorf("not found")).AnyTimes() + + utx := types.UniversalTx{Id: "utx-dl-3"} + require.NoError(f.k.UniversalTx.Set(f.ctx, "utx-dl-3", utx)) + + outbound := &types.OutboundTx{ + Id: "outbound-dl-3", + DestinationChain: "eip155:999", + Recipient: "0xRecipient", + Amount: "1000", + OutboundStatus: types.Status_PENDING, + } + + err := f.k.TestAttachOutboundsToUtx(f.ctx, "utx-dl-3", []*types.OutboundTx{outbound}, "") + require.NoError(err) + + entry, err := f.k.PendingOutbounds.Get(f.ctx, "outbound-dl-3") + require.NoError(err) + require.Equal(int64(0), entry.SigningDeadline, + "signing_deadline should be 0 when chain config is not found") +} + +func TestPendingOutbound_SigningDeadline_VisibleInQuery(t *testing.T) { + f := setupPendingOutboundFixture(t) + require := require.New(t) + + // Directly set an entry with a deadline to verify the query surfaces it. + require.NoError(f.k.PendingOutbounds.Set(f.ctx, "outbound-q-1", types.PendingOutboundEntry{ + OutboundId: "outbound-q-1", + UniversalTxId: "utx-q-1", + CreatedAt: 100, + SigningDeadline: 1716700000, + })) + + utx := types.UniversalTx{ + Id: "utx-q-1", + OutboundTx: []*types.OutboundTx{{ + Id: "outbound-q-1", + DestinationChain: "solana:devnet", + Recipient: "SomeRecipient", + Amount: "5000", + OutboundStatus: types.Status_PENDING, + }}, + } + require.NoError(f.k.UniversalTx.Set(f.ctx, "utx-q-1", utx)) + + resp, err := f.queryServer.GetPendingOutbound(f.ctx, &types.QueryGetPendingOutboundRequest{ + OutboundId: "outbound-q-1", + }) + require.NoError(err) + require.Equal(int64(1716700000), resp.Entry.SigningDeadline) + + allResp, err := f.queryServer.AllPendingOutbounds(f.ctx, &types.QueryAllPendingOutboundsRequest{}) + require.NoError(err) + require.Len(allResp.Entries, 1) + require.Equal(int64(1716700000), allResp.Entries[0].SigningDeadline) +} diff --git a/x/uregistry/types/chain_config_test.go b/x/uregistry/types/chain_config_test.go index f470dea26..464aa61dc 100644 --- a/x/uregistry/types/chain_config_test.go +++ b/x/uregistry/types/chain_config_test.go @@ -2,11 +2,17 @@ package types_test import ( "testing" + "time" "github.com/pushchain/push-chain-node/x/uregistry/types" "github.com/stretchr/testify/require" ) +func durationPtr(seconds int64) *time.Duration { + d := time.Duration(seconds) * time.Second + return &d +} + func TestChainConfig_ValidateBasic(t *testing.T) { validMethod := &types.GatewayMethods{ Name: "add_funds", @@ -194,6 +200,49 @@ func TestChainConfig_ValidateBasic(t *testing.T) { }, expectErr: false, }, + { + name: "valid - with tss_signing_deadline", + config: types.ChainConfig{ + Chain: "solana:devnet", + VmType: types.VmType_SVM, + PublicRpcUrl: "https://api.devnet.solana.com", + GatewayAddress: "addr", + BlockConfirmation: validBlockConfirmation, + GatewayMethods: []*types.GatewayMethods{validMethod}, + GasOracleFetchInterval: 30, + TssSigningDeadline: durationPtr(10 * 60), // 10 minutes + }, + expectErr: false, + }, + { + name: "valid - nil tss_signing_deadline", + config: types.ChainConfig{ + Chain: "eip155:1", + VmType: types.VmType_EVM, + PublicRpcUrl: "https://mainnet.infura.io", + GatewayAddress: "0x1234", + BlockConfirmation: validBlockConfirmation, + GatewayMethods: []*types.GatewayMethods{validMethod}, + GasOracleFetchInterval: 30, + TssSigningDeadline: nil, + }, + expectErr: false, + }, + { + name: "invalid - negative tss_signing_deadline", + config: types.ChainConfig{ + Chain: "solana:devnet", + VmType: types.VmType_SVM, + PublicRpcUrl: "https://api.devnet.solana.com", + GatewayAddress: "addr", + BlockConfirmation: validBlockConfirmation, + GatewayMethods: []*types.GatewayMethods{validMethod}, + GasOracleFetchInterval: 30, + TssSigningDeadline: durationPtr(-60), + }, + expectErr: true, + errMsg: "tss_signing_deadline must not be negative", + }, { name: "invalid - bad vault method inside vault_methods", config: types.ChainConfig{ From 6dd01085d527c6472ab022fa7bf04d12e23e5a44 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 26 May 2026 12:42:06 +0530 Subject: [PATCH 03/17] feat: added signingDeadline in OutboundCreated event --- x/uexecutor/keeper/create_outbound.go | 1 + x/uexecutor/types/events.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/x/uexecutor/keeper/create_outbound.go b/x/uexecutor/keeper/create_outbound.go index 6a3c5c7e3..3391c7c29 100644 --- a/x/uexecutor/keeper/create_outbound.go +++ b/x/uexecutor/keeper/create_outbound.go @@ -395,6 +395,7 @@ func (k Keeper) attachOutboundsToUtx( PcTxHash: pcTxHash, LogIndex: logIndex, RevertMsg: revertMsg, + SigningDeadline: signingDeadline, }) if err == nil { ctx.EventManager().EmitEvent(evt) diff --git a/x/uexecutor/types/events.go b/x/uexecutor/types/events.go index 59e621870..e4685d35c 100644 --- a/x/uexecutor/types/events.go +++ b/x/uexecutor/types/events.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "fmt" + "strconv" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -29,6 +30,7 @@ type OutboundCreatedEvent struct { PcTxHash string `json:"pc_tx_hash"` LogIndex string `json:"log_index"` RevertMsg string `json:"revert_msg"` + SigningDeadline int64 `json:"signing_deadline,omitempty"` } // NewOutboundCreatedEvent creates a Cosmos SDK event for outbound creation. @@ -60,6 +62,7 @@ func NewOutboundCreatedEvent(e OutboundCreatedEvent) (sdk.Event, error) { sdk.NewAttribute("pc_tx_hash", e.PcTxHash), sdk.NewAttribute("log_index", e.LogIndex), sdk.NewAttribute("revert_msg", e.RevertMsg), + sdk.NewAttribute("signing_deadline", strconv.FormatInt(e.SigningDeadline, 10)), sdk.NewAttribute("data", string(bz)), // full JSON payload for indexers ) From 6b41b3405283a937c60a1bb4e48d3ef9552c8d43 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 13:43:55 +0530 Subject: [PATCH 04/17] fix: parse signatureDeadline --- universalClient/chains/push/event_parser.go | 2 +- .../chains/push/event_parser_test.go | 99 +++++++++++++------ 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/universalClient/chains/push/event_parser.go b/universalClient/chains/push/event_parser.go index d82d60bba..901d02396 100644 --- a/universalClient/chains/push/event_parser.go +++ b/universalClient/chains/push/event_parser.go @@ -64,7 +64,6 @@ func convertTssEvent(tssEvent *utsstypes.TssEvent) (*store.Event, error) { }, nil } - // convertFundMigrationEvent converts a FundMigration to a store.Event. func convertFundMigrationEvent(migration *utsstypes.FundMigration) (*store.Event, error) { if migration == nil { @@ -138,6 +137,7 @@ func convertOutboundToEvent(entry *uexecutortypes.PendingOutboundEntry, outbound PcTxHash: pcTxHash, LogIndex: logIndex, RevertMsg: revertMsg, + SigningDeadline: entry.SigningDeadline, } eventData, err := json.Marshal(outboundData) diff --git a/universalClient/chains/push/event_parser_test.go b/universalClient/chains/push/event_parser_test.go index 31168157b..b24ba7c1e 100644 --- a/universalClient/chains/push/event_parser_test.go +++ b/universalClient/chains/push/event_parser_test.go @@ -245,42 +245,54 @@ func TestConvertOutboundToEvent(t *testing.T) { assert.Equal(t, "3", data.LogIndex) assert.Empty(t, data.RevertMsg) }) -} - -func TestDefaultExpiryOffset(t *testing.T) { - assert.Equal(t, uint64(600), uint64(DefaultExpiryOffset)) -} -func TestHashEventID(t *testing.T) { - t.Run("deterministic output", func(t *testing.T) { - id1 := hashEventID("keygen", "123") - id2 := hashEventID("keygen", "123") - assert.Equal(t, id1, id2) + t.Run("both nil returns error", func(t *testing.T) { + result, err := convertOutboundToEvent(nil, nil) + require.Error(t, err) + assert.Nil(t, result) + assert.Contains(t, err.Error(), "entry or outbound is nil") }) - t.Run("different types produce different IDs", func(t *testing.T) { - id1 := hashEventID("keygen", "123") - id2 := hashEventID("refresh", "123") - assert.NotEqual(t, id1, id2) - }) + t.Run("chain-supplied signing deadline flows through", func(t *testing.T) { + entry := &uexecutortypes.PendingOutboundEntry{ + OutboundId: "0xabc", + UniversalTxId: "utx-deadline", + CreatedAt: 1000, + SigningDeadline: 1735689600, + } + outbound := &uexecutortypes.OutboundTx{ + Id: "0xabc", + DestinationChain: "solana:devnet", + Amount: "1", + } - t.Run("different raw IDs produce different IDs", func(t *testing.T) { - id1 := hashEventID("keygen", "1") - id2 := hashEventID("keygen", "2") - assert.NotEqual(t, id1, id2) - }) + result, err := convertOutboundToEvent(entry, outbound) + require.NoError(t, err) - t.Run("output is hex string of sha256 length", func(t *testing.T) { - id := hashEventID("type", "id") - assert.Len(t, id, 64) // sha256 = 32 bytes = 64 hex chars + var data uexecutortypes.OutboundCreatedEvent + require.NoError(t, json.Unmarshal(result.EventData, &data)) + assert.Equal(t, int64(1735689600), data.SigningDeadline) }) -} -func TestConvertOutboundToEvent_BothNil(t *testing.T) { - result, err := convertOutboundToEvent(nil, nil) - require.Error(t, err) - assert.Nil(t, result) - assert.Contains(t, err.Error(), "entry or outbound is nil") + t.Run("zero signing deadline stays zero", func(t *testing.T) { + entry := &uexecutortypes.PendingOutboundEntry{ + OutboundId: "0xnone", + UniversalTxId: "utx-no-deadline", + CreatedAt: 1000, + } + outbound := &uexecutortypes.OutboundTx{ + Id: "0xnone", + DestinationChain: "eip155:1", + Amount: "1", + } + + result, err := convertOutboundToEvent(entry, outbound) + require.NoError(t, err) + + var data uexecutortypes.OutboundCreatedEvent + require.NoError(t, json.Unmarshal(result.EventData, &data)) + assert.Equal(t, int64(0), data.SigningDeadline) + }) } func TestConvertFundMigrationEvent(t *testing.T) { @@ -341,3 +353,32 @@ func TestConvertFundMigrationEvent(t *testing.T) { assert.Equal(t, hashEventID(store.EventTypeSignFundMigrate, "42"), result.EventID) }) } + +func TestHashEventID(t *testing.T) { + t.Run("deterministic output", func(t *testing.T) { + id1 := hashEventID("keygen", "123") + id2 := hashEventID("keygen", "123") + assert.Equal(t, id1, id2) + }) + + t.Run("different types produce different IDs", func(t *testing.T) { + id1 := hashEventID("keygen", "123") + id2 := hashEventID("refresh", "123") + assert.NotEqual(t, id1, id2) + }) + + t.Run("different raw IDs produce different IDs", func(t *testing.T) { + id1 := hashEventID("keygen", "1") + id2 := hashEventID("keygen", "2") + assert.NotEqual(t, id1, id2) + }) + + t.Run("output is hex string of sha256 length", func(t *testing.T) { + id := hashEventID("type", "id") + assert.Len(t, id, 64) // sha256 = 32 bytes = 64 hex chars + }) +} + +func TestDefaultExpiryOffset(t *testing.T) { + assert.Equal(t, uint64(600), uint64(DefaultExpiryOffset)) +} From 5e15093b5276b2e04dcb1f803ff95daee164edc7 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 14:23:59 +0530 Subject: [PATCH 05/17] fix: tx builder tss msg creation --- universalClient/chains/evm/tx_builder.go | 4 +- universalClient/chains/svm/rpc_client.go | 4 +- universalClient/chains/svm/tx_builder.go | 36 ++++- universalClient/chains/svm/tx_builder_test.go | 130 +++++++++--------- 4 files changed, 103 insertions(+), 71 deletions(-) diff --git a/universalClient/chains/evm/tx_builder.go b/universalClient/chains/evm/tx_builder.go index 317d9caaa..25b0bc77a 100644 --- a/universalClient/chains/evm/tx_builder.go +++ b/universalClient/chains/evm/tx_builder.go @@ -77,7 +77,9 @@ func NewTxBuilder( return tb, nil } -// GetOutboundSigningRequest creates a signing request from outbound event data +// GetOutboundSigningRequest creates a signing request from outbound event data. +// EVM doesn't consume data.SigningDeadline — deadlines are SVM-only; EVM relies +// on nonce-based finality. func (tb *TxBuilder) GetOutboundSigningRequest( ctx context.Context, data *uetypes.OutboundCreatedEvent, diff --git a/universalClient/chains/svm/rpc_client.go b/universalClient/chains/svm/rpc_client.go index f63676a9d..369cd91da 100644 --- a/universalClient/chains/svm/rpc_client.go +++ b/universalClient/chains/svm/rpc_client.go @@ -333,7 +333,9 @@ func (rc *RPCClient) SimulateTransaction(ctx context.Context, tx *solana.Transac return result.Value, nil } -// GetAccountData fetches account data for a given public key +// GetAccountData fetches account data for a given public key. Uses Solana +// RPC's default commitment (`finalized`, per the JSON-RPC spec) — reorg-safe +// for both terminal decisions and race-recovery probes. func (rc *RPCClient) GetAccountData(ctx context.Context, pubkey solana.PublicKey) ([]byte, error) { var accountData []byte err := rc.executeWithFailover(ctx, "get_account_data", func(client *rpc.Client) error { diff --git a/universalClient/chains/svm/tx_builder.go b/universalClient/chains/svm/tx_builder.go index 1b218fafe..5dc39b8ce 100644 --- a/universalClient/chains/svm/tx_builder.go +++ b/universalClient/chains/svm/tx_builder.go @@ -396,7 +396,7 @@ func (tb *TxBuilder) GetOutboundSigningRequest( // This message is what TSS validators sign. The gateway contract reconstructs // the same message on-chain and verifies the signature matches. messageHash, err := tb.constructTSSMessage( - instructionID, chainID, amount.Uint64(), + instructionID, chainID, data.SigningDeadline, amount.Uint64(), txID, universalTxID, sender, token, gasFee, targetProgram, accounts, ixData, revertRecipient, revertMint, revertMsg, @@ -870,7 +870,7 @@ func (tb *TxBuilder) BuildOutboundTransaction( instructionData = tb.buildWithdrawAndExecuteData( instructionID, txID, universalTxID, amount.Uint64(), sender, - writableFlags, ixData, gasFee, + writableFlags, ixData, gasFee, data.SigningDeadline, signature, recoveryID, req.SigningHash, ) @@ -888,7 +888,7 @@ func (tb *TxBuilder) BuildOutboundTransaction( // ---- revert_universal_tx (unified for SOL and SPL) ---- instructionData = tb.buildRevertData( txID, universalTxID, amount.Uint64(), - recipientPubkey, revertMsgBytes, gasFee, + recipientPubkey, revertMsgBytes, gasFee, data.SigningDeadline, signature, recoveryID, req.SigningHash, ) accounts = tb.buildRevertAccounts( @@ -900,7 +900,7 @@ func (tb *TxBuilder) BuildOutboundTransaction( case instructionID == 4: // ---- rescue_funds ---- instructionData = tb.buildRescueData( - txID, universalTxID, amount.Uint64(), gasFee, + txID, universalTxID, amount.Uint64(), gasFee, data.SigningDeadline, signature, recoveryID, req.SigningHash, ) accounts = tb.buildRescueAccounts( @@ -1216,6 +1216,7 @@ func (tb *TxBuilder) BuildRefRouteTransactions( ixDataHash, writableFlags, gasFee, + data.SigningDeadline, signature, recoveryID, req.SigningHash, ) @@ -1391,6 +1392,7 @@ func (tb *TxBuilder) determineInstructionID(txType uetypes.TxType) (uint8, error func (tb *TxBuilder) constructTSSMessage( instructionID uint8, chainID string, + deadlineUnix int64, amount uint64, txID [32]byte, universalTxID [32]byte, @@ -1404,10 +1406,16 @@ func (tb *TxBuilder) constructTSSMessage( revertMint [32]byte, revertMsg []byte, ) ([]byte, error) { + // Wire format expected by the SVM gateway program's validate_message: + // PREFIX || instruction_id || chain_id || deadline(i64 BE) || amount(u64 BE) || additional_data message := append([]byte(nil), tssMessagePrefix...) message = append(message, instructionID) message = append(message, []byte(chainID)...) + deadlineBytes := make([]byte, 8) + binary.BigEndian.PutUint64(deadlineBytes, uint64(deadlineUnix)) + message = append(message, deadlineBytes...) + amountBytes := make([]byte, 8) binary.BigEndian.PutUint64(amountBytes, amount) message = append(message, amountBytes...) @@ -1706,6 +1714,7 @@ func (tb *TxBuilder) buildWithdrawAndExecuteData( writableFlags []byte, ixData []byte, gasFee uint64, + deadlineUnix int64, signature []byte, recoveryID byte, messageHash []byte, @@ -1739,6 +1748,10 @@ func (tb *TxBuilder) buildWithdrawAndExecuteData( binary.LittleEndian.PutUint64(gasFeeBytes, gasFee) data = append(data, gasFeeBytes...) + deadlineBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(deadlineBytes, uint64(deadlineUnix)) + data = append(data, deadlineBytes...) + data = append(data, signature...) data = append(data, recoveryID) data = append(data, messageHash...) @@ -1767,6 +1780,7 @@ func (tb *TxBuilder) buildRevertData( revertRecipient solana.PublicKey, revertMsg []byte, gasFee uint64, + deadlineUnix int64, signature []byte, recoveryID byte, messageHash []byte, @@ -1793,6 +1807,10 @@ func (tb *TxBuilder) buildRevertData( binary.LittleEndian.PutUint64(gasFeeBytes, gasFee) data = append(data, gasFeeBytes...) + deadlineBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(deadlineBytes, uint64(deadlineUnix)) + data = append(data, deadlineBytes...) + data = append(data, signature...) data = append(data, recoveryID) data = append(data, messageHash...) @@ -1817,6 +1835,7 @@ func (tb *TxBuilder) buildRescueData( universalTxID [32]byte, amount uint64, gasFee uint64, + deadlineUnix int64, signature []byte, recoveryID byte, messageHash []byte, @@ -1836,6 +1855,10 @@ func (tb *TxBuilder) buildRescueData( binary.LittleEndian.PutUint64(gasFeeBytes, gasFee) data = append(data, gasFeeBytes...) + deadlineBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(deadlineBytes, uint64(deadlineUnix)) + data = append(data, deadlineBytes...) + data = append(data, signature...) data = append(data, recoveryID) data = append(data, messageHash...) @@ -2216,6 +2239,7 @@ func (tb *TxBuilder) buildWithdrawAndExecuteRefData( ixDataHash [32]byte, writableFlags []byte, gasFee uint64, + deadlineUnix int64, signature []byte, recoveryID byte, messageHash []byte, @@ -2242,6 +2266,10 @@ func (tb *TxBuilder) buildWithdrawAndExecuteRefData( binary.LittleEndian.PutUint64(gasFeeBytes, gasFee) data = append(data, gasFeeBytes...) + deadlineBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(deadlineBytes, uint64(deadlineUnix)) + data = append(data, deadlineBytes...) + data = append(data, signature...) data = append(data, recoveryID) data = append(data, messageHash...) diff --git a/universalClient/chains/svm/tx_builder_test.go b/universalClient/chains/svm/tx_builder_test.go index 2c145c5fd..3e9b613b9 100644 --- a/universalClient/chains/svm/tx_builder_test.go +++ b/universalClient/chains/svm/tx_builder_test.go @@ -345,6 +345,38 @@ func TestAnchorDiscriminator(t *testing.T) { } } +func TestAnchorDiscriminatorKnownValues(t *testing.T) { + // Verify discriminator values are deterministic and can be independently computed + for _, method := range []string{"finalize_universal_tx", "revert_universal_tx", "rescue_funds"} { + disc := anchorDiscriminator(method) + h := sha256.Sum256([]byte("global:" + method)) + assert.Equal(t, h[:8], disc, "discriminator for %s", method) + } +} + +// TestRefRouteDiscriminatorConstants pins the hardcoded discriminator byte +// arrays for the ref-route instructions to their canonical Anchor derivation. +// These are protocol-critical: a single-byte typo would silently make every +// store / finalize-ref / close call fail against the on-chain gateway with a +// "fallback function not found" error, with no signal at compile time. +func TestRefRouteDiscriminatorConstants(t *testing.T) { + cases := []struct { + method string + got [8]byte + }{ + {"store_execute_ix_data", discStoreExecuteIxData}, + {"finalize_universal_tx_with_ix_data_ref", discFinalizeUniversalTxRef}, + {"close_stored_ix_data", discCloseStoredIxData}, + } + for _, c := range cases { + t.Run(c.method, func(t *testing.T) { + h := sha256.Sum256([]byte("global:" + c.method)) + assert.Equal(t, h[:8], c.got[:], + "discriminator constant for %s does not match sha256(\"global:%s\")[:8]", c.method, c.method) + }) + } +} + func TestConstructTSSMessage(t *testing.T) { builder := newTestBuilder(t) @@ -356,7 +388,7 @@ func TestConstructTSSMessage(t *testing.T) { t.Run("withdraw (id=1) message format", func(t *testing.T) { hash, err := builder.constructTSSMessage( - 1, "devnet", 1000000, + 1, "devnet", int64(0), 1000000, txID, utxID, sender, token, 0, // gasFee target, nil, nil, @@ -393,7 +425,7 @@ func TestConstructTSSMessage(t *testing.T) { ixData := []byte{0xDE, 0xAD, 0xBE, 0xEF} hash, err := builder.constructTSSMessage( - 2, "devnet", 2000000, + 2, "devnet", int64(0), 2000000, txID, utxID, sender, token, 100, // gasFee target, accs, ixData, @@ -440,7 +472,7 @@ func TestConstructTSSMessage(t *testing.T) { t.Run("revert SOL (id=3) message format", func(t *testing.T) { revertRecipient := makeTxID(0xEE) hash, err := builder.constructTSSMessage( - 3, "devnet", 500000, + 3, "devnet", int64(0), 500000, txID, utxID, sender, token, 0, [32]byte{}, nil, nil, revertRecipient, [32]byte{}, nil, @@ -470,7 +502,7 @@ func TestConstructTSSMessage(t *testing.T) { revertRecipient := makeTxID(0xEE) revertMint := makeTxID(0xFF) hash, err := builder.constructTSSMessage( - 3, "devnet", 750000, + 3, "devnet", int64(0), 750000, txID, utxID, sender, token, 0, [32]byte{}, nil, nil, revertRecipient, revertMint, nil, @@ -504,14 +536,14 @@ func TestConstructTSSMessage(t *testing.T) { // the same TSS signature. revertRecipient := makeTxID(0xEE) hashA, err := builder.constructTSSMessage( - 3, "devnet", 500000, + 3, "devnet", int64(0), 500000, txID, utxID, sender, token, 0, [32]byte{}, nil, nil, revertRecipient, [32]byte{}, []byte("reason A"), ) require.NoError(t, err) hashB, err := builder.constructTSSMessage( - 3, "devnet", 500000, + 3, "devnet", int64(0), 500000, txID, utxID, sender, token, 0, [32]byte{}, nil, nil, revertRecipient, [32]byte{}, []byte("reason B"), @@ -526,14 +558,14 @@ func TestConstructTSSMessage(t *testing.T) { // still produce the same hash. rescueRecipient := makeTxID(0xEE) hashA, err := builder.constructTSSMessage( - 4, "devnet", 300000, + 4, "devnet", int64(0), 300000, txID, utxID, sender, token, 50, [32]byte{}, nil, nil, rescueRecipient, [32]byte{}, []byte("reason A"), ) require.NoError(t, err) hashB, err := builder.constructTSSMessage( - 4, "devnet", 300000, + 4, "devnet", int64(0), 300000, txID, utxID, sender, token, 50, [32]byte{}, nil, nil, rescueRecipient, [32]byte{}, []byte("reason B"), @@ -545,7 +577,7 @@ func TestConstructTSSMessage(t *testing.T) { t.Run("rescue SOL (id=4) message format", func(t *testing.T) { rescueRecipient := makeTxID(0xEE) hash, err := builder.constructTSSMessage( - 4, "devnet", 300000, + 4, "devnet", int64(0), 300000, txID, utxID, sender, token, 50, [32]byte{}, nil, nil, rescueRecipient, [32]byte{}, nil, @@ -573,7 +605,7 @@ func TestConstructTSSMessage(t *testing.T) { rescueRecipient := makeTxID(0xEE) rescueMint := makeTxID(0xFF) hash, err := builder.constructTSSMessage( - 4, "devnet", 400000, + 4, "devnet", int64(0), 400000, txID, utxID, sender, token, 75, [32]byte{}, nil, nil, rescueRecipient, rescueMint, nil, @@ -602,7 +634,7 @@ func TestConstructTSSMessage(t *testing.T) { // Verify that the chain_id in the message is raw UTF-8, not Borsh-encoded chainID := "test_chain" hash1, err := builder.constructTSSMessage( - 1, chainID, 0, + 1, chainID, int64(0), 0, [32]byte{}, [32]byte{}, [20]byte{}, [32]byte{}, 0, [32]byte{}, nil, nil, [32]byte{}, [32]byte{}, nil, @@ -627,7 +659,7 @@ func TestConstructTSSMessage(t *testing.T) { t.Run("unknown instruction ID returns error", func(t *testing.T) { _, err := builder.constructTSSMessage( - 99, "devnet", 0, + 99, "devnet", int64(0), 0, [32]byte{}, [32]byte{}, [20]byte{}, [32]byte{}, 0, [32]byte{}, nil, nil, [32]byte{}, [32]byte{}, nil, @@ -642,7 +674,7 @@ func TestConstructTSSMessage_HashIsKeccak256(t *testing.T) { // Construct a simple withdraw message and verify the hash algo hash, err := builder.constructTSSMessage( - 1, "x", 0, + 1, "x", int64(0), 0, [32]byte{}, [32]byte{}, [20]byte{}, [32]byte{}, 0, [32]byte{}, nil, nil, [32]byte{}, [32]byte{}, nil, @@ -917,7 +949,7 @@ func TestBuildWithdrawAndExecuteData(t *testing.T) { data := builder.buildWithdrawAndExecuteData( 1, txID, utxID, 1000000, sender, []byte{}, []byte{}, // empty writable_flags and ix_data for withdraw - 0, // gasFee + 0, int64(0), // gasFee sig, 2, msgHash, ) @@ -970,7 +1002,7 @@ func TestBuildWithdrawAndExecuteData(t *testing.T) { data := builder.buildWithdrawAndExecuteData( 2, txID, utxID, 500, sender, wf, ixData, - 100, // gasFee + 100, int64(0), // gasFee sig, 0, msgHash, ) @@ -1015,7 +1047,7 @@ func TestBuildRevertData(t *testing.T) { msgHash := make([]byte, 32) t.Run("revert uses correct discriminator (revert_universal_tx)", func(t *testing.T) { - data := builder.buildRevertData(txID, utxID, 1000, recipient, revertMsg, 0, sig, 1, msgHash) + data := builder.buildRevertData(txID, utxID, 1000, recipient, revertMsg, 0, int64(0), sig, 1, msgHash) expectedDisc := anchorDiscriminator("revert_universal_tx") assert.Equal(t, expectedDisc, data[:8]) @@ -1029,7 +1061,7 @@ func TestBuildRevertData(t *testing.T) { }) t.Run("revert with empty revert_msg", func(t *testing.T) { - data := builder.buildRevertData(txID, utxID, 2000, recipient, nil, 0, sig, 0, msgHash) + data := builder.buildRevertData(txID, utxID, 2000, recipient, nil, 0, int64(0), sig, 0, msgHash) expectedDisc := anchorDiscriminator("revert_universal_tx") assert.Equal(t, expectedDisc, data[:8]) @@ -1047,7 +1079,7 @@ func TestBuildRescueData(t *testing.T) { msgHash := make([]byte, 32) t.Run("rescue uses correct discriminator", func(t *testing.T) { - data := builder.buildRescueData(txID, utxID, 5000, 100, sig, 1, msgHash) + data := builder.buildRescueData(txID, utxID, 5000, 100, int64(0), sig, 1, msgHash) expectedDisc := anchorDiscriminator("rescue_funds") assert.Equal(t, expectedDisc, data[:8], "discriminator") @@ -1063,9 +1095,9 @@ func TestBuildRescueData(t *testing.T) { }) t.Run("rescue has no revert instructions (unlike revert)", func(t *testing.T) { - rescueData := builder.buildRescueData(txID, utxID, 1000, 50, sig, 0, msgHash) + rescueData := builder.buildRescueData(txID, utxID, 1000, 50, int64(0), sig, 0, msgHash) revertRecipient := solana.MustPublicKeyFromBase58(testGatewayAddress) - revertData := builder.buildRevertData(txID, utxID, 1000, revertRecipient, nil, 50, sig, 0, msgHash) + revertData := builder.buildRevertData(txID, utxID, 1000, revertRecipient, nil, 50, int64(0), sig, 0, msgHash) // Rescue should be shorter than revert (no recipient + revert_msg fields) assert.Less(t, len(rescueData), len(revertData), "rescue data should be shorter than revert data") @@ -1395,7 +1427,7 @@ func TestEndToEndWithdrawMessageAndData(t *testing.T) { target := makeTxID(0xDD) msgHash, err := builder.constructTSSMessage( - 1, "devnet", 1000000, + 1, "devnet", int64(0), 1000000, txID, utxID, sender, token, 0, target, nil, nil, [32]byte{}, [32]byte{}, nil, @@ -1405,7 +1437,7 @@ func TestEndToEndWithdrawMessageAndData(t *testing.T) { sig := make([]byte, 64) instrData := builder.buildWithdrawAndExecuteData( 1, txID, utxID, 1000000, sender, - []byte{}, []byte{}, 0, + []byte{}, []byte{}, 0, int64(0), sig, 0, msgHash, ) @@ -1417,38 +1449,6 @@ func TestEndToEndWithdrawMessageAndData(t *testing.T) { assert.Equal(t, msgHash, msgHashFromData, "message_hash in instruction data must match TSS message hash") } -func TestAnchorDiscriminatorKnownValues(t *testing.T) { - // Verify discriminator values are deterministic and can be independently computed - for _, method := range []string{"finalize_universal_tx", "revert_universal_tx", "rescue_funds"} { - disc := anchorDiscriminator(method) - h := sha256.Sum256([]byte("global:" + method)) - assert.Equal(t, h[:8], disc, "discriminator for %s", method) - } -} - -// TestRefRouteDiscriminatorConstants pins the hardcoded discriminator byte -// arrays for the ref-route instructions to their canonical Anchor derivation. -// These are protocol-critical: a single-byte typo would silently make every -// store / finalize-ref / close call fail against the on-chain gateway with a -// "fallback function not found" error, with no signal at compile time. -func TestRefRouteDiscriminatorConstants(t *testing.T) { - cases := []struct { - method string - got [8]byte - }{ - {"store_execute_ix_data", discStoreExecuteIxData}, - {"finalize_universal_tx_with_ix_data_ref", discFinalizeUniversalTxRef}, - {"close_stored_ix_data", discCloseStoredIxData}, - } - for _, c := range cases { - t.Run(c.method, func(t *testing.T) { - h := sha256.Sum256([]byte("global:" + c.method)) - assert.Equal(t, h[:8], c.got[:], - "discriminator constant for %s does not match sha256(\"global:%s\")[:8]", c.method, c.method) - }) - } -} - func TestEndToEndWithRealSignature(t *testing.T) { builder := newTestBuilder(t) evmKey, _, _ := generateTestEVMKey(t) @@ -1464,7 +1464,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { // 1. Construct TSS message hash (what TSS nodes would sign) msgHash, err := builder.constructTSSMessage( - 1, "devnet", amount, + 1, "devnet", int64(0), amount, txID, utxID, sender, token, 0, target, nil, nil, [32]byte{}, [32]byte{}, nil, @@ -1477,7 +1477,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { // 3. Build instruction data with real signature instrData := builder.buildWithdrawAndExecuteData( 1, txID, utxID, amount, sender, - []byte{}, []byte{}, 0, + []byte{}, []byte{}, 0, int64(0), sig, recoveryID, msgHash, ) @@ -1497,7 +1497,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { ixData := []byte{0xDE, 0xAD} msgHash, err := builder.constructTSSMessage( - 2, "devnet", amount, + 2, "devnet", int64(0), amount, txID, utxID, sender, token, 0, target, accs, ixData, [32]byte{}, [32]byte{}, nil, @@ -1510,7 +1510,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { instrData := builder.buildWithdrawAndExecuteData( 2, txID, utxID, amount, sender, wf, ixData, - 0, + 0, int64(0), sig, recoveryID, msgHash, ) @@ -1526,7 +1526,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { revertRecipient := makeTxID(0xEE) msgHash, err := builder.constructTSSMessage( - 3, "devnet", amount, + 3, "devnet", int64(0), amount, txID, utxID, sender, token, 0, [32]byte{}, nil, nil, revertRecipient, [32]byte{}, nil, @@ -1538,7 +1538,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { recipient := solana.PublicKeyFromBytes(revertRecipient[:]) instrData := builder.buildRevertData( txID, utxID, amount, recipient, - []byte("revert msg"), 0, + []byte("revert msg"), 0, int64(0), sig, recoveryID, msgHash, ) @@ -1552,7 +1552,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { rescueRecipient := makeTxID(0xEE) msgHash, err := builder.constructTSSMessage( - 4, "devnet", amount, + 4, "devnet", int64(0), amount, txID, utxID, sender, token, 50, [32]byte{}, nil, nil, rescueRecipient, [32]byte{}, nil, @@ -1562,7 +1562,7 @@ func TestEndToEndWithRealSignature(t *testing.T) { sig, recoveryID := signMessageHash(t, evmKey, msgHash) instrData := builder.buildRescueData( - txID, utxID, amount, 50, + txID, utxID, amount, 50, int64(0), sig, recoveryID, msgHash, ) @@ -1814,7 +1814,7 @@ func TestBuildWithdrawAndExecuteRefData_ArgOrder(t *testing.T) { pushAccount, ixDataHash, writableFlags, - 500_000, // gas_fee + 500_000, int64(0), // gas_fee signature, 1, // recovery_id msgHash, @@ -2398,7 +2398,7 @@ func buildAndSimulateRescue(t *testing.T, rpcClient *RPCClient, builder *TxBuild gasFee := uint64(0) messageHash, err := builder.constructTSSMessage( - 4, chainID, amount, + 4, chainID, int64(0), amount, txID, universalTxID, sender, token, gasFee, [32]byte{}, nil, nil, revertRecipient, revertMint, nil, @@ -2407,7 +2407,7 @@ func buildAndSimulateRescue(t *testing.T, rpcClient *RPCClient, builder *TxBuild sig, recoveryID := signMessageHash(t, evmKey, messageHash) - instructionData := builder.buildRescueData(txID, universalTxID, amount, gasFee, sig, recoveryID, messageHash) + instructionData := builder.buildRescueData(txID, universalTxID, amount, gasFee, int64(0), sig, recoveryID, messageHash) // Derive PDAs configPDA, _, err := solana.FindProgramAddress([][]byte{[]byte("config")}, builder.gatewayAddress) From fc7a69c1baed7e8211abaeb74587d30b303ce7c7 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 16:23:22 +0530 Subject: [PATCH 06/17] add: check for queryTime --- universalClient/chains/common/types.go | 10 +- universalClient/chains/evm/tx_builder.go | 9 +- universalClient/chains/evm/tx_builder_test.go | 11 +- universalClient/chains/svm/rpc_client.go | 34 +++++ universalClient/chains/svm/tx_builder.go | 28 ++-- .../tss/coordinator/coordinator_test.go | 4 +- .../tss/txbroadcaster/broadcaster_test.go | 143 +++++++++++++++--- 7 files changed, 190 insertions(+), 49 deletions(-) diff --git a/universalClient/chains/common/types.go b/universalClient/chains/common/types.go index d0526d59d..ffb3fd493 100644 --- a/universalClient/chains/common/types.go +++ b/universalClient/chains/common/types.go @@ -67,9 +67,13 @@ type TxBuilder interface { // IsAlreadyExecuted checks whether a transaction with the given txID has already been // executed on the destination chain (e.g., by another relayer). - // For SVM: checks if the ExecutedTx PDA exists on-chain. - // For EVM: returns false (EVM uses nonce-based replay protection). - IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) + // For SVM: checks if the ExecutedTx PDA exists on-chain, AND returns the + // unix timestamp of the latest finalized block. Callers use this as the + // cluster's "now" to gate deadline-based give-up/REVERT decisions and to + // detect cluster halt or finalization stall (queryBlockTime far behind + // wall-clock). 0 means freshness couldn't be determined. + // For EVM: returns (false, 0, nil). EVM uses nonce-based replay protection. + IsAlreadyExecuted(ctx context.Context, txID string) (executed bool, queryBlockTime int64, err error) // GetGasFeeUsed returns the gas fee used by a transaction on the destination chain. // EVM: fetches receipt and returns gasUsed * effectiveGasPrice as decimal string. diff --git a/universalClient/chains/evm/tx_builder.go b/universalClient/chains/evm/tx_builder.go index 25b0bc77a..59594cfca 100644 --- a/universalClient/chains/evm/tx_builder.go +++ b/universalClient/chains/evm/tx_builder.go @@ -441,10 +441,11 @@ func parseGasLimit(gasLimitStr string) (*big.Int, error) { return gasLimit, nil } -// IsAlreadyExecuted returns false for EVM. EVM uses nonce-based replay protection, -// checked via GetNextNonce in the broadcaster. -func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) { - return false, nil +// IsAlreadyExecuted returns (false, 0, nil) for EVM. EVM uses nonce-based +// replay protection (checked via GetNextNonce in the broadcaster); the +// cluster-time signal is SVM-only. +func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error) { + return false, 0, nil } diff --git a/universalClient/chains/evm/tx_builder_test.go b/universalClient/chains/evm/tx_builder_test.go index 50fd54e39..e1da9da67 100644 --- a/universalClient/chains/evm/tx_builder_test.go +++ b/universalClient/chains/evm/tx_builder_test.go @@ -549,27 +549,30 @@ func TestFinalizeUniversalTxUnifiedEncoding(t *testing.T) { } } -// TestIsAlreadyExecuted tests the stub that always returns false +// TestIsAlreadyExecuted tests the stub that always returns (false, 0, nil) func TestIsAlreadyExecuted(t *testing.T) { builder := newTestTxBuilder(t) ctx := context.Background() t.Run("always returns false", func(t *testing.T) { - executed, err := builder.IsAlreadyExecuted(ctx, "0x1234567890abcdef") + executed, queryBlockTime, err := builder.IsAlreadyExecuted(ctx, "0x1234567890abcdef") assert.NoError(t, err) assert.False(t, executed) + assert.Equal(t, int64(0), queryBlockTime) }) t.Run("returns false for empty txID", func(t *testing.T) { - executed, err := builder.IsAlreadyExecuted(ctx, "") + executed, queryBlockTime, err := builder.IsAlreadyExecuted(ctx, "") assert.NoError(t, err) assert.False(t, executed) + assert.Equal(t, int64(0), queryBlockTime) }) t.Run("returns false for arbitrary txID", func(t *testing.T) { - executed, err := builder.IsAlreadyExecuted(ctx, "any-string-at-all") + executed, queryBlockTime, err := builder.IsAlreadyExecuted(ctx, "any-string-at-all") assert.NoError(t, err) assert.False(t, executed) + assert.Equal(t, int64(0), queryBlockTime) }) } diff --git a/universalClient/chains/svm/rpc_client.go b/universalClient/chains/svm/rpc_client.go index 369cd91da..fb788b7a8 100644 --- a/universalClient/chains/svm/rpc_client.go +++ b/universalClient/chains/svm/rpc_client.go @@ -172,6 +172,40 @@ func (rc *RPCClient) GetLatestSlot(ctx context.Context) (uint64, error) { return slot, err } +// LatestFinalizedBlockTime returns the unix timestamp of the latest finalized +// block — the cluster's view of "now" against which on-chain deadline checks +// fire. Used by broadcaster and resolver to gate deadline-based decisions: +// comparing local wall-clock to this value catches host-clock skew, full +// cluster halts (block time stops advancing), and finalization stalls (the +// latest *finalized* block ages even while production continues). +// +// Returns 0 + nil error if block time is unavailable for the latest slot +// (e.g., the slot is too new for the RPC to have indexed). Returns 0 + err +// only when the slot lookup itself fails. +func (rc *RPCClient) LatestFinalizedBlockTime(ctx context.Context) (int64, error) { + slot, err := rc.GetLatestSlot(ctx) + if err != nil { + return 0, err + } + + var blockTime int64 + if err := rc.executeWithFailover(ctx, "get_block_time", func(client *rpc.Client) error { + t, innerErr := client.GetBlockTime(ctx, slot) + if innerErr != nil { + return innerErr + } + if t != nil { + blockTime = int64(*t) + } + return nil + }); err != nil { + // Block-time lookup failed (e.g., slot too recent). Surface 0 so caller + // treats it as "unknown freshness" and defers irreversible decisions. + return 0, nil + } + return blockTime, nil +} + // GetRecentBlockhash gets a recent blockhash for transaction building func (rc *RPCClient) GetRecentBlockhash(ctx context.Context) (solana.Hash, error) { var blockhash solana.Hash diff --git a/universalClient/chains/svm/tx_builder.go b/universalClient/chains/svm/tx_builder.go index 5dc39b8ce..784ac1d3a 100644 --- a/universalClient/chains/svm/tx_builder.go +++ b/universalClient/chains/svm/tx_builder.go @@ -426,14 +426,19 @@ func (tb *TxBuilder) GetNextNonce(ctx context.Context, signerAddress string, use } // IsAlreadyExecuted checks if the ExecutedTx PDA for the given txID exists on-chain, -// indicating another relayer has already processed this transaction. -func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) { +// indicating another relayer has already processed this transaction. Also +// returns the latest finalized block's unix timestamp — the cluster's view of +// "now" — so callers can gate deadline-based decisions against cluster time +// rather than the host's local clock. queryBlockTime is best-effort: 0 means +// the RPC couldn't supply it and the caller should treat cluster freshness as +// unknown (defer irreversible decisions). +func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error) { txIDBytes, err := hex.DecodeString(removeHexPrefix(txID)) if err != nil { - return false, fmt.Errorf("invalid txID: %s", txID) + return false, 0, fmt.Errorf("invalid txID: %s", txID) } if len(txIDBytes) != 32 { - return false, fmt.Errorf("txID must be 32 bytes, got %d", len(txIDBytes)) + return false, 0, fmt.Errorf("txID must be 32 bytes, got %d", len(txIDBytes)) } var txIDArr [32]byte @@ -441,17 +446,16 @@ func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, executedTxPDA, _, err := solana.FindProgramAddress([][]byte{executedSubTxSeed, txIDArr[:]}, tb.gatewayAddress) if err != nil { - return false, fmt.Errorf("failed to derive executed_tx PDA: %w", err) + return false, 0, fmt.Errorf("failed to derive executed_tx PDA: %w", err) } - data, err := tb.rpcClient.GetAccountData(ctx, executedTxPDA) - if err != nil { - // Account doesn't exist or RPC error — treat as not executed - return false, nil - } + data, _ := tb.rpcClient.GetAccountData(ctx, executedTxPDA) + executed := len(data) > 0 + + // Cluster freshness signal — best-effort; 0 on RPC failure. + blockTime, _ := tb.rpcClient.LatestFinalizedBlockTime(ctx) - // If we got non-empty data, the PDA exists → tx was already executed - return len(data) > 0, nil + return executed, blockTime, nil } // GetGasFeeUsed returns "0" for SVM. SVM gas accounting is handled via vault diff --git a/universalClient/tss/coordinator/coordinator_test.go b/universalClient/tss/coordinator/coordinator_test.go index b26baca04..1e347231a 100644 --- a/universalClient/tss/coordinator/coordinator_test.go +++ b/universalClient/tss/coordinator/coordinator_test.go @@ -53,9 +53,9 @@ func (m *coordMockTxBuilder) VerifyBroadcastedTx(ctx context.Context, txHash str return args.Bool(0), args.Get(1).(uint64), args.Get(2).(uint64), args.Get(3).(uint8), args.Error(4) } -func (m *coordMockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) { +func (m *coordMockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error) { args := m.Called(ctx, txID) - return args.Bool(0), args.Error(1) + return args.Bool(0), args.Get(1).(int64), args.Error(2) } func (m *coordMockTxBuilder) GetGasFeeUsed(ctx context.Context, txHash string) (string, error) { diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index dff2d811f..854cec7a1 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -53,9 +53,9 @@ func (m *mockTxBuilder) VerifyBroadcastedTx(ctx context.Context, txHash string) return args.Bool(0), args.Get(1).(uint64), args.Get(2).(uint64), args.Get(3).(uint8), args.Error(4) } -func (m *mockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) { +func (m *mockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error) { args := m.Called(ctx, txID) - return args.Bool(0), args.Error(1) + return args.Bool(0), args.Get(1).(int64), args.Error(2) } func (m *mockTxBuilder) GetGasFeeUsed(ctx context.Context, txHash string) (string, error) { @@ -153,6 +153,42 @@ func insertSignedEvent(t *testing.T, db *gorm.DB, eventID, destChain string, non require.NoError(t, db.Create(&event).Error) } +// insertSignedSVMEventWithDeadline inserts a SIGNED outbound with an explicit +// SigningDeadline on the persisted payload. Used by tests that exercise the +// broadcaster's deadline-gated retry behavior. +func insertSignedSVMEventWithDeadline(t *testing.T, db *gorm.DB, eventID, destChain string, nonce uint64, deadlineUnix int64) { + t.Helper() + sig := hex.EncodeToString(make([]byte, 64)) + hash := hex.EncodeToString(make([]byte, 32)) + data := SignedOutboundData{ + OutboundCreatedEvent: uexecutortypes.OutboundCreatedEvent{ + TxID: "tx-123", + UniversalTxId: "utx-456", + DestinationChain: destChain, + Recipient: "0xRecipient", + Amount: "1000000", + SigningDeadline: deadlineUnix, + }, + SigningData: &SigningData{ + Signature: sig, + SigningHash: hash, + Nonce: nonce, + }, + } + body, err := json.Marshal(data) + require.NoError(t, err) + event := store.Event{ + EventID: eventID, + BlockHeight: 100, + ExpiryBlockHeight: 99999, + Type: "SIGN_OUTBOUND", + ConfirmationType: "STANDARD", + Status: store.StatusSigned, + EventData: body, + } + require.NoError(t, db.Create(&event).Error) +} + func getEvent(t *testing.T, db *gorm.DB, eventID string) store.Event { t.Helper() var ev store.Event @@ -330,7 +366,7 @@ func TestSVM_BroadcastFails_PDAExists_MarksBroadcasted(t *testing.T) { builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("", fmt.Errorf("tx simulation failed: account already exists")) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(true, nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(true, int64(0), nil) b := newBroadcaster(evtStore, ch, "") b.processSigned(context.Background()) @@ -340,54 +376,113 @@ func TestSVM_BroadcastFails_PDAExists_MarksBroadcasted(t *testing.T) { require.Equal(t, "solana:mainnet:", ev.BroadcastedTxHash) // empty tx hash } -func TestSVM_BroadcastFails_FirstAttempt_StaysSignedAndCountsAttempt(t *testing.T) { - // Broadcast fails, tx not on chain → stay SIGNED, increment in-memory - // attempt counter. Retry happens next tick; only after maxSVMBroadcastAttempts - // do we escalate. +func TestSVM_BroadcastFails_BeforeDeadline_StaysSigned(t *testing.T) { + // F-2026-16964: broadcast fails before deadline → stay SIGNED, retry next tick. + // The deadline (not an attempt counter) is the sole cap on retries now. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) - insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()+600) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("", fmt.Errorf("simulation failed: invalid instruction")) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), nil) b := newBroadcaster(evtStore, ch, "") b.processSigned(context.Background()) ev := getEvent(t, db, "ev-1") - require.Equal(t, store.StatusSigned, ev.Status) - require.Equal(t, uint32(1), b.svmBroadcastAttempts["ev-1"]) + require.Equal(t, store.StatusSigned, ev.Status, "before deadline, failures stay SIGNED for retry") } -func TestSVM_BroadcastFails_ExhaustsAttempts_MarksBroadcasted(t *testing.T) { - // After maxSVMBroadcastAttempts failures, escalate to BROADCASTED-empty so - // the resolver can REVERT, and clear the in-memory counter. +func TestSVM_BroadcastFails_PastDeadline_MarksBroadcastedForRevert(t *testing.T) { + // Past local deadline + cluster confirms deadline expired + cluster fresh → + // BROADCASTED("") so the resolver REVERTs. No broadcast attempt. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) - insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()-3600) + // PDA absent, cluster time = now (fresh) and well past deadline → cluster-confirmed expiry. + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix(), nil) + + b := newBroadcaster(evtStore, ch, "") + b.processSigned(context.Background()) + + ev := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusBroadcasted, ev.Status) + require.Equal(t, "solana:mainnet:", ev.BroadcastedTxHash, "empty tx hash signals REVERT to resolver") + builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) +} + +func TestSVM_PastLocalDeadline_ClusterStale_StaysSigned(t *testing.T) { + // Past local deadline but cluster appears stale (halt or finalization stall) + // → defer give-up. Stays SIGNED, no broadcast attempt. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()-3600) + // Cluster time is well over 120s old → treated as stale. + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix()-600, nil) + + b := newBroadcaster(evtStore, ch, "") + b.processSigned(context.Background()) + + ev := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusSigned, ev.Status) + builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) +} + +func TestSVM_PastLocalDeadline_ClusterSaysStillInWindow_FallsThroughToBroadcast(t *testing.T) { + // Local clock ahead of cluster: local says past deadline+slack, but + // cluster's own (fresh) clock says still inside deadline+slack → + // broadcaster falls through and attempts to broadcast. Constructed so + // the cluster time is both fresh (<120s old) and not yet past deadline+slack. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + now := time.Now().Unix() + // Local sees deadline as 65s ago → past slack (60s). + deadline := now - 65 + // Cluster time is 10s old (fresh) and <= deadline+slack (now-5). + clusterTime := now - 10 + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, deadline) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, clusterTime, nil) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return("", fmt.Errorf("persistent failure")) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, nil) + Return("tx-hash-ok", nil) b := newBroadcaster(evtStore, ch, "") - // Seed the counter one attempt away from the cap, then the next call hits - // the cap and escalates. - b.svmBroadcastAttempts["ev-1"] = maxSVMBroadcastAttempts - 1 b.processSigned(context.Background()) ev := getEvent(t, db, "ev-1") require.Equal(t, store.StatusBroadcasted, ev.Status) - require.Equal(t, "solana:mainnet:", ev.BroadcastedTxHash, "empty tx hash signals REVERT to resolver") - _, present := b.svmBroadcastAttempts["ev-1"] - require.False(t, present, "counter should be cleared after escalation") + require.Equal(t, "solana:mainnet:tx-hash-ok", ev.BroadcastedTxHash) +} + +func TestSVM_PastLocalDeadline_RPCError_StaysSigned(t *testing.T) { + // Past local deadline but cluster check itself errors → defer give-up. + // Stays SIGNED, no broadcast attempt. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()-3600) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), fmt.Errorf("RPC down")) + + b := newBroadcaster(evtStore, ch, "") + b.processSigned(context.Background()) + + ev := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusSigned, ev.Status) + builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) } func TestSVM_BroadcastFails_PDACheckFails_StaysSigned(t *testing.T) { @@ -401,7 +496,7 @@ func TestSVM_BroadcastFails_PDACheckFails_StaysSigned(t *testing.T) { builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("", fmt.Errorf("RPC timeout")) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, fmt.Errorf("RPC down")) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), fmt.Errorf("RPC down")) b := newBroadcaster(evtStore, ch, "") b.processSigned(context.Background()) From 30442654a172819f51313cf99455fb94de72c280 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 16:41:33 +0530 Subject: [PATCH 07/17] fix: add deadline check in broadcast --- .../tss/txbroadcaster/broadcaster.go | 18 +--- .../tss/txbroadcaster/broadcaster_test.go | 29 +++--- universalClient/tss/txbroadcaster/svm.go | 94 +++++++++++-------- 3 files changed, 73 insertions(+), 68 deletions(-) diff --git a/universalClient/tss/txbroadcaster/broadcaster.go b/universalClient/tss/txbroadcaster/broadcaster.go index 09457328e..d4f3bd592 100644 --- a/universalClient/tss/txbroadcaster/broadcaster.go +++ b/universalClient/tss/txbroadcaster/broadcaster.go @@ -63,13 +63,6 @@ type Broadcaster struct { checkInterval time.Duration logger zerolog.Logger getTSSAddress func(ctx context.Context) (string, error) - - // svmBroadcastAttempts is an in-memory failure counter per event_id used to - // cap SVM retries before escalating to REVERT. Lost on process restart by - // design — restart resets all counters, giving the operator a fresh budget. - // Temporary mechanism; the signature-deadline system will supersede it. - // Safe without a mutex: processSigned drains events serially. - svmBroadcastAttempts map[string]uint32 } // NewBroadcaster creates a new tx broadcaster. @@ -79,12 +72,11 @@ func NewBroadcaster(cfg Config) *Broadcaster { interval = 15 * time.Second } return &Broadcaster{ - eventStore: cfg.EventStore, - chains: cfg.Chains, - checkInterval: interval, - logger: cfg.Logger.With().Str("component", "txbroadcaster").Logger(), - getTSSAddress: cfg.GetTSSAddress, - svmBroadcastAttempts: make(map[string]uint32), + eventStore: cfg.EventStore, + chains: cfg.Chains, + checkInterval: interval, + logger: cfg.Logger.With().Str("component", "txbroadcaster").Logger(), + getTSSAddress: cfg.GetTSSAddress, } } diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index 854cec7a1..c0f715b6c 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -377,8 +377,8 @@ func TestSVM_BroadcastFails_PDAExists_MarksBroadcasted(t *testing.T) { } func TestSVM_BroadcastFails_BeforeDeadline_StaysSigned(t *testing.T) { - // F-2026-16964: broadcast fails before deadline → stay SIGNED, retry next tick. - // The deadline (not an attempt counter) is the sole cap on retries now. + // Broadcast fails before deadline → stay SIGNED, retry next tick. The + // deadline is the only retry cap; failures inside the window keep cycling. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} @@ -418,41 +418,38 @@ func TestSVM_BroadcastFails_PastDeadline_MarksBroadcastedForRevert(t *testing.T) builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) } -func TestSVM_PastLocalDeadline_ClusterStale_StaysSigned(t *testing.T) { - // Past local deadline but cluster appears stale (halt or finalization stall) - // → defer give-up. Stays SIGNED, no broadcast attempt. +func TestSVM_PastLocalDeadline_ExecutedByPeer_MarksBroadcasted(t *testing.T) { + // Past local deadline + peer landed the tx (PDA exists) → BROADCASTED("") + // so the resolver sees it and marks COMPLETED. No broadcast attempt. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()-3600) - // Cluster time is well over 120s old → treated as stale. - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix()-600, nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(true, time.Now().Unix(), nil) b := newBroadcaster(evtStore, ch, "") b.processSigned(context.Background()) ev := getEvent(t, db, "ev-1") - require.Equal(t, store.StatusSigned, ev.Status) + require.Equal(t, store.StatusBroadcasted, ev.Status) + require.Equal(t, "solana:mainnet:", ev.BroadcastedTxHash) builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) } func TestSVM_PastLocalDeadline_ClusterSaysStillInWindow_FallsThroughToBroadcast(t *testing.T) { - // Local clock ahead of cluster: local says past deadline+slack, but - // cluster's own (fresh) clock says still inside deadline+slack → - // broadcaster falls through and attempts to broadcast. Constructed so - // the cluster time is both fresh (<120s old) and not yet past deadline+slack. + // Local clock ahead of cluster: local says past deadline, but the + // cluster's own (fresh) clock is still before the deadline → broadcaster + // falls through and attempts to broadcast. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) now := time.Now().Unix() - // Local sees deadline as 65s ago → past slack (60s). - deadline := now - 65 - // Cluster time is 10s old (fresh) and <= deadline+slack (now-5). - clusterTime := now - 10 + deadline := now - 1 // local says 1s past deadline + clusterTime := now - 30 // cluster says 30s before deadline; well within freshness insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, deadline) builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, clusterTime, nil) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). diff --git a/universalClient/tss/txbroadcaster/svm.go b/universalClient/tss/txbroadcaster/svm.go index 976f6fe70..761edf8f2 100644 --- a/universalClient/tss/txbroadcaster/svm.go +++ b/universalClient/tss/txbroadcaster/svm.go @@ -2,31 +2,34 @@ package txbroadcaster import ( "context" + "time" "github.com/pushchain/push-chain-node/universalClient/store" ) -// maxSVMBroadcastAttempts caps the number of failed broadcast attempts before -// the broadcaster gives up and marks the event for REVERT. +// broadcastSVM broadcasts a signed Solana transaction and moves the event to +// its next state. // -// TEMPORARY: a future signature-deadline mechanism will supersede this -// attempt-based cap with time-based finality. Until then, this prevents events -// from looping indefinitely on persistent failures (bad payload, downstream -// program upgrade, etc.). -const maxSVMBroadcastAttempts = 10 - -// broadcastSVM broadcasts a signed Solana transaction. +// Three phases, top to bottom: +// +// 1. If local clock is past the signed deadline, check the cluster's own +// clock (latest finalized block time) before giving up. The cluster +// clock — not the host clock — is what the gateway program enforces +// against, so it's the authoritative cutoff. +// 2. Broadcast. +// 3. On broadcast error, check whether a peer landed the same signed tx. // -// Three branches: -// - Success → BROADCASTED with tx hash. -// - Tx already executed by another relayer → BROADCASTED with empty hash. -// - Anything else → increment attempt counter; stay SIGNED until the cap is -// hit, then mark BROADCASTED with empty hash so the resolver can REVERT. +// The give-up cutoff is exactly `clusterTime > deadline`. The finalized block +// time lags the on-chain `Clock::unix_timestamp` by ~13s, so by the time our +// reading crosses the deadline the program has already been rejecting new +// attempts. Cluster staleness is not handled here: the resolver gates the +// irreversible REVERT vote on freshness, and falling through to "broadcast" +// is the safe direction if our cluster view is unreliable. // -// Branch 3 deliberately absorbs every other failure mode (RPC lag, race-lost, -// CPI failure, blockhash stale, transport error, etc.) — we don't classify -// them, we just retry. The attempt cap is the safety valve for genuinely -// stuck events. +// Outcomes: +// - BROADCASTED(real-hash) → broadcast succeeded +// - BROADCASTED("") → peer landed it, or cluster confirmed expiry +// - stay SIGNED → retry next tick func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data *SignedOutboundData, chainID string) { client, err := b.chains.GetClient(chainID) if err != nil { @@ -38,7 +41,6 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder") return } - signingReq, signature, err := decodeSigningData(data.SigningData) if err != nil { b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to decode signing data") @@ -46,36 +48,50 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data } outboundData := data.OutboundCreatedEvent - txHash, broadcastErr := builder.BroadcastOutboundSigningRequest(ctx, signingReq, &outboundData, signature) + txID := outboundData.TxID + deadline := data.SigningDeadline + now := time.Now().Unix() + + // Past local deadline — confirm with the cluster before giving up. + if deadline > 0 && now > deadline { + executed, clusterTime, checkErr := builder.IsAlreadyExecuted(ctx, txID) + log := b.logger.With(). + Str("event_id", event.EventID).Str("chain", chainID). + Int64("signing_deadline", deadline).Int64("cluster_block_time", clusterTime).Logger() + + switch { + case checkErr != nil: + log.Debug().Err(checkErr).Msg("SVM cluster check failed at deadline, retry next tick") + return + case executed: + log.Info().Msg("SVM tx executed by peer past local deadline, marking BROADCASTED") + b.markBroadcasted(event, chainID, "") + return + case clusterTime > deadline: + log.Warn().Msg("SVM deadline cluster-confirmed expired, marking BROADCASTED for resolver REVERT") + b.markBroadcasted(event, chainID, "") + return + } + // Cluster says still inside the window (or freshness unknown) — broadcast. + } + // Broadcast attempt. + txHash, broadcastErr := builder.BroadcastOutboundSigningRequest(ctx, signingReq, &outboundData, signature) if broadcastErr == nil { - delete(b.svmBroadcastAttempts, event.EventID) b.markBroadcasted(event, chainID, txHash) return } - // Tx may have landed via another relayer — that ends the event cleanly. - if executed, execErr := builder.IsAlreadyExecuted(ctx, outboundData.TxID); execErr == nil && executed { - delete(b.svmBroadcastAttempts, event.EventID) + // Race: a peer may have landed the same signed tx in the meantime. + if executed, _, _ := builder.IsAlreadyExecuted(ctx, txID); executed { b.logger.Info().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Msg("broadcast failed but tx already executed on-chain, marking BROADCASTED") - b.markBroadcasted(event, chainID, "") - return - } - - // Broadcast failed and tx isn't on chain. Count the attempt; cap or retry. - attempts := b.svmBroadcastAttempts[event.EventID] + 1 - if attempts >= maxSVMBroadcastAttempts { - delete(b.svmBroadcastAttempts, event.EventID) - b.logger.Warn().Err(broadcastErr).Uint32("attempts", attempts). - Str("event_id", event.EventID).Str("chain", chainID). - Msg("SVM broadcast exhausted retry budget, marking BROADCASTED for resolver to REVERT") + Msg("SVM broadcast failed but tx executed on chain (race), marking BROADCASTED") b.markBroadcasted(event, chainID, "") return } - b.svmBroadcastAttempts[event.EventID] = attempts - b.logger.Info().Err(broadcastErr).Uint32("attempts", attempts). + b.logger.Info().Err(broadcastErr). Str("event_id", event.EventID).Str("chain", chainID). - Msg("SVM broadcast failed, staying SIGNED for next-tick retry") + Int64("signing_deadline", deadline). + Msg("SVM broadcast failed, staying SIGNED for next tick") } From e0753869e70d2d38d1c1f7df51552ff45e170a61 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 16:53:10 +0530 Subject: [PATCH 08/17] fix: handle deadline = 0 , legacy tx --- universalClient/tss/txbroadcaster/svm.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/universalClient/tss/txbroadcaster/svm.go b/universalClient/tss/txbroadcaster/svm.go index 761edf8f2..fd0f9436b 100644 --- a/universalClient/tss/txbroadcaster/svm.go +++ b/universalClient/tss/txbroadcaster/svm.go @@ -12,12 +12,12 @@ import ( // // Three phases, top to bottom: // -// 1. If local clock is past the signed deadline, check the cluster's own -// clock (latest finalized block time) before giving up. The cluster -// clock — not the host clock — is what the gateway program enforces -// against, so it's the authoritative cutoff. -// 2. Broadcast. -// 3. On broadcast error, check whether a peer landed the same signed tx. +// 1. If local clock is past the signed deadline, check the cluster's own +// clock (latest finalized block time) before giving up. The cluster +// clock — not the host clock — is what the gateway program enforces +// against, so it's the authoritative cutoff. +// 2. Broadcast. +// 3. On broadcast error, check whether a peer landed the same signed tx. // // The give-up cutoff is exactly `clusterTime > deadline`. The finalized block // time lags the on-chain `Clock::unix_timestamp` by ~13s, so by the time our @@ -53,7 +53,7 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data now := time.Now().Unix() // Past local deadline — confirm with the cluster before giving up. - if deadline > 0 && now > deadline { + if now > deadline { executed, clusterTime, checkErr := builder.IsAlreadyExecuted(ctx, txID) log := b.logger.With(). Str("event_id", event.EventID).Str("chain", chainID). From 0016352b52864d74d85d755674b27d51f4e52d08 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 17:37:56 +0530 Subject: [PATCH 09/17] fix: svm revert logic --- universalClient/tss/txresolver/svm.go | 85 ++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/universalClient/tss/txresolver/svm.go b/universalClient/tss/txresolver/svm.go index 6243afa51..cfe61ba9e 100644 --- a/universalClient/tss/txresolver/svm.go +++ b/universalClient/tss/txresolver/svm.go @@ -2,19 +2,58 @@ package txresolver import ( "context" + "encoding/json" + "time" "github.com/pushchain/push-chain-node/universalClient/store" ) +// svmRevertSlackSeconds is the buffer past the signed deadline before the +// resolver finalizes REVERT. Matches the broadcaster's slack so the two +// modules agree on when the payload is dead. +const svmRevertSlackSeconds int64 = 60 + +// svmClusterStaleSeconds is how far the latest finalized block's timestamp +// can lag wall-clock before the cluster is treated as halted or stalled — +// either case means our `finalized` queries may be missing recently-included +// txs, so we defer REVERT. MUST match the broadcaster-side constant. +const svmClusterStaleSeconds int64 = 120 + +// svmEventEnvelope is the slice of the persisted outbound event the resolver +// needs to make a REVERT decision: just the chain-emitted signing deadline. +type svmEventEnvelope struct { + SigningDeadline int64 `json:"signing_deadline,omitempty"` +} + +// extractSVMDeadline returns the unix-second deadline emitted by Push chain on +// the OutboundCreatedEvent. Zero means the destination chain didn't configure +// a deadline window — caller falls back to the pre-deadline eager-revert +// behavior. +func extractSVMDeadline(event *store.Event) int64 { + var env svmEventEnvelope + if err := json.Unmarshal(event.EventData, &env); err != nil { + return 0 + } + return env.SigningDeadline +} + // resolveSVM checks the on-chain ExecutedTx PDA and moves the event to COMPLETED or REVERTED. // -// With the V2 gateway contract, Solana transactions either land atomically or fully revert. -// A failed CPI means nothing is created on-chain (no PDA, no event). The resolver checks -// whether the ExecutedTx PDA exists to determine the outcome: +// The REVERT decision is gated on the cluster's own clock (latest finalized +// block timestamp returned by IsAlreadyExecuted) rather than the host's local +// clock. This catches three failure modes that would otherwise cause a false +// REVERT: host clock skew, full cluster halt (block time stops advancing), +// and finalization stalls (production continues but finalized lags). +// +// - PDA exists → COMPLETED. +// - PDA check RPC error → stay BROADCASTED, retry. +// - PDA absent + cluster time unknown (0) → stay BROADCASTED, retry. +// - PDA absent + cluster stale (>120s old) → stay BROADCASTED, retry. +// - PDA absent + cluster says still in window → stay BROADCASTED, retry. +// - PDA absent + cluster confirms past deadline → REVERT. // -// - PDA exists → mark COMPLETED (success vote comes from destination chain event listening) -// - PDA absent → vote failure on Push chain and mark REVERTED (triggers user refund) -// - RPC error → stay BROADCASTED, retry next tick +// Legacy events (deadline = 0) preserve the pre-deadline eager-revert path — +// REVERT as soon as PDA is absent, no cluster check needed. func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID string) { txID, utxID, err := extractOutboundIDs(event) if err != nil { @@ -35,9 +74,8 @@ func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID s return } - executed, err := builder.IsAlreadyExecuted(ctx, txID) + executed, clusterTime, err := builder.IsAlreadyExecuted(ctx, txID) if err != nil { - // RPC error — stay BROADCASTED, retry next tick r.logger.Debug().Err(err).Str("event_id", event.EventID).Str("tx_id", txID). Msg("SVM PDA check failed, will retry next tick") return @@ -53,6 +91,35 @@ func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID s return } - // PDA not found — tx was not executed on destination chain, no gas consumed + // PDA absent. Decide REVERT using the cluster's own clock so we don't + // false-revert during halt/stall or host clock skew. + deadline := extractSVMDeadline(event) + if deadline == 0 { + // Legacy event: no deadline, fall back to eager revert. + _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", "tx not executed on destination chain") + return + } + + switch { + case clusterTime == 0: + r.logger.Debug(). + Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). + Msg("SVM cluster time unavailable, deferring REVERT decision") + return + case time.Now().Unix()-clusterTime > svmClusterStaleSeconds: + r.logger.Warn(). + Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). + Int64("cluster_block_time", clusterTime). + Msg("SVM cluster appears stale, deferring REVERT") + return + case clusterTime <= deadline+svmRevertSlackSeconds: + r.logger.Debug(). + Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). + Int64("signing_deadline", deadline). + Int64("cluster_block_time", clusterTime). + Msg("SVM PDA absent but cluster clock still inside deadline window, will retry next tick") + return + } + _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", "tx not executed on destination chain") } From 439fb396b9cdb4e8ac8fdadb3e33c1911b4ecf94 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 17:45:04 +0530 Subject: [PATCH 10/17] fix: tc --- universalClient/tss/txresolver/svm.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/universalClient/tss/txresolver/svm.go b/universalClient/tss/txresolver/svm.go index cfe61ba9e..610fb07e5 100644 --- a/universalClient/tss/txresolver/svm.go +++ b/universalClient/tss/txresolver/svm.go @@ -9,14 +9,14 @@ import ( ) // svmRevertSlackSeconds is the buffer past the signed deadline before the -// resolver finalizes REVERT. Matches the broadcaster's slack so the two -// modules agree on when the payload is dead. +// resolver finalizes REVERT. Gives an in-flight tx that's already confirmed +// time to reach `finalized` before we vote against it. const svmRevertSlackSeconds int64 = 60 // svmClusterStaleSeconds is how far the latest finalized block's timestamp // can lag wall-clock before the cluster is treated as halted or stalled — // either case means our `finalized` queries may be missing recently-included -// txs, so we defer REVERT. MUST match the broadcaster-side constant. +// txs, so we defer REVERT. const svmClusterStaleSeconds int64 = 120 // svmEventEnvelope is the slice of the persisted outbound event the resolver From 453ee0587ea7d2ad967678132eb3170e3ce18921 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 18:10:22 +0530 Subject: [PATCH 11/17] fix: simulation tc --- universalClient/chains/svm/tx_builder_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/universalClient/chains/svm/tx_builder_test.go b/universalClient/chains/svm/tx_builder_test.go index 3e9b613b9..f4b9ec64d 100644 --- a/universalClient/chains/svm/tx_builder_test.go +++ b/universalClient/chains/svm/tx_builder_test.go @@ -2206,6 +2206,9 @@ func newDevnetOutbound(t *testing.T, amount, assetAddr, payload, revertMsg, txTy GasFee: "3000000", TxType: txType, RevertMsg: revertMsg, + // 10-minute window past wall-clock — the on-chain program enforces + // Clock::unix_timestamp <= signing_deadline + SigningDeadline: time.Now().Unix() + 600, }, evmKey } @@ -2397,8 +2400,10 @@ func buildAndSimulateRescue(t *testing.T, rpcClient *RPCClient, builder *TxBuild } gasFee := uint64(0) + // 10-minute window past wall-clock; gateway enforces Clock::unix_timestamp <= deadline. + deadline := time.Now().Unix() + 600 messageHash, err := builder.constructTSSMessage( - 4, chainID, int64(0), amount, + 4, chainID, deadline, amount, txID, universalTxID, sender, token, gasFee, [32]byte{}, nil, nil, revertRecipient, revertMint, nil, @@ -2407,7 +2412,7 @@ func buildAndSimulateRescue(t *testing.T, rpcClient *RPCClient, builder *TxBuild sig, recoveryID := signMessageHash(t, evmKey, messageHash) - instructionData := builder.buildRescueData(txID, universalTxID, amount, gasFee, int64(0), sig, recoveryID, messageHash) + instructionData := builder.buildRescueData(txID, universalTxID, amount, gasFee, deadline, sig, recoveryID, messageHash) // Derive PDAs configPDA, _, err := solana.FindProgramAddress([][]byte{[]byte("config")}, builder.gatewayAddress) From 5c65d28922c40c3c204934f8115608146d241c1b Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 18:11:23 +0530 Subject: [PATCH 12/17] fix: evm revert logic when tx is not found --- universalClient/tss/tss.go | 31 +- universalClient/tss/txresolver/evm.go | 247 ++++++--- universalClient/tss/txresolver/resolver.go | 31 +- .../tss/txresolver/resolver_test.go | 504 +++++++++++++++--- 4 files changed, 630 insertions(+), 183 deletions(-) diff --git a/universalClient/tss/tss.go b/universalClient/tss/tss.go index a22eadc46..b386133e4 100644 --- a/universalClient/tss/tss.go +++ b/universalClient/tss/tss.go @@ -236,27 +236,30 @@ func NewNode(ctx context.Context, cfg Config) (*Node, error) { pushSigner: cfg.PushSigner, stopCh: make(chan struct{}), registeredPeers: make(map[string]bool), - txResolver: txresolver.NewResolver(txresolver.Config{ - EventStore: evtStore, - Chains: cfg.Chains, - PushSigner: cfg.PushSigner, - CheckInterval: sessionExpiryCheckInterval, - Logger: logger, - }), } - // Create broadcaster after node so the closure can capture `node`. + getTSSAddress := func(ctx context.Context) (string, error) { + if node.coordinator == nil { + return "", fmt.Errorf("coordinator not initialized") + } + return node.coordinator.GetTSSAddress(ctx) + } + + node.txResolver = txresolver.NewResolver(txresolver.Config{ + EventStore: evtStore, + Chains: cfg.Chains, + PushSigner: cfg.PushSigner, + CheckInterval: sessionExpiryCheckInterval, + Logger: logger, + GetTSSAddress: getTSSAddress, + }) + node.txBroadcaster = txbroadcaster.NewBroadcaster(txbroadcaster.Config{ EventStore: evtStore, Chains: cfg.Chains, CheckInterval: sessionExpiryCheckInterval, Logger: logger, - GetTSSAddress: func(ctx context.Context) (string, error) { - if node.coordinator == nil { - return "", fmt.Errorf("coordinator not initialized") - } - return node.coordinator.GetTSSAddress(ctx) - }, + GetTSSAddress: getTSSAddress, }) node.expirySweeper = expirysweeper.NewSweeper(expirysweeper.Config{ diff --git a/universalClient/tss/txresolver/evm.go b/universalClient/tss/txresolver/evm.go index 192192d28..db33f26a7 100644 --- a/universalClient/tss/txresolver/evm.go +++ b/universalClient/tss/txresolver/evm.go @@ -2,59 +2,84 @@ package txresolver import ( "context" + "encoding/json" + "github.com/pushchain/push-chain-node/universalClient/chains/common" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/tss/coordinator" ) -// txCheckResult represents the outcome of verifying a tx on chain with not-found retry handling. -type txCheckResult int +// Decision flow for EVM-broadcasted events (outbound and fund migration both +// follow this shape): +// +// - VerifyBroadcastedTx error → stay BROADCASTED (retry) +// - Tx found, insufficient confirmations → stay BROADCASTED (retry) +// - Tx found, status=1 (success) → COMPLETED / vote success +// - Tx found, status=0 (reverted on chain) → REVERT / vote failure with tx hash +// - Tx not found, signed nonce < finalized nonce → REVERT / vote failure (another tx +// consumed our nonce slot) +// - Tx not found, signed nonce >= finalized nonce → rewind to SIGNED so the broadcaster +// re-broadcasts (covers mempool drop) +// - Tx not found, nonce check unavailable → stay BROADCASTED (retry) +// +// The nonce IS the give-up signal; there is no max-retry counter. The two +// flows differ only in (a) which vote function records success/failure and +// (b) where the signer address comes from — current TSS for outbound, OLD TSS +// (derived from the event's old pubkey) for fund migration. + +// signedNonceEnvelope is the slice of any EVM-signed event payload the +// resolver consults on tx-not-found. OldTssPubkey is set only on fund +// migration events; it identifies the sender (and thus the address whose +// nonce gates the decision). +type signedNonceEnvelope struct { + OldTssPubkey string `json:"old_tss_pubkey,omitempty"` + SigningData *struct { + Nonce uint64 `json:"nonce"` + } `json:"signing_data,omitempty"` +} -const ( - txCheckRetry txCheckResult = iota // tx not found or not enough confirmations, retry later - txCheckMaxRetries // tx not found after max retries - txCheckReverted // tx found, confirmed, status=0 - txCheckSuccess // tx found, confirmed, status=1 -) +func readSignedNonce(event *store.Event) (uint64, bool) { + var env signedNonceEnvelope + if err := json.Unmarshal(event.EventData, &env); err != nil || env.SigningData == nil { + return 0, false + } + return env.SigningData.Nonce, true +} -// checkEVMTx verifies a tx on chain and handles the not-found retry counter. -// Returns the check result, block height, and raw tx hash for further processing. -func (r *Resolver) checkEVMTx(ctx context.Context, event *store.Event, chainID, rawTxHash string) (txCheckResult, uint64) { - found, blockHeight, confirmations, status, err := r.verifyTxOnChain(ctx, chainID, rawTxHash) +func readFundMigrationSigner(event *store.Event) (signer string, nonce uint64, ok bool) { + var env signedNonceEnvelope + if err := json.Unmarshal(event.EventData, &env); err != nil || env.SigningData == nil || env.OldTssPubkey == "" { + return "", 0, false + } + addr, err := coordinator.DeriveEVMAddressFromPubkey(env.OldTssPubkey) if err != nil { - r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("tx verification error") - return txCheckRetry, 0 + return "", 0, false } + return addr, env.SigningData.Nonce, true +} - if !found { - r.notFoundCounts[event.EventID]++ - count := r.notFoundCounts[event.EventID] - r.logger.Debug(). - Str("event_id", event.EventID).Str("tx_hash", rawTxHash). - Int("not_found_count", count).Msg("tx not found on chain, will retry") - - if count >= maxNotFoundRetries { - delete(r.notFoundCounts, event.EventID) - return txCheckMaxRetries, 0 - } - return txCheckRetry, 0 - } +// nonceVerdict captures the outcome of comparing the signed nonce against the +// chain's finalized nonce when a tx hash isn't on chain. +type nonceVerdict int - delete(r.notFoundCounts, event.EventID) +const ( + nonceUnknown nonceVerdict = iota // RPC failed; defer the decision + nonceConsumed // chain advanced past the signed nonce → tx is dead + nonceAvailable // chain hasn't consumed the signed nonce yet → re-broadcast may still land +) - requiredConfs := r.chains.GetStandardConfirmations(chainID) - if confirmations < requiredConfs { - return txCheckRetry, 0 +func checkNonce(ctx context.Context, builder common.TxBuilder, signer string, signedNonce uint64) (nonceVerdict, uint64) { + finalizedNonce, err := builder.GetNextNonce(ctx, signer, true) + if err != nil { + return nonceUnknown, 0 } - - if status == 0 { - return txCheckReverted, blockHeight + if signedNonce < finalizedNonce { + return nonceConsumed, finalizedNonce } - - return txCheckSuccess, blockHeight + return nonceAvailable, finalizedNonce } -// resolveOutboundEVM checks the on-chain receipt for an outbound EVM tx. -// Success vote is done by destination chain event listener, not here. +// resolveOutboundEVM resolves a BROADCASTED outbound on an EVM chain. func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string) { txID, utxID, err := extractOutboundIDs(event) if err != nil { @@ -62,51 +87,135 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c return } - result, blockHeight := r.checkEVMTx(ctx, event, chainID, rawTxHash) - - switch result { - case txCheckRetry: + builder, err := r.getBuilder(chainID) + if err != nil { + r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder, will retry next tick") return + } - case txCheckMaxRetries: - _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", - "tx not found on destination chain after max retries") + found, blockHeight, confirmations, status, vErr := builder.VerifyBroadcastedTx(ctx, rawTxHash) + if vErr != nil { + r.logger.Debug().Err(vErr).Str("event_id", event.EventID).Msg("tx verification error, will retry next tick") + return + } - case txCheckReverted: - gasFeeUsed := "0" - if builder, err := r.getBuilder(chainID); err == nil { - if fee, err := builder.GetGasFeeUsed(ctx, rawTxHash); err == nil { + if found { + if confirmations < r.chains.GetStandardConfirmations(chainID) { + return + } + if status == 0 { + gasFeeUsed := "0" + if fee, fErr := builder.GetGasFeeUsed(ctx, rawTxHash); fErr == nil { gasFeeUsed = fee } + _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, rawTxHash, blockHeight, gasFeeUsed, + "tx execution reverted on destination chain") + return } - _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, rawTxHash, blockHeight, gasFeeUsed, - "tx execution reverted on destination chain") - - case txCheckSuccess: - // Success vote done by destination chain event listener - if err := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusCompleted}); err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to mark event COMPLETED") + if uerr := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusCompleted}); uerr != nil { + r.logger.Warn().Err(uerr).Str("event_id", event.EventID).Msg("failed to mark event COMPLETED") return } - r.logger.Info(). - Str("event_id", event.EventID).Str("tx_hash", rawTxHash). - Msg("outbound EVM tx marked COMPLETED") + r.logger.Info().Str("event_id", event.EventID).Str("tx_hash", rawTxHash).Msg("outbound EVM tx marked COMPLETED") + return + } + + signer, signedNonce, ok := r.outboundSigner(ctx, event) + if !ok { + return + } + verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + switch verdict { + case nonceUnknown: + r.logger.Debug().Str("event_id", event.EventID).Msg("could not fetch finalized nonce, will retry next tick") + case nonceConsumed: + r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). + Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). + Msg("EVM outbound tx not found and nonce already finalized → REVERT") + _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", + "tx not found on destination chain and nonce consumed by another tx") + case nonceAvailable: + r.rewindToSigned(event, chainID, signedNonce, finalizedNonce) } } -// resolveFundMigrationEVM checks the on-chain receipt for a fund migration EVM tx. -// Votes success/failure explicitly since there is no gateway event listener for native transfers. +// resolveFundMigrationEVM mirrors resolveOutboundEVM. The signer comes from +// the event payload (OldTssPubkey) instead of the current TSS, and the +// success/failure path uses the fund-migration voting helper which both votes +// and marks the event in a single step. func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string, migrationID uint64) { - result, _ := r.checkEVMTx(ctx, event, chainID, rawTxHash) + builder, err := r.getBuilder(chainID) + if err != nil { + r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder, will retry next tick") + return + } + + found, _, confirmations, status, vErr := builder.VerifyBroadcastedTx(ctx, rawTxHash) + if vErr != nil { + r.logger.Debug().Err(vErr).Str("event_id", event.EventID).Msg("fund migration tx verification error, will retry next tick") + return + } + + if found { + if confirmations < r.chains.GetStandardConfirmations(chainID) { + return + } + r.voteFundMigrationAndMark(ctx, event, migrationID, rawTxHash, status != 0) + return + } - switch result { - case txCheckRetry: + signer, signedNonce, ok := readFundMigrationSigner(event) + if !ok { + r.logger.Warn().Str("event_id", event.EventID). + Msg("fund migration tx not found and signer info unavailable, staying BROADCASTED") return - case txCheckMaxRetries: + } + verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + switch verdict { + case nonceUnknown: + r.logger.Debug().Str("event_id", event.EventID).Msg("could not fetch finalized nonce, will retry next tick") + case nonceConsumed: + r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). + Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). + Msg("EVM fund migration tx not found and nonce already finalized → REVERT") r.voteFundMigrationAndMark(ctx, event, migrationID, "", false) - case txCheckReverted: - r.voteFundMigrationAndMark(ctx, event, migrationID, rawTxHash, false) - case txCheckSuccess: - r.voteFundMigrationAndMark(ctx, event, migrationID, rawTxHash, true) + case nonceAvailable: + r.rewindToSigned(event, chainID, signedNonce, finalizedNonce) + } +} + +// outboundSigner resolves the outbound signer + signed nonce, logging and +// returning ok=false when either is unavailable so the caller can defer +// without dragging the resolver-level guards into the main flow. +func (r *Resolver) outboundSigner(ctx context.Context, event *store.Event) (string, uint64, bool) { + signedNonce, ok := readSignedNonce(event) + if !ok { + r.logger.Warn().Str("event_id", event.EventID). + Msg("EVM tx not found and signed nonce unavailable, staying BROADCASTED") + return "", 0, false + } + if r.getTSSAddress == nil { + r.logger.Warn().Str("event_id", event.EventID). + Msg("EVM tx not found and no TSS-address resolver configured, staying BROADCASTED") + return "", 0, false + } + addr, err := r.getTSSAddress(ctx) + if err != nil { + r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("could not fetch TSS address, will retry next tick") + return "", 0, false + } + return addr, signedNonce, true +} + +// rewindToSigned moves a BROADCASTED event back to SIGNED so the broadcaster +// will re-broadcast on the next tick. Used when the EVM tx hash isn't visible +// on chain but the signed nonce is still available — covers mempool drops. +func (r *Resolver) rewindToSigned(event *store.Event, chainID string, signedNonce, finalizedNonce uint64) { + if err := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusSigned}); err != nil { + r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to rewind event to SIGNED for re-broadcast") + return } + r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). + Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). + Msg("EVM tx not found and nonce un-consumed, rewound to SIGNED for re-broadcast") } diff --git a/universalClient/tss/txresolver/resolver.go b/universalClient/tss/txresolver/resolver.go index 83c102fd9..8c0703dee 100644 --- a/universalClient/tss/txresolver/resolver.go +++ b/universalClient/tss/txresolver/resolver.go @@ -30,20 +30,19 @@ type Config struct { PushSigner *pushsigner.Signer CheckInterval time.Duration Logger zerolog.Logger + // GetTSSAddress returns the current TSS ECDSA address — used by the EVM + // resolver path to compare on-chain finalized nonce against the signed nonce + GetTSSAddress func(ctx context.Context) (string, error) } -// maxNotFoundRetries is the number of consecutive "not found" checks before reverting. -// At a 30s check interval this gives ~5 minutes for a tx to appear on chain. -const maxNotFoundRetries = 10 - // Resolver takes BROADCASTED txs and moves them to terminal status (COMPLETED or REVERTED). type Resolver struct { - eventStore *eventstore.Store - chains *chains.Chains - pushSigner *pushsigner.Signer - checkInterval time.Duration - logger zerolog.Logger - notFoundCounts map[string]int // eventID -> consecutive not-found count + eventStore *eventstore.Store + chains *chains.Chains + pushSigner *pushsigner.Signer + checkInterval time.Duration + logger zerolog.Logger + getTSSAddress func(ctx context.Context) (string, error) } // NewResolver creates a new tx resolver. @@ -53,12 +52,12 @@ func NewResolver(cfg Config) *Resolver { interval = 15 * time.Second } return &Resolver{ - eventStore: cfg.EventStore, - chains: cfg.Chains, - pushSigner: cfg.PushSigner, - checkInterval: interval, - logger: cfg.Logger.With().Str("component", "txresolver").Logger(), - notFoundCounts: make(map[string]int), + eventStore: cfg.EventStore, + chains: cfg.Chains, + pushSigner: cfg.PushSigner, + checkInterval: interval, + logger: cfg.Logger.With().Str("component", "txresolver").Logger(), + getTSSAddress: cfg.GetTSSAddress, } } diff --git a/universalClient/tss/txresolver/resolver_test.go b/universalClient/tss/txresolver/resolver_test.go index a30d7d554..84e62bd77 100644 --- a/universalClient/tss/txresolver/resolver_test.go +++ b/universalClient/tss/txresolver/resolver_test.go @@ -51,9 +51,9 @@ func (m *mockTxBuilder) VerifyBroadcastedTx(ctx context.Context, txHash string) return args.Bool(0), args.Get(1).(uint64), args.Get(2).(uint64), args.Get(3).(uint8), args.Error(4) } -func (m *mockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, error) { +func (m *mockTxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error) { args := m.Called(ctx, txID) - return args.Bool(0), args.Error(1) + return args.Bool(0), args.Get(1).(int64), args.Error(2) } func (m *mockTxBuilder) GetGasFeeUsed(ctx context.Context, txHash string) (string, error) { @@ -178,6 +178,19 @@ func newResolver(evtStore *eventstore.Store, ch *chains.Chains) *Resolver { }) } +// newResolverWithTSSAddress builds a Resolver that returns a fixed TSS address +// from GetTSSAddress — needed by tests that exercise the EVM nonce-based +// retry/revert path. +func newResolverWithTSSAddress(evtStore *eventstore.Store, ch *chains.Chains, addr string) *Resolver { + return NewResolver(Config{ + EventStore: evtStore, + Chains: ch, + CheckInterval: 0, + Logger: zerolog.Nop(), + GetTSSAddress: func(ctx context.Context) (string, error) { return addr, nil }, + }) +} + func TestParseCAIPTxHash(t *testing.T) { t.Run("valid CAIP tx hash", func(t *testing.T) { chainID, txHash, err := parseCAIPTxHash("eip155:1:0xabc123") @@ -259,7 +272,7 @@ func TestSVM_PDAExists_MarksCompleted(t *testing.T) { eventData := makeOutboundEventData("tx-123", "utx-456", "solana:mainnet") insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(true, nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(true, int64(0), nil) resolver := newResolver(evtStore, ch) ev := getEvent(t, db, "ev-1") @@ -281,7 +294,8 @@ func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) { eventData := makeOutboundEventData("tx-123", "utx-456", "solana:mainnet") insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:", eventData) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, nil) + // Event data has no SigningDeadline → legacy eager-revert path; no cluster check. + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), nil) // No PushSigner — voteFailure logs a warning and returns nil without marking REVERTED. resolver := newResolver(evtStore, ch) @@ -303,7 +317,7 @@ func TestSVM_PDACheckFails_StaysBroadcasted(t *testing.T) { eventData := makeOutboundEventData("tx-123", "utx-456", "solana:mainnet") insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, assert.AnError) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), assert.AnError) resolver := newResolver(evtStore, ch) ev := getEvent(t, db, "ev-1") @@ -313,6 +327,111 @@ func TestSVM_PDACheckFails_StaysBroadcasted(t *testing.T) { require.Equal(t, store.StatusBroadcasted, updated.Status) // stays BROADCASTED } +// makeOutboundEventDataWithDeadline mirrors makeOutboundEventData but sets the +// chain-emitted signing deadline used by the resolver's cluster-time gate. +func makeOutboundEventDataWithDeadline(txID, utxID, destChain string, deadline int64) []byte { + data := uexecutortypes.OutboundCreatedEvent{ + TxID: txID, + UniversalTxId: utxID, + DestinationChain: destChain, + SigningDeadline: deadline, + } + b, _ := json.Marshal(data) + return b +} + +func TestSVM_PDAAbsent_ClusterTimeUnknown_DefersRevert(t *testing.T) { + // PDA absent + deadline set + cluster time = 0 (RPC didn't supply it) → + // stay BROADCASTED, defer REVERT until we can verify cluster health. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + eventData := makeOutboundEventDataWithDeadline("tx-123", "utx-456", "solana:mainnet", time.Now().Unix()-3600) + insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) + + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), nil) + + resolver := newResolver(evtStore, ch) + ev := getEvent(t, db, "ev-1") + resolver.resolveSVM(context.Background(), &ev, "solana:mainnet") + + updated := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusBroadcasted, updated.Status) +} + +func TestSVM_PDAAbsent_ClusterStale_DefersRevert(t *testing.T) { + // PDA absent + cluster time >120s old → cluster halted/lagging → defer REVERT. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + eventData := makeOutboundEventDataWithDeadline("tx-123", "utx-456", "solana:mainnet", time.Now().Unix()-3600) + insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) + + // Cluster block time is 10 minutes old. + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix()-600, nil) + + resolver := newResolver(evtStore, ch) + ev := getEvent(t, db, "ev-1") + resolver.resolveSVM(context.Background(), &ev, "solana:mainnet") + + updated := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusBroadcasted, updated.Status) +} + +func TestSVM_PDAAbsent_ClusterStillInWindow_DefersRevert(t *testing.T) { + // PDA absent + cluster fresh but cluster's clock <= deadline+slack → + // the program still accepts late retries; defer REVERT. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + now := time.Now().Unix() + deadline := now - 30 // local says past, but well under slack + eventData := makeOutboundEventDataWithDeadline("tx-123", "utx-456", "solana:mainnet", deadline) + insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) + + // Cluster time = now (fresh) but <= deadline+slack (deadline+60 = now+30). + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, now, nil) + + resolver := newResolver(evtStore, ch) + ev := getEvent(t, db, "ev-1") + resolver.resolveSVM(context.Background(), &ev, "solana:mainnet") + + updated := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusBroadcasted, updated.Status) +} + +func TestSVM_PDAAbsent_ClusterConfirmsExpiry_Reverts(t *testing.T) { + // PDA absent + cluster fresh + cluster past deadline+slack → REVERT path. + // (With no PushSigner the vote is logged but status stays BROADCASTED; + // what we assert is that the resolver reached the vote call.) + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + now := time.Now().Unix() + eventData := makeOutboundEventDataWithDeadline("tx-123", "utx-456", "solana:mainnet", now-3600) + insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:solTxSig", eventData) + + // Cluster time = now (fresh) and well past deadline+slack. + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, now, nil) + + resolver := newResolver(evtStore, ch) + ev := getEvent(t, db, "ev-1") + resolver.resolveSVM(context.Background(), &ev, "solana:mainnet") + + // No PushSigner → vote returns nil early; status unchanged. The point is + // the resolver REACHED the vote (i.e., didn't defer); covered by absence + // of any defer log path and the mock having been called. + builder.AssertCalled(t, "IsAlreadyExecuted", mock.Anything, "tx-123") +} + func TestSVM_InvalidEventData_Skips(t *testing.T) { // Bad event data → logged and skipped (stays BROADCASTED). evtStore, db := setupTestDB(t) @@ -369,56 +488,6 @@ func TestResolveEventRouting(t *testing.T) { }) } -func TestNotFoundCountTracking(t *testing.T) { - t.Run("increments on not found", func(t *testing.T) { - evtStore, _ := setupTestDB(t) - resolver := NewResolver(Config{ - EventStore: evtStore, - Logger: zerolog.Nop(), - }) - - eventID := "test-event-1" - assert.Equal(t, 0, resolver.notFoundCounts[eventID]) - - resolver.notFoundCounts[eventID]++ - assert.Equal(t, 1, resolver.notFoundCounts[eventID]) - - resolver.notFoundCounts[eventID]++ - assert.Equal(t, 2, resolver.notFoundCounts[eventID]) - }) - - t.Run("cleared after max retries", func(t *testing.T) { - evtStore, _ := setupTestDB(t) - resolver := NewResolver(Config{ - EventStore: evtStore, - Logger: zerolog.Nop(), - }) - - eventID := "test-event-2" - resolver.notFoundCounts[eventID] = maxNotFoundRetries - - // Simulate cleanup - delete(resolver.notFoundCounts, eventID) - assert.Equal(t, 0, resolver.notFoundCounts[eventID]) - }) - - t.Run("cleared when tx found", func(t *testing.T) { - evtStore, _ := setupTestDB(t) - resolver := NewResolver(Config{ - EventStore: evtStore, - Logger: zerolog.Nop(), - }) - - eventID := "test-event-3" - resolver.notFoundCounts[eventID] = 5 - - // Simulate tx found — clear tracking - delete(resolver.notFoundCounts, eventID) - _, exists := resolver.notFoundCounts[eventID] - assert.False(t, exists) - }) -} - func TestVoteFailureAndMarkReverted(t *testing.T) { t.Run("no push signer logs warning and returns nil", func(t *testing.T) { evtStore, _ := setupTestDB(t) @@ -499,31 +568,155 @@ func TestFundMigrationEVM_TxReverted_VotesFailure(t *testing.T) { require.Equal(t, store.StatusBroadcasted, ev.Status) } -func TestFundMigrationEVM_TxNotFound_RetriesAndReverts(t *testing.T) { +// testOldTSSPubkey is a valid compressed secp256k1 pubkey that DeriveEVMAddressFromPubkey +// can parse into testOldTSSAddr. Used by fund migration nonce-based tests. +const testOldTSSPubkey = "03d5d5d290a0ecec420e843fc2a57f1696781ec657e204406fc67bb5fe0c751317" +const testOldTSSAddr = "0x9fed6f778a956244c06a3b905ba45bdb2ec3afea" + +// makeFundMigrationEventDataWithNonce mirrors makeFundMigrationEventData but +// adds OldTssPubkey + signing_data.nonce — the fields the resolver consults +// on tx-not-found. +func makeFundMigrationEventDataWithNonce(migrationID uint64, chain, oldPubkey string, nonce uint64) []byte { + b, _ := json.Marshal(map[string]any{ + "migration_id": migrationID, + "chain": chain, + "old_tss_pubkey": oldPubkey, + "signing_data": map[string]any{ + "nonce": nonce, + }, + }) + return b +} + +func insertBroadcastedFundMigrationEventWithNonce( + t *testing.T, db *gorm.DB, + eventID, chain, broadcastedTxHash string, + migrationID uint64, oldPubkey string, nonce uint64, +) { + t.Helper() + event := store.Event{ + EventID: eventID, + BlockHeight: 100, + ExpiryBlockHeight: 99999, + Type: store.EventTypeSignFundMigrate, + ConfirmationType: "INSTANT", + Status: store.StatusBroadcasted, + EventData: makeFundMigrationEventDataWithNonce(migrationID, chain, oldPubkey, nonce), + BroadcastedTxHash: broadcastedTxHash, + } + require.NoError(t, db.Create(&event).Error) +} + +func TestFundMigrationEVM_NotFound_NonceConsumed_VotesFailure(t *testing.T) { + // Fund migration tx not found AND old-TSS signed nonce already finalized → + // another tx consumed the slot. REVERT path. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) - insertBroadcastedFundMigrationEvent(t, db, "fm-1", "eip155:1", "eip155:1:0xnotfound", 42) + insertBroadcastedFundMigrationEventWithNonce( + t, db, "fm-consumed", "eip155:1", "eip155:1:0xmigmissing", 42, testOldTSSPubkey, 3, + ) - // Tx not found - builder.On("VerifyBroadcastedTx", mock.Anything, "0xnotfound"). + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmigmissing"). Return(false, uint64(0), uint64(0), uint8(0), nil) + builder.On("GetNextNonce", mock.Anything, testOldTSSAddr, true).Return(uint64(5), nil) + + resolver := newResolver(evtStore, ch) // GetTSSAddress not needed; signer derived from event + resolver.processBroadcasted(context.Background()) + + // No PushSigner → vote skipped, status stays BROADCASTED. + ev := getEvent(t, db, "fm-consumed") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertCalled(t, "GetNextNonce", mock.Anything, testOldTSSAddr, true) +} + +func TestFundMigrationEVM_NotFound_NonceUnconsumed_RewindsToSigned(t *testing.T) { + // Fund migration tx not found AND old-TSS signed nonce not yet finalized → + // rewind to SIGNED for re-broadcast. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + insertBroadcastedFundMigrationEventWithNonce( + t, db, "fm-unconsumed", "eip155:1", "eip155:1:0xmigpending", 42, testOldTSSPubkey, 5, + ) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmigpending"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + builder.On("GetNextNonce", mock.Anything, testOldTSSAddr, true).Return(uint64(5), nil) resolver := newResolver(evtStore, ch) + resolver.processBroadcasted(context.Background()) - // Should increment not found count each time, stay BROADCASTED - for i := 0; i < maxNotFoundRetries-1; i++ { - resolver.processBroadcasted(context.Background()) - ev := getEvent(t, db, "fm-1") - require.Equal(t, store.StatusBroadcasted, ev.Status) - } + ev := getEvent(t, db, "fm-unconsumed") + require.Equal(t, store.StatusSigned, ev.Status) +} + +func TestFundMigrationEVM_NotFound_NonceRPCError_StaysBroadcasted(t *testing.T) { + // Fund migration tx not found and nonce RPC errors → defer (retry next tick). + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + insertBroadcastedFundMigrationEventWithNonce( + t, db, "fm-rpc-err", "eip155:1", "eip155:1:0xmigmissing", 42, testOldTSSPubkey, 3, + ) - // On max retries, without pushSigner vote is skipped + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmigmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + builder.On("GetNextNonce", mock.Anything, testOldTSSAddr, true).Return(uint64(0), assert.AnError) + + resolver := newResolver(evtStore, ch) resolver.processBroadcasted(context.Background()) - ev := getEvent(t, db, "fm-1") - require.Equal(t, store.StatusBroadcasted, ev.Status) // no signer = no revert + + ev := getEvent(t, db, "fm-rpc-err") + require.Equal(t, store.StatusBroadcasted, ev.Status) +} + +func TestFundMigrationEVM_NotFound_SignerInfoMissing_StaysBroadcasted(t *testing.T) { + // Fund migration tx not found but OldTssPubkey is missing from the event + // payload → can't derive signer → defer. Uses the standard fund-migration + // helper which doesn't populate OldTssPubkey. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + insertBroadcastedFundMigrationEvent(t, db, "fm-no-pubkey", "eip155:1", "eip155:1:0xmigmissing", 42) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmigmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + + resolver := newResolver(evtStore, ch) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "fm-no-pubkey") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) +} + +func TestFundMigrationEVM_VerifyError_StaysBroadcasted(t *testing.T) { + // VerifyBroadcastedTx errors → defer (retry next tick); no nonce check, no vote. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + insertBroadcastedFundMigrationEvent(t, db, "fm-verify-err", "eip155:1", "eip155:1:0xmigerr", 42) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmigerr"). + Return(false, uint64(0), uint64(0), uint8(0), assert.AnError) + + resolver := newResolver(evtStore, ch) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "fm-verify-err") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) } func TestFundMigrationEVM_InsufficientConfirmations_Retries(t *testing.T) { @@ -547,11 +740,6 @@ func TestFundMigrationEVM_InsufficientConfirmations_Retries(t *testing.T) { } func TestConstants(t *testing.T) { - t.Run("maxNotFoundRetries is reasonable", func(t *testing.T) { - // At 30s interval, 10 retries = ~5 minutes - assert.Equal(t, 10, maxNotFoundRetries) - }) - t.Run("processBroadcastedBatchSize", func(t *testing.T) { assert.Equal(t, 100, processBroadcastedBatchSize) }) @@ -577,16 +765,6 @@ func TestNewResolverDefaults(t *testing.T) { }) assert.Equal(t, 45*time.Second, r.checkInterval) }) - - t.Run("notFoundCounts map is initialized", func(t *testing.T) { - evtStore, _ := setupTestDB(t) - r := NewResolver(Config{ - EventStore: evtStore, - Logger: zerolog.Nop(), - }) - assert.NotNil(t, r.notFoundCounts) - assert.Len(t, r.notFoundCounts, 0) - }) } func TestResolveOutboundEVM_Success_MarksCompleted(t *testing.T) { @@ -670,6 +848,164 @@ func TestResolveOutboundEVM_Reverted_NoPushSigner_StaysBroadcasted(t *testing.T) builder.AssertCalled(t, "GetGasFeeUsed", mock.Anything, "0xreverted") } +// makeOutboundEventDataWithNonce mirrors makeOutboundEventData but adds the +// `signing_data.nonce` field the EVM resolver consults on tx-not-found. +func makeOutboundEventDataWithNonce(txID, utxID, destChain string, nonce uint64) []byte { + b, _ := json.Marshal(map[string]any{ + "tx_id": txID, + "utx_id": utxID, + "destination_chain": destChain, + "signing_data": map[string]any{ + "nonce": nonce, + }, + }) + return b +} + +const testEVMTSSAddr = "0x4D353565442Eb33b66ef88E14336F3F4Bf3a02FB" + +func TestResolveOutboundEVM_NotFound_NonceConsumed_Reverts(t *testing.T) { + // Tx not found AND signed nonce < finalized nonce → another tx consumed + // the slot. Our tx is dead, REVERT path is taken. (No PushSigner means + // the vote is logged but status stays BROADCASTED; we verify the resolver + // took the REVERT branch by checking the nonce RPC was called.) + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventDataWithNonce("tx-100", "utx-200", "eip155:1", 5) + insertBroadcastedEvent(t, db, "ev-consumed-1", "eip155:1", "eip155:1:0xmissing", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + // Finalized nonce = 7 → our nonce 5 is past finalized → consumed. + builder.On("GetNextNonce", mock.Anything, testEVMTSSAddr, true).Return(uint64(7), nil) + + resolver := newResolverWithTSSAddress(evtStore, ch, testEVMTSSAddr) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-consumed-1") + require.Equal(t, store.StatusBroadcasted, ev.Status, "no PushSigner → vote skipped, status unchanged") + builder.AssertCalled(t, "GetNextNonce", mock.Anything, testEVMTSSAddr, true) +} + +func TestResolveOutboundEVM_NotFound_NonceUnconsumed_RewindsToSigned(t *testing.T) { + // Tx not found AND signed nonce >= finalized nonce → tx may still land + // (or was dropped from mempool). Rewind to SIGNED so the broadcaster + // re-broadcasts. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventDataWithNonce("tx-100", "utx-200", "eip155:1", 5) + insertBroadcastedEvent(t, db, "ev-unconsumed-1", "eip155:1", "eip155:1:0xstillpending", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xstillpending"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + // Finalized nonce = 5 → our nonce 5 not yet finalized. + builder.On("GetNextNonce", mock.Anything, testEVMTSSAddr, true).Return(uint64(5), nil) + + resolver := newResolverWithTSSAddress(evtStore, ch, testEVMTSSAddr) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-unconsumed-1") + require.Equal(t, store.StatusSigned, ev.Status) +} + +func TestResolveOutboundEVM_NotFound_NonceRPCError_StaysBroadcasted(t *testing.T) { + // Tx not found and nonce RPC errors → defer (retry next tick). + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventDataWithNonce("tx-100", "utx-200", "eip155:1", 5) + insertBroadcastedEvent(t, db, "ev-rpc-err-1", "eip155:1", "eip155:1:0xmissing", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + builder.On("GetNextNonce", mock.Anything, testEVMTSSAddr, true).Return(uint64(0), assert.AnError) + + resolver := newResolverWithTSSAddress(evtStore, ch, testEVMTSSAddr) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-rpc-err-1") + require.Equal(t, store.StatusBroadcasted, ev.Status) +} + +func TestResolveOutboundEVM_NotFound_SignedNonceMissing_StaysBroadcasted(t *testing.T) { + // Tx not found and event payload has no signing_data.nonce → can't run + // nonce check → defer. Uses the standard outbound helper which doesn't + // populate signing_data. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventData("tx-100", "utx-200", "eip155:1") + insertBroadcastedEvent(t, db, "ev-no-nonce", "eip155:1", "eip155:1:0xmissing", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + + resolver := newResolverWithTSSAddress(evtStore, ch, testEVMTSSAddr) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-no-nonce") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) +} + +func TestResolveOutboundEVM_NotFound_TSSAddressFetchError_StaysBroadcasted(t *testing.T) { + // Tx not found and GetTSSAddress callback errors → defer (retry next tick). + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventDataWithNonce("tx-100", "utx-200", "eip155:1", 5) + insertBroadcastedEvent(t, db, "ev-tss-err", "eip155:1", "eip155:1:0xmissing", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + + resolver := NewResolver(Config{ + EventStore: evtStore, + Chains: ch, + CheckInterval: 0, + Logger: zerolog.Nop(), + GetTSSAddress: func(ctx context.Context) (string, error) { return "", assert.AnError }, + }) + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-tss-err") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) +} + +func TestResolveOutboundEVM_NotFound_NoTSSAddressResolver_StaysBroadcasted(t *testing.T) { + // Tx not found and GetTSSAddress is nil → can't run nonce check → defer. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) + + eventData := makeOutboundEventDataWithNonce("tx-100", "utx-200", "eip155:1", 5) + insertBroadcastedEvent(t, db, "ev-no-tss-1", "eip155:1", "eip155:1:0xmissing", eventData) + + builder.On("VerifyBroadcastedTx", mock.Anything, "0xmissing"). + Return(false, uint64(0), uint64(0), uint8(0), nil) + + resolver := newResolver(evtStore, ch) // no GetTSSAddress configured + resolver.processBroadcasted(context.Background()) + + ev := getEvent(t, db, "ev-no-tss-1") + require.Equal(t, store.StatusBroadcasted, ev.Status) + builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) +} + func TestResolveOutboundEVM_VerifyError_StaysBroadcasted(t *testing.T) { evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} @@ -776,7 +1112,7 @@ func TestResolveOutbound_SVM_RoutingPath(t *testing.T) { insertBroadcastedEvent(t, db, "ev-svm-route", "solana:mainnet", "solana:mainnet:someSig", eventData) // PDA found → COMPLETED - builder.On("IsAlreadyExecuted", mock.Anything, "tx-svm-1").Return(true, nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-svm-1").Return(true, int64(0), nil) resolver := newResolver(evtStore, ch) resolver.processBroadcasted(context.Background()) From f7457e938eb913702653f0251268e98d2bf00d8e Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 18:27:27 +0530 Subject: [PATCH 13/17] fix: log binding --- universalClient/tss/txbroadcaster/evm.go | 37 ++++++++-------- universalClient/tss/txbroadcaster/svm.go | 25 +++++------ universalClient/tss/txresolver/evm.go | 54 ++++++++++++------------ universalClient/tss/txresolver/svm.go | 35 ++++++--------- 4 files changed, 71 insertions(+), 80 deletions(-) diff --git a/universalClient/tss/txbroadcaster/evm.go b/universalClient/tss/txbroadcaster/evm.go index 9279608f8..ed49dcc0a 100644 --- a/universalClient/tss/txbroadcaster/evm.go +++ b/universalClient/tss/txbroadcaster/evm.go @@ -21,20 +21,22 @@ import ( // - nonce consumed (tx landed) → BROADCASTED with tx hash // - nonce NOT consumed → keep SIGNED, retry next tick func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data *SignedOutboundData, chainID string) { + log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + client, err := b.chains.GetClient(chainID) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get chain client") + log.Warn().Err(err).Msg("failed to get chain client") return } builder, err := client.GetTxBuilder() if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder") + log.Warn().Err(err).Msg("failed to get tx builder") return } signingReq, signature, err := decodeSigningData(data.SigningData) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to decode signing data") + log.Warn().Err(err).Msg("failed to decode signing data") return } @@ -49,8 +51,7 @@ func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data // Broadcast failed — check if the tx landed on chain anyway (another node, or "already known") if txHash == "" { - b.logger.Warn().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Msg("failed to assemble tx, will retry next tick") + log.Warn().Err(broadcastErr).Msg("failed to assemble tx, will retry next tick") return } @@ -59,8 +60,7 @@ func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data var addrErr error tssAddress, addrErr = b.getTSSAddress(ctx) if addrErr != nil { - b.logger.Warn().Err(addrErr).Str("event_id", event.EventID). - Msg("failed to get TSS address for nonce check, will retry next tick") + log.Warn().Err(addrErr).Msg("failed to get TSS address for nonce check, will retry next tick") return } } @@ -71,31 +71,33 @@ func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data // broadcastFundMigrationEVM broadcasts a signed EVM fund migration transaction. // Same nonce-consumed recovery pattern as outbound, but uses old TSS address for nonce check. func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *store.Event, data *SignedFundMigrationData, chainID string) { + log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + oldTSSAddr, err := coordinator.DeriveEVMAddressFromPubkey(data.OldTssPubkey) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to derive old TSS address") + log.Warn().Err(err).Msg("failed to derive old TSS address") return } currentTSSAddr, err := coordinator.DeriveEVMAddressFromPubkey(data.CurrentTssPubkey) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to derive new TSS address") + log.Warn().Err(err).Msg("failed to derive new TSS address") return } client, err := b.chains.GetClient(chainID) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get chain client") + log.Warn().Err(err).Msg("failed to get chain client") return } builder, err := client.GetTxBuilder() if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder") + log.Warn().Err(err).Msg("failed to get tx builder") return } signingReq, signature, err := decodeSigningData(data.SigningData) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to decode signing data") + log.Warn().Err(err).Msg("failed to decode signing data") return } @@ -121,8 +123,7 @@ func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *stor } if txHash == "" { - b.logger.Warn().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Msg("failed to assemble fund migration tx, will retry next tick") + log.Warn().Err(broadcastErr).Msg("failed to assemble fund migration tx, will retry next tick") return } @@ -140,11 +141,12 @@ func (b *Broadcaster) checkNonceAndMarkBroadcasted( eventNonce uint64, broadcastErr error, ) { + log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + finalizedNonce, err := builder.GetNextNonce(ctx, signerAddr, true) if err == nil && eventNonce < finalizedNonce { // Nonce consumed — tx is on chain. Mark BROADCASTED so the resolver can verify it. - b.logger.Info().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Str("tx_hash", txHash). + log.Info().Err(broadcastErr).Str("tx_hash", txHash). Uint64("event_nonce", eventNonce).Uint64("finalized_nonce", finalizedNonce). Msg("broadcast failed but tx already on chain, marking BROADCASTED") b.markBroadcasted(event, chainID, txHash) @@ -153,6 +155,5 @@ func (b *Broadcaster) checkNonceAndMarkBroadcasted( // Nonce not consumed — transient error (RPC down, gas issues, etc.). // Keep as SIGNED and retry next tick. - b.logger.Debug().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Msg("broadcast failed, will retry next tick") + log.Debug().Err(broadcastErr).Msg("broadcast failed, will retry next tick") } diff --git a/universalClient/tss/txbroadcaster/svm.go b/universalClient/tss/txbroadcaster/svm.go index fd0f9436b..0924e7fb1 100644 --- a/universalClient/tss/txbroadcaster/svm.go +++ b/universalClient/tss/txbroadcaster/svm.go @@ -31,19 +31,21 @@ import ( // - BROADCASTED("") → peer landed it, or cluster confirmed expiry // - stay SIGNED → retry next tick func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data *SignedOutboundData, chainID string) { + log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + client, err := b.chains.GetClient(chainID) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get chain client") + log.Warn().Err(err).Msg("failed to get chain client") return } builder, err := client.GetTxBuilder() if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder") + log.Warn().Err(err).Msg("failed to get tx builder") return } signingReq, signature, err := decodeSigningData(data.SigningData) if err != nil { - b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to decode signing data") + log.Warn().Err(err).Msg("failed to decode signing data") return } @@ -55,20 +57,18 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data // Past local deadline — confirm with the cluster before giving up. if now > deadline { executed, clusterTime, checkErr := builder.IsAlreadyExecuted(ctx, txID) - log := b.logger.With(). - Str("event_id", event.EventID).Str("chain", chainID). - Int64("signing_deadline", deadline).Int64("cluster_block_time", clusterTime).Logger() + dlog := log.With().Int64("signing_deadline", deadline).Int64("cluster_block_time", clusterTime).Logger() switch { case checkErr != nil: - log.Debug().Err(checkErr).Msg("SVM cluster check failed at deadline, retry next tick") + dlog.Debug().Err(checkErr).Msg("SVM cluster check failed at deadline, retry next tick") return case executed: - log.Info().Msg("SVM tx executed by peer past local deadline, marking BROADCASTED") + dlog.Info().Msg("SVM tx executed by peer past local deadline, marking BROADCASTED") b.markBroadcasted(event, chainID, "") return case clusterTime > deadline: - log.Warn().Msg("SVM deadline cluster-confirmed expired, marking BROADCASTED for resolver REVERT") + dlog.Warn().Msg("SVM deadline cluster-confirmed expired, marking BROADCASTED for resolver REVERT") b.markBroadcasted(event, chainID, "") return } @@ -84,14 +84,11 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data // Race: a peer may have landed the same signed tx in the meantime. if executed, _, _ := builder.IsAlreadyExecuted(ctx, txID); executed { - b.logger.Info().Err(broadcastErr).Str("event_id", event.EventID).Str("chain", chainID). - Msg("SVM broadcast failed but tx executed on chain (race), marking BROADCASTED") + log.Info().Err(broadcastErr).Msg("SVM broadcast failed but tx executed on chain (race), marking BROADCASTED") b.markBroadcasted(event, chainID, "") return } - b.logger.Info().Err(broadcastErr). - Str("event_id", event.EventID).Str("chain", chainID). - Int64("signing_deadline", deadline). + log.Info().Err(broadcastErr).Int64("signing_deadline", deadline). Msg("SVM broadcast failed, staying SIGNED for next tick") } diff --git a/universalClient/tss/txresolver/evm.go b/universalClient/tss/txresolver/evm.go index db33f26a7..465a36356 100644 --- a/universalClient/tss/txresolver/evm.go +++ b/universalClient/tss/txresolver/evm.go @@ -81,21 +81,23 @@ func checkNonce(ctx context.Context, builder common.TxBuilder, signer string, si // resolveOutboundEVM resolves a BROADCASTED outbound on an EVM chain. func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string) { + log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + txID, utxID, err := extractOutboundIDs(event) if err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to extract outbound IDs") + log.Warn().Err(err).Msg("failed to extract outbound IDs") return } builder, err := r.getBuilder(chainID) if err != nil { - r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder, will retry next tick") + log.Debug().Err(err).Msg("failed to get tx builder, will retry next tick") return } found, blockHeight, confirmations, status, vErr := builder.VerifyBroadcastedTx(ctx, rawTxHash) if vErr != nil { - r.logger.Debug().Err(vErr).Str("event_id", event.EventID).Msg("tx verification error, will retry next tick") + log.Debug().Err(vErr).Msg("tx verification error, will retry next tick") return } @@ -113,10 +115,10 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c return } if uerr := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusCompleted}); uerr != nil { - r.logger.Warn().Err(uerr).Str("event_id", event.EventID).Msg("failed to mark event COMPLETED") + log.Warn().Err(uerr).Msg("failed to mark event COMPLETED") return } - r.logger.Info().Str("event_id", event.EventID).Str("tx_hash", rawTxHash).Msg("outbound EVM tx marked COMPLETED") + log.Info().Str("tx_hash", rawTxHash).Msg("outbound EVM tx marked COMPLETED") return } @@ -125,13 +127,12 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c return } verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + nlog := log.With().Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() switch verdict { case nonceUnknown: - r.logger.Debug().Str("event_id", event.EventID).Msg("could not fetch finalized nonce, will retry next tick") + nlog.Debug().Msg("could not fetch finalized nonce, will retry next tick") case nonceConsumed: - r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). - Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). - Msg("EVM outbound tx not found and nonce already finalized → REVERT") + nlog.Info().Msg("EVM outbound tx not found and nonce already finalized → REVERT") _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", "tx not found on destination chain and nonce consumed by another tx") case nonceAvailable: @@ -144,15 +145,17 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c // success/failure path uses the fund-migration voting helper which both votes // and marks the event in a single step. func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string, migrationID uint64) { + log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + builder, err := r.getBuilder(chainID) if err != nil { - r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("failed to get tx builder, will retry next tick") + log.Debug().Err(err).Msg("failed to get tx builder, will retry next tick") return } found, _, confirmations, status, vErr := builder.VerifyBroadcastedTx(ctx, rawTxHash) if vErr != nil { - r.logger.Debug().Err(vErr).Str("event_id", event.EventID).Msg("fund migration tx verification error, will retry next tick") + log.Debug().Err(vErr).Msg("fund migration tx verification error, will retry next tick") return } @@ -166,18 +169,16 @@ func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Eve signer, signedNonce, ok := readFundMigrationSigner(event) if !ok { - r.logger.Warn().Str("event_id", event.EventID). - Msg("fund migration tx not found and signer info unavailable, staying BROADCASTED") + log.Warn().Msg("fund migration tx not found and signer info unavailable, staying BROADCASTED") return } verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + nlog := log.With().Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() switch verdict { case nonceUnknown: - r.logger.Debug().Str("event_id", event.EventID).Msg("could not fetch finalized nonce, will retry next tick") + nlog.Debug().Msg("could not fetch finalized nonce, will retry next tick") case nonceConsumed: - r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). - Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). - Msg("EVM fund migration tx not found and nonce already finalized → REVERT") + nlog.Info().Msg("EVM fund migration tx not found and nonce already finalized → REVERT") r.voteFundMigrationAndMark(ctx, event, migrationID, "", false) case nonceAvailable: r.rewindToSigned(event, chainID, signedNonce, finalizedNonce) @@ -188,20 +189,20 @@ func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Eve // returning ok=false when either is unavailable so the caller can defer // without dragging the resolver-level guards into the main flow. func (r *Resolver) outboundSigner(ctx context.Context, event *store.Event) (string, uint64, bool) { + log := r.logger.With().Str("event_id", event.EventID).Logger() + signedNonce, ok := readSignedNonce(event) if !ok { - r.logger.Warn().Str("event_id", event.EventID). - Msg("EVM tx not found and signed nonce unavailable, staying BROADCASTED") + log.Warn().Msg("EVM tx not found and signed nonce unavailable, staying BROADCASTED") return "", 0, false } if r.getTSSAddress == nil { - r.logger.Warn().Str("event_id", event.EventID). - Msg("EVM tx not found and no TSS-address resolver configured, staying BROADCASTED") + log.Warn().Msg("EVM tx not found and no TSS-address resolver configured, staying BROADCASTED") return "", 0, false } addr, err := r.getTSSAddress(ctx) if err != nil { - r.logger.Debug().Err(err).Str("event_id", event.EventID).Msg("could not fetch TSS address, will retry next tick") + log.Debug().Err(err).Msg("could not fetch TSS address, will retry next tick") return "", 0, false } return addr, signedNonce, true @@ -211,11 +212,12 @@ func (r *Resolver) outboundSigner(ctx context.Context, event *store.Event) (stri // will re-broadcast on the next tick. Used when the EVM tx hash isn't visible // on chain but the signed nonce is still available — covers mempool drops. func (r *Resolver) rewindToSigned(event *store.Event, chainID string, signedNonce, finalizedNonce uint64) { + log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID). + Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() + if err := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusSigned}); err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to rewind event to SIGNED for re-broadcast") + log.Warn().Err(err).Msg("failed to rewind event to SIGNED for re-broadcast") return } - r.logger.Info().Str("event_id", event.EventID).Str("chain", chainID). - Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce). - Msg("EVM tx not found and nonce un-consumed, rewound to SIGNED for re-broadcast") + log.Info().Msg("EVM tx not found and nonce un-consumed, rewound to SIGNED for re-broadcast") } diff --git a/universalClient/tss/txresolver/svm.go b/universalClient/tss/txresolver/svm.go index 610fb07e5..c4e636a7b 100644 --- a/universalClient/tss/txresolver/svm.go +++ b/universalClient/tss/txresolver/svm.go @@ -55,39 +55,38 @@ func extractSVMDeadline(event *store.Event) int64 { // Legacy events (deadline = 0) preserve the pre-deadline eager-revert path — // REVERT as soon as PDA is absent, no cluster check needed. func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID string) { + log := r.logger.With().Str("event_id", event.EventID).Str("chain_id", chainID).Logger() + txID, utxID, err := extractOutboundIDs(event) if err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to extract outbound IDs for SVM resolve") + log.Warn().Err(err).Msg("failed to extract outbound IDs for SVM resolve") return } + log = log.With().Str("tx_id", txID).Logger() client, err := r.chains.GetClient(chainID) if err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Str("chain_id", chainID). - Msg("failed to get chain client for SVM resolve") + log.Warn().Err(err).Msg("failed to get chain client for SVM resolve") return } builder, err := client.GetTxBuilder() if err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Str("chain_id", chainID). - Msg("failed to get tx builder for SVM resolve") + log.Warn().Err(err).Msg("failed to get tx builder for SVM resolve") return } executed, clusterTime, err := builder.IsAlreadyExecuted(ctx, txID) if err != nil { - r.logger.Debug().Err(err).Str("event_id", event.EventID).Str("tx_id", txID). - Msg("SVM PDA check failed, will retry next tick") + log.Debug().Err(err).Msg("SVM PDA check failed, will retry next tick") return } if executed { if err := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusCompleted}); err != nil { - r.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to mark SVM event COMPLETED") + log.Warn().Err(err).Msg("failed to mark SVM event COMPLETED") return } - r.logger.Info().Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). - Msg("SVM ExecutedTx PDA found, marked COMPLETED") + log.Info().Msg("SVM ExecutedTx PDA found, marked COMPLETED") return } @@ -100,24 +99,16 @@ func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID s return } + dlog := log.With().Int64("signing_deadline", deadline).Int64("cluster_block_time", clusterTime).Logger() switch { case clusterTime == 0: - r.logger.Debug(). - Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). - Msg("SVM cluster time unavailable, deferring REVERT decision") + dlog.Debug().Msg("SVM cluster time unavailable, deferring REVERT decision") return case time.Now().Unix()-clusterTime > svmClusterStaleSeconds: - r.logger.Warn(). - Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). - Int64("cluster_block_time", clusterTime). - Msg("SVM cluster appears stale, deferring REVERT") + dlog.Warn().Msg("SVM cluster appears stale, deferring REVERT") return case clusterTime <= deadline+svmRevertSlackSeconds: - r.logger.Debug(). - Str("event_id", event.EventID).Str("tx_id", txID).Str("chain_id", chainID). - Int64("signing_deadline", deadline). - Int64("cluster_block_time", clusterTime). - Msg("SVM PDA absent but cluster clock still inside deadline window, will retry next tick") + dlog.Debug().Msg("SVM PDA absent but cluster clock still inside deadline window, will retry next tick") return } From 4d89ffaf9d6d0cdb8ef9d12ebf4875d92df3293d Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 18:27:38 +0530 Subject: [PATCH 14/17] remove unused fn --- universalClient/tss/txresolver/resolver.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/universalClient/tss/txresolver/resolver.go b/universalClient/tss/txresolver/resolver.go index 8c0703dee..1795f95aa 100644 --- a/universalClient/tss/txresolver/resolver.go +++ b/universalClient/tss/txresolver/resolver.go @@ -208,14 +208,6 @@ func (r *Resolver) getBuilder(chainID string) (common.TxBuilder, error) { return client.GetTxBuilder() } -func (r *Resolver) verifyTxOnChain(ctx context.Context, chainID, txHash string) (bool, uint64, uint64, uint8, error) { - builder, err := r.getBuilder(chainID) - if err != nil { - return false, 0, 0, 0, err - } - return builder.VerifyBroadcastedTx(ctx, txHash) -} - // voteOutboundFailureAndMarkReverted votes failure for an outbound event and marks it REVERTED. func (r *Resolver) voteOutboundFailureAndMarkReverted(ctx context.Context, event *store.Event, txID, utxID, txHash string, blockHeight uint64, gasFeeUsed string, errorMsg string) error { if r.pushSigner == nil { From 2b68a07a6a9a7c863b36b12358a060b00de410eb Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 26 May 2026 18:27:48 +0530 Subject: [PATCH 15/17] chore: tc --- .../tss/txbroadcaster/broadcaster_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index c0f715b6c..c908fb35b 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -336,13 +336,15 @@ func TestEVM_GetTSSAddressNil_UsesEmptyAddress(t *testing.T) { } func TestSVM_BroadcastSuccess_MarksBroadcasted(t *testing.T) { - // Broadcast succeeds → BROADCASTED with tx hash. + // Broadcast succeeds → BROADCASTED with tx hash. Future deadline keeps the + // broadcaster out of the cluster-time branch (deadline=0 events take the + // legacy hand-off-to-resolver path; tested separately). evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) - insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()+600) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("solTxSig123", nil) @@ -357,12 +359,13 @@ func TestSVM_BroadcastSuccess_MarksBroadcasted(t *testing.T) { func TestSVM_BroadcastFails_PDAExists_MarksBroadcasted(t *testing.T) { // Broadcast fails, but ExecutedTx PDA exists → another relayer processed it → BROADCASTED. + // Future deadline so the broadcaster goes to broadcast attempt (not cluster check). evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) - insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()+600) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("", fmt.Errorf("tx simulation failed: account already exists")) @@ -484,12 +487,13 @@ func TestSVM_PastLocalDeadline_RPCError_StaysSigned(t *testing.T) { func TestSVM_BroadcastFails_PDACheckFails_StaysSigned(t *testing.T) { // Broadcast fails, PDA check also fails (RPC truly down) → stays SIGNED for retry. + // Future deadline so the broadcaster goes to broadcast attempt (not cluster check). evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) - insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + insertSignedSVMEventWithDeadline(t, db, "ev-1", "solana:mainnet", 0, time.Now().Unix()+600) builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return("", fmt.Errorf("RPC timeout")) From 9dfdfeeadeabccdc20406e94a586a20af9fd8129 Mon Sep 17 00:00:00 2001 From: aman035 Date: Wed, 27 May 2026 12:30:12 +0530 Subject: [PATCH 16/17] fix: nonce handling + refactor --- .../tss/txbroadcaster/broadcaster.go | 73 ++--------- .../tss/txbroadcaster/broadcaster_test.go | 59 ++++----- universalClient/tss/txbroadcaster/evm.go | 25 ++-- universalClient/tss/txbroadcaster/svm.go | 15 +-- universalClient/tss/txflow/nonce.go | 41 ++++++ universalClient/tss/txflow/parse.go | 66 ++++++++++ universalClient/tss/txflow/types.go | 40 ++++++ universalClient/tss/txresolver/evm.go | 117 ++++++------------ universalClient/tss/txresolver/resolver.go | 23 ++-- .../tss/txresolver/resolver_test.go | 17 ++- universalClient/tss/txresolver/svm.go | 39 ++---- 11 files changed, 274 insertions(+), 241 deletions(-) create mode 100644 universalClient/tss/txflow/nonce.go create mode 100644 universalClient/tss/txflow/parse.go create mode 100644 universalClient/tss/txflow/types.go diff --git a/universalClient/tss/txbroadcaster/broadcaster.go b/universalClient/tss/txbroadcaster/broadcaster.go index d4f3bd592..b5b99c4c1 100644 --- a/universalClient/tss/txbroadcaster/broadcaster.go +++ b/universalClient/tss/txbroadcaster/broadcaster.go @@ -2,52 +2,17 @@ package txbroadcaster import ( "context" - "encoding/hex" "encoding/json" - "fmt" - "math/big" "time" "github.com/rs/zerolog" - uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" - utsstypes "github.com/pushchain/push-chain-node/x/utss/types" - "github.com/pushchain/push-chain-node/universalClient/chains" - "github.com/pushchain/push-chain-node/universalClient/chains/common" "github.com/pushchain/push-chain-node/universalClient/store" "github.com/pushchain/push-chain-node/universalClient/tss/eventstore" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) -// --------------------------------------------------------------------------- -// Signed event data types -// --------------------------------------------------------------------------- - -// SigningData holds the signing parameters persisted by sessionManager when marking SIGNED. -type SigningData struct { - Signature string `json:"signature"` // hex-encoded 64/65 byte signature - SigningHash string `json:"signing_hash"` // hex-encoded signing hash - Nonce uint64 `json:"nonce"` - TSSFundMigrationAmount *big.Int `json:"tss_fund_migration_amount,omitempty"` -} - -// SignedOutboundData wraps OutboundCreatedEvent with signing data. -type SignedOutboundData struct { - uexecutortypes.OutboundCreatedEvent - SigningData *SigningData `json:"signing_data,omitempty"` -} - -// SignedFundMigrationData wraps FundMigrationInitiatedEventData with signing data. -type SignedFundMigrationData struct { - utsstypes.FundMigrationInitiatedEventData - SigningData *SigningData `json:"signing_data,omitempty"` -} - -// --------------------------------------------------------------------------- -// Broadcaster -// --------------------------------------------------------------------------- - -// Config holds configuration for the broadcaster. type Config struct { EventStore *eventstore.Store Chains *chains.Chains @@ -56,7 +21,6 @@ type Config struct { GetTSSAddress func(ctx context.Context) (string, error) } -// Broadcaster polls SIGNED events and broadcasts them to external chains. type Broadcaster struct { eventStore *eventstore.Store chains *chains.Chains @@ -65,7 +29,6 @@ type Broadcaster struct { getTSSAddress func(ctx context.Context) (string, error) } -// NewBroadcaster creates a new tx broadcaster. func NewBroadcaster(cfg Config) *Broadcaster { interval := cfg.CheckInterval if interval == 0 { @@ -143,7 +106,7 @@ func (b *Broadcaster) broadcastEvent(ctx context.Context, event *store.Event) { // broadcastOutbound parses outbound event data and delegates to chain-specific broadcast. func (b *Broadcaster) broadcastOutbound(ctx context.Context, event *store.Event) { - var data SignedOutboundData + var data txflow.SignedOutboundData if err := json.Unmarshal(event.EventData, &data); err != nil { b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to parse signed outbound data") return @@ -161,9 +124,9 @@ func (b *Broadcaster) broadcastOutbound(ctx context.Context, event *store.Event) } if b.chains.IsEVMChain(chainID) { - b.broadcastEVM(ctx, event, &data, chainID) + b.broadcastOutboundEVM(ctx, event, &data, chainID) } else { - b.broadcastSVM(ctx, event, &data, chainID) + b.broadcastOutboundSVM(ctx, event, &data, chainID) } } @@ -173,7 +136,7 @@ func (b *Broadcaster) broadcastOutbound(ctx context.Context, event *store.Event) // broadcastFundMigration parses fund migration event data and delegates to chain-specific broadcast. func (b *Broadcaster) broadcastFundMigration(ctx context.Context, event *store.Event) { - var data SignedFundMigrationData + var data txflow.SignedFundMigrationData if err := json.Unmarshal(event.EventData, &data); err != nil { b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to parse fund migration signed data") return @@ -197,25 +160,6 @@ func (b *Broadcaster) broadcastFundMigration(ctx context.Context, event *store.E // Helpers // --------------------------------------------------------------------------- -// decodeSigningData extracts the UnsignedSigningReq and raw signature bytes from SigningData. -func decodeSigningData(sd *SigningData) (*common.UnsignedSigningReq, []byte, error) { - signingHash, err := hex.DecodeString(sd.SigningHash) - if err != nil { - return nil, nil, fmt.Errorf("failed to decode signing hash: %w", err) - } - - signature, err := hex.DecodeString(sd.Signature) - if err != nil { - return nil, nil, fmt.Errorf("failed to decode signature: %w", err) - } - - return &common.UnsignedSigningReq{ - SigningHash: signingHash, - Nonce: sd.Nonce, - TSSFundMigrationAmount: sd.TSSFundMigrationAmount, - }, signature, nil -} - // markBroadcasted updates the event status to BROADCASTED with the given tx hash. func (b *Broadcaster) markBroadcasted(event *store.Event, chainID, txHash string) { caipTxHash := chainID + ":" + txHash @@ -226,6 +170,9 @@ func (b *Broadcaster) markBroadcasted(event *store.Event, chainID, txHash string b.logger.Warn().Err(err).Str("event_id", event.EventID).Msg("failed to update event to BROADCASTED") return } - b.logger.Info().Str("event_id", event.EventID).Str("tx_hash", txHash).Str("chain", chainID). - Msg("marked BROADCASTED") + b.logger.Info(). + Str("event_id", event.EventID). + Str("type", event.Type). + Str("chain", chainID). + Msg("event marked as BROADCASTED") } diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index c908fb35b..d52992e45 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -26,6 +26,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/store" "github.com/pushchain/push-chain-node/universalClient/tss/eventstore" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) type mockTxBuilder struct{ mock.Mock } @@ -120,7 +121,7 @@ func makeSignedOutboundData(t *testing.T, destChain string, nonce uint64) []byte t.Helper() sig := hex.EncodeToString(make([]byte, 64)) hash := hex.EncodeToString(make([]byte, 32)) - data := SignedOutboundData{ + data := txflow.SignedOutboundData{ OutboundCreatedEvent: uexecutortypes.OutboundCreatedEvent{ TxID: "tx-123", UniversalTxId: "utx-456", @@ -128,7 +129,7 @@ func makeSignedOutboundData(t *testing.T, destChain string, nonce uint64) []byte Recipient: "0xRecipient", Amount: "1000000", }, - SigningData: &SigningData{ + SigningData: &txflow.SigningData{ Signature: sig, SigningHash: hash, Nonce: nonce, @@ -160,7 +161,7 @@ func insertSignedSVMEventWithDeadline(t *testing.T, db *gorm.DB, eventID, destCh t.Helper() sig := hex.EncodeToString(make([]byte, 64)) hash := hex.EncodeToString(make([]byte, 32)) - data := SignedOutboundData{ + data := txflow.SignedOutboundData{ OutboundCreatedEvent: uexecutortypes.OutboundCreatedEvent{ TxID: "tx-123", UniversalTxId: "utx-456", @@ -169,7 +170,7 @@ func insertSignedSVMEventWithDeadline(t *testing.T, db *gorm.DB, eventID, destCh Amount: "1000000", SigningDeadline: deadlineUnix, }, - SigningData: &SigningData{ + SigningData: &txflow.SigningData{ Signature: sig, SigningHash: hash, Nonce: nonce, @@ -248,28 +249,9 @@ func TestEVM_BroadcastSuccess_MarksBroadcasted(t *testing.T) { builder.AssertNotCalled(t, "GetNextNonce", mock.Anything, mock.Anything, mock.Anything) } -func TestEVM_BroadcastFails_NonceConsumedOnRecheck_MarksBroadcasted(t *testing.T) { - // Broadcast fails, but nonce check shows it was consumed (race with another node). - evtStore, db := setupTestDB(t) - builder := &mockTxBuilder{} - client := &mockChainClient{builder: builder} - ch := newTestChains(t, "eip155:1", uregistrytypes.VmType_EVM, client) - - insertSignedEvent(t, db, "ev-1", "eip155:1", 5) - - builder.On("BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return("0xfailed", fmt.Errorf("some RPC error")) - builder.On("GetNextNonce", mock.Anything, "0xTSS", true).Return(uint64(6), nil) - - b := newBroadcaster(evtStore, ch, "0xTSS") - b.processSigned(context.Background()) - - ev := getEvent(t, db, "ev-1") - require.Equal(t, store.StatusBroadcasted, ev.Status) -} - -func TestEVM_BroadcastFails_NonceNotConsumed_StaysSigned(t *testing.T) { - // Broadcast fails with no txHash (assembly failure) → stay SIGNED for retry. +func TestEVM_BroadcastAssemblyFails_StaysSigned(t *testing.T) { + // Broadcast returns empty txHash (assembly/encode failure before sending) → + // nonce check is never reached; stay SIGNED for retry. evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} @@ -335,6 +317,27 @@ func TestEVM_GetTSSAddressNil_UsesEmptyAddress(t *testing.T) { builder.AssertCalled(t, "GetNextNonce", mock.Anything, "", true) } +func TestSVM_DeadlineZero_ClusterConfirmsExpiry_MarksBroadcasted(t *testing.T) { + // Legacy event without a signing deadline. `now > 0` enters the deadline + // branch and any fresh cluster time (>> 0) trips the expiry case → + // BROADCASTED("") for the resolver to REVERT. + evtStore, db := setupTestDB(t) + builder := &mockTxBuilder{} + client := &mockChainClient{builder: builder} + ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client) + + insertSignedEvent(t, db, "ev-1", "solana:mainnet", 0) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix(), nil) + + b := newBroadcaster(evtStore, ch, "") + b.processSigned(context.Background()) + + ev := getEvent(t, db, "ev-1") + require.Equal(t, store.StatusBroadcasted, ev.Status) + require.Equal(t, "solana:mainnet:", ev.BroadcastedTxHash) + builder.AssertNotCalled(t, "BroadcastOutboundSigningRequest", mock.Anything, mock.Anything, mock.Anything, mock.Anything) +} + func TestSVM_BroadcastSuccess_MarksBroadcasted(t *testing.T) { // Broadcast succeeds → BROADCASTED with tx hash. Future deadline keeps the // broadcaster out of the cluster-time branch (deadline=0 events take the @@ -585,7 +588,7 @@ func makeSignedFundMigrationDataWithTransfer(t *testing.T, chainID string, nonce t.Helper() sig := hex.EncodeToString(make([]byte, 65)) hash := hex.EncodeToString(make([]byte, 32)) - data := SignedFundMigrationData{ + data := txflow.SignedFundMigrationData{ FundMigrationInitiatedEventData: utsstypes.FundMigrationInitiatedEventData{ MigrationID: 1, OldKeyID: "old-key", @@ -597,7 +600,7 @@ func makeSignedFundMigrationDataWithTransfer(t *testing.T, chainID string, nonce GasLimit: 21100, L1GasFee: "150", }, - SigningData: &SigningData{ + SigningData: &txflow.SigningData{ Signature: sig, SigningHash: hash, Nonce: nonce, diff --git a/universalClient/tss/txbroadcaster/evm.go b/universalClient/tss/txbroadcaster/evm.go index ed49dcc0a..15da0cdb7 100644 --- a/universalClient/tss/txbroadcaster/evm.go +++ b/universalClient/tss/txbroadcaster/evm.go @@ -7,9 +7,10 @@ import ( "github.com/pushchain/push-chain-node/universalClient/chains/common" "github.com/pushchain/push-chain-node/universalClient/store" "github.com/pushchain/push-chain-node/universalClient/tss/coordinator" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) -// broadcastEVM broadcasts a signed EVM outbound transaction. +// broadcastOutboundEVM broadcasts a signed EVM outbound transaction. // // All validators produce the same signed tx (deterministic TSS output), so the // tx hash is known before broadcasting (computed from the assembled signed tx). @@ -20,7 +21,7 @@ import ( // 3. Error → check finalized nonce on chain: // - nonce consumed (tx landed) → BROADCASTED with tx hash // - nonce NOT consumed → keep SIGNED, retry next tick -func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data *SignedOutboundData, chainID string) { +func (b *Broadcaster) broadcastOutboundEVM(ctx context.Context, event *store.Event, data *txflow.SignedOutboundData, chainID string) { log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() client, err := b.chains.GetClient(chainID) @@ -34,7 +35,7 @@ func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data return } - signingReq, signature, err := decodeSigningData(data.SigningData) + signingReq, signature, err := txflow.DecodeSigningData(data.SigningData) if err != nil { log.Warn().Err(err).Msg("failed to decode signing data") return @@ -70,7 +71,7 @@ func (b *Broadcaster) broadcastEVM(ctx context.Context, event *store.Event, data // broadcastFundMigrationEVM broadcasts a signed EVM fund migration transaction. // Same nonce-consumed recovery pattern as outbound, but uses old TSS address for nonce check. -func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *store.Event, data *SignedFundMigrationData, chainID string) { +func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *store.Event, data *txflow.SignedFundMigrationData, chainID string) { log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() oldTSSAddr, err := coordinator.DeriveEVMAddressFromPubkey(data.OldTssPubkey) @@ -95,7 +96,7 @@ func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *stor return } - signingReq, signature, err := decodeSigningData(data.SigningData) + signingReq, signature, err := txflow.DecodeSigningData(data.SigningData) if err != nil { log.Warn().Err(err).Msg("failed to decode signing data") return @@ -131,8 +132,9 @@ func (b *Broadcaster) broadcastFundMigrationEVM(ctx context.Context, event *stor b.checkNonceAndMarkBroadcasted(ctx, event, builder, chainID, txHash, oldTSSAddr, data.SigningData.Nonce, broadcastErr) } -// checkNonceAndMarkBroadcasted checks if a nonce has been consumed on-chain despite broadcast error. -// If consumed, the tx landed and we mark BROADCASTED. Otherwise keep SIGNED for retry. +// checkNonceAndMarkBroadcasted checks if a nonce has been consumed on-chain +// despite a broadcast error. If consumed, the tx landed (possibly via another +// node) and we mark BROADCASTED. Otherwise keep SIGNED for retry. func (b *Broadcaster) checkNonceAndMarkBroadcasted( ctx context.Context, event *store.Event, @@ -143,17 +145,14 @@ func (b *Broadcaster) checkNonceAndMarkBroadcasted( ) { log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() - finalizedNonce, err := builder.GetNextNonce(ctx, signerAddr, true) - if err == nil && eventNonce < finalizedNonce { - // Nonce consumed — tx is on chain. Mark BROADCASTED so the resolver can verify it. - log.Info().Err(broadcastErr).Str("tx_hash", txHash). + verdict, finalizedNonce := txflow.CheckNonce(ctx, builder, signerAddr, eventNonce) + if verdict == txflow.NonceConsumed { + log.Debug().Err(broadcastErr).Str("tx_hash", txHash). Uint64("event_nonce", eventNonce).Uint64("finalized_nonce", finalizedNonce). Msg("broadcast failed but tx already on chain, marking BROADCASTED") b.markBroadcasted(event, chainID, txHash) return } - // Nonce not consumed — transient error (RPC down, gas issues, etc.). - // Keep as SIGNED and retry next tick. log.Debug().Err(broadcastErr).Msg("broadcast failed, will retry next tick") } diff --git a/universalClient/tss/txbroadcaster/svm.go b/universalClient/tss/txbroadcaster/svm.go index 0924e7fb1..5fa7dce5d 100644 --- a/universalClient/tss/txbroadcaster/svm.go +++ b/universalClient/tss/txbroadcaster/svm.go @@ -5,9 +5,10 @@ import ( "time" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) -// broadcastSVM broadcasts a signed Solana transaction and moves the event to +// broadcastOutboundSVM broadcasts a signed Solana transaction and moves the event to // its next state. // // Three phases, top to bottom: @@ -30,7 +31,7 @@ import ( // - BROADCASTED(real-hash) → broadcast succeeded // - BROADCASTED("") → peer landed it, or cluster confirmed expiry // - stay SIGNED → retry next tick -func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data *SignedOutboundData, chainID string) { +func (b *Broadcaster) broadcastOutboundSVM(ctx context.Context, event *store.Event, data *txflow.SignedOutboundData, chainID string) { log := b.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() client, err := b.chains.GetClient(chainID) @@ -43,7 +44,7 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data log.Warn().Err(err).Msg("failed to get tx builder") return } - signingReq, signature, err := decodeSigningData(data.SigningData) + signingReq, signature, err := txflow.DecodeSigningData(data.SigningData) if err != nil { log.Warn().Err(err).Msg("failed to decode signing data") return @@ -64,11 +65,11 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data dlog.Debug().Err(checkErr).Msg("SVM cluster check failed at deadline, retry next tick") return case executed: - dlog.Info().Msg("SVM tx executed by peer past local deadline, marking BROADCASTED") + dlog.Debug().Msg("SVM tx executed by peer past local deadline, marking BROADCASTED") b.markBroadcasted(event, chainID, "") return case clusterTime > deadline: - dlog.Warn().Msg("SVM deadline cluster-confirmed expired, marking BROADCASTED for resolver REVERT") + dlog.Debug().Msg("SVM deadline cluster-confirmed expired, marking BROADCASTED for resolver REVERT") b.markBroadcasted(event, chainID, "") return } @@ -84,11 +85,11 @@ func (b *Broadcaster) broadcastSVM(ctx context.Context, event *store.Event, data // Race: a peer may have landed the same signed tx in the meantime. if executed, _, _ := builder.IsAlreadyExecuted(ctx, txID); executed { - log.Info().Err(broadcastErr).Msg("SVM broadcast failed but tx executed on chain (race), marking BROADCASTED") + log.Debug().Err(broadcastErr).Msg("SVM broadcast failed but tx executed on chain (race), marking BROADCASTED") b.markBroadcasted(event, chainID, "") return } - log.Info().Err(broadcastErr).Int64("signing_deadline", deadline). + log.Debug().Err(broadcastErr).Int64("signing_deadline", deadline). Msg("SVM broadcast failed, staying SIGNED for next tick") } diff --git a/universalClient/tss/txflow/nonce.go b/universalClient/tss/txflow/nonce.go new file mode 100644 index 000000000..6a99c3320 --- /dev/null +++ b/universalClient/tss/txflow/nonce.go @@ -0,0 +1,41 @@ +package txflow + +import ( + "context" + + "github.com/pushchain/push-chain-node/universalClient/chains/common" +) + +// NonceVerdict captures the outcome of comparing the signed nonce against +// the chain's finalized nonce. EVM-only — SVM does not use nonces this way. +type NonceVerdict int + +const ( + // NonceUnknown means the RPC failed and the caller should defer the decision. + NonceUnknown NonceVerdict = iota + // NonceConsumed means the chain advanced past the signed nonce. Some other + // tx took that slot; our signed tx can never land. + NonceConsumed + // NonceAvailable means the chain hasn't consumed the signed nonce yet. The + // tx may still be in mempool, or was dropped — a re-broadcast may land it. + NonceAvailable +) + +// CheckNonce compares signedNonce against the chain's finalized nonce for +// `signer`. Used by: +// - broadcaster (post-broadcast-error path) to detect "the tx already +// landed via another node despite our RPC error" +// - resolver (tx-not-found path) to distinguish dead tx (REVERT) from +// mempool-drop (rewind to SIGNED). +// +// The returned finalizedNonce is for logging / observability. +func CheckNonce(ctx context.Context, builder common.TxBuilder, signer string, signedNonce uint64) (NonceVerdict, uint64) { + finalizedNonce, err := builder.GetNextNonce(ctx, signer, true) + if err != nil { + return NonceUnknown, 0 + } + if signedNonce < finalizedNonce { + return NonceConsumed, finalizedNonce + } + return NonceAvailable, finalizedNonce +} diff --git a/universalClient/tss/txflow/parse.go b/universalClient/tss/txflow/parse.go new file mode 100644 index 000000000..6bb71857e --- /dev/null +++ b/universalClient/tss/txflow/parse.go @@ -0,0 +1,66 @@ +package txflow + +import ( + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/pushchain/push-chain-node/universalClient/chains/common" + "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/tss/coordinator" +) + +// DecodeSigningData converts the persisted hex-encoded signature + signing +// hash into the byte forms the chain-specific tx builders consume. +func DecodeSigningData(sd *SigningData) (*common.UnsignedSigningReq, []byte, error) { + signingHash, err := hex.DecodeString(sd.SigningHash) + if err != nil { + return nil, nil, fmt.Errorf("failed to decode signing hash: %w", err) + } + signature, err := hex.DecodeString(sd.Signature) + if err != nil { + return nil, nil, fmt.Errorf("failed to decode signature: %w", err) + } + return &common.UnsignedSigningReq{ + SigningHash: signingHash, + Nonce: sd.Nonce, + TSSFundMigrationAmount: sd.TSSFundMigrationAmount, + }, signature, nil +} + +// ReadSignedNonce extracts the signed nonce from any signed outbound event +// payload. Returns ok=false when the payload is unparseable or signing data +// is missing — caller defers in that case. +func ReadSignedNonce(event *store.Event) (uint64, bool) { + var data SignedOutboundData + if err := json.Unmarshal(event.EventData, &data); err != nil || data.SigningData == nil { + return 0, false + } + return data.SigningData.Nonce, true +} + +// ReadSigningDeadline extracts the chain-emitted signing deadline from a +// signed outbound event payload. Returns 0 if the event is unparseable or +// the deadline was never set (legacy events). +func ReadSigningDeadline(event *store.Event) int64 { + var data SignedOutboundData + if err := json.Unmarshal(event.EventData, &data); err != nil { + return 0 + } + return data.SigningDeadline +} + +// ReadFundMigrationSigner derives the sender EVM address (old TSS) and reads +// the signed nonce from a fund migration event payload. Returns ok=false on +// missing/invalid fields — caller defers in that case. +func ReadFundMigrationSigner(event *store.Event) (signer string, nonce uint64, ok bool) { + var data SignedFundMigrationData + if err := json.Unmarshal(event.EventData, &data); err != nil || data.SigningData == nil || data.OldTssPubkey == "" { + return "", 0, false + } + addr, err := coordinator.DeriveEVMAddressFromPubkey(data.OldTssPubkey) + if err != nil { + return "", 0, false + } + return addr, data.SigningData.Nonce, true +} diff --git a/universalClient/tss/txflow/types.go b/universalClient/tss/txflow/types.go new file mode 100644 index 000000000..c51a31f81 --- /dev/null +++ b/universalClient/tss/txflow/types.go @@ -0,0 +1,40 @@ +// Package txflow holds the shared types and helpers used by both the +// transaction broadcaster and the resolver. Each module owns its own +// lifecycle (broadcaster pushes SIGNED→BROADCASTED, resolver pulls +// BROADCASTED→terminal), but they read the same persisted event payloads +// and apply the same rules (signed-vs-finalized nonce comparison, signing +// data decoding). Lifting those shared concerns here gives one source of +// truth without conflating the two modules' responsibilities. +package txflow + +import ( + "math/big" + + uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" + utsstypes "github.com/pushchain/push-chain-node/x/utss/types" +) + +// SigningData holds the signing parameters persisted by sessionManager when +// marking an event SIGNED. Both broadcaster and resolver read these fields +// — broadcaster to assemble + send the tx, resolver to compare the signed +// nonce against the chain's finalized nonce. +type SigningData struct { + Signature string `json:"signature"` // hex-encoded 64/65 byte signature + SigningHash string `json:"signing_hash"` // hex-encoded signing hash + Nonce uint64 `json:"nonce"` + TSSFundMigrationAmount *big.Int `json:"tss_fund_migration_amount,omitempty"` +} + +// SignedOutboundData wraps OutboundCreatedEvent with the signing data the +// broadcaster needs to assemble the destination-chain tx. +type SignedOutboundData struct { + uexecutortypes.OutboundCreatedEvent + SigningData *SigningData `json:"signing_data,omitempty"` +} + +// SignedFundMigrationData wraps FundMigrationInitiatedEventData with the +// signing data needed for the migration sweep tx. +type SignedFundMigrationData struct { + utsstypes.FundMigrationInitiatedEventData + SigningData *SigningData `json:"signing_data,omitempty"` +} diff --git a/universalClient/tss/txresolver/evm.go b/universalClient/tss/txresolver/evm.go index 465a36356..ea9778974 100644 --- a/universalClient/tss/txresolver/evm.go +++ b/universalClient/tss/txresolver/evm.go @@ -2,11 +2,9 @@ package txresolver import ( "context" - "encoding/json" - "github.com/pushchain/push-chain-node/universalClient/chains/common" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/tss/coordinator" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) // Decision flow for EVM-broadcasted events (outbound and fund migration both @@ -26,62 +24,18 @@ import ( // flows differ only in (a) which vote function records success/failure and // (b) where the signer address comes from — current TSS for outbound, OLD TSS // (derived from the event's old pubkey) for fund migration. - -// signedNonceEnvelope is the slice of any EVM-signed event payload the -// resolver consults on tx-not-found. OldTssPubkey is set only on fund -// migration events; it identifies the sender (and thus the address whose -// nonce gates the decision). -type signedNonceEnvelope struct { - OldTssPubkey string `json:"old_tss_pubkey,omitempty"` - SigningData *struct { - Nonce uint64 `json:"nonce"` - } `json:"signing_data,omitempty"` -} - -func readSignedNonce(event *store.Event) (uint64, bool) { - var env signedNonceEnvelope - if err := json.Unmarshal(event.EventData, &env); err != nil || env.SigningData == nil { - return 0, false - } - return env.SigningData.Nonce, true -} - -func readFundMigrationSigner(event *store.Event) (signer string, nonce uint64, ok bool) { - var env signedNonceEnvelope - if err := json.Unmarshal(event.EventData, &env); err != nil || env.SigningData == nil || env.OldTssPubkey == "" { - return "", 0, false - } - addr, err := coordinator.DeriveEVMAddressFromPubkey(env.OldTssPubkey) - if err != nil { - return "", 0, false - } - return addr, env.SigningData.Nonce, true -} - -// nonceVerdict captures the outcome of comparing the signed nonce against the -// chain's finalized nonce when a tx hash isn't on chain. -type nonceVerdict int - -const ( - nonceUnknown nonceVerdict = iota // RPC failed; defer the decision - nonceConsumed // chain advanced past the signed nonce → tx is dead - nonceAvailable // chain hasn't consumed the signed nonce yet → re-broadcast may still land -) - -func checkNonce(ctx context.Context, builder common.TxBuilder, signer string, signedNonce uint64) (nonceVerdict, uint64) { - finalizedNonce, err := builder.GetNextNonce(ctx, signer, true) - if err != nil { - return nonceUnknown, 0 - } - if signedNonce < finalizedNonce { - return nonceConsumed, finalizedNonce - } - return nonceAvailable, finalizedNonce -} +// +// Shared types (SignedOutboundData / SigningData) and helpers (DecodeSigningData, +// ReadSignedNonce, ReadFundMigrationSigner, CheckNonce, NonceVerdict) live in +// tss/txflow so the broadcaster applies the exact same rules. // resolveOutboundEVM resolves a BROADCASTED outbound on an EVM chain. func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string) { - log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + log := r.logger.With(). + Str("event_id", event.EventID). + Str("type", event.Type). + Str("chain", chainID). + Str("tx_hash", rawTxHash).Logger() txID, utxID, err := extractOutboundIDs(event) if err != nil { @@ -106,9 +60,10 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c return } if status == 0 { - gasFeeUsed := "0" - if fee, fErr := builder.GetGasFeeUsed(ctx, rawTxHash); fErr == nil { - gasFeeUsed = fee + gasFeeUsed, fErr := builder.GetGasFeeUsed(ctx, rawTxHash) + if fErr != nil { + log.Debug().Err(fErr).Msg("failed to fetch gas fee for reverted tx, will retry next tick") + return } _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, rawTxHash, blockHeight, gasFeeUsed, "tx execution reverted on destination chain") @@ -118,7 +73,7 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c log.Warn().Err(uerr).Msg("failed to mark event COMPLETED") return } - log.Info().Str("tx_hash", rawTxHash).Msg("outbound EVM tx marked COMPLETED") + log.Info().Msg("event marked as COMPLETED") return } @@ -126,16 +81,16 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c if !ok { return } - verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + verdict, finalizedNonce := txflow.CheckNonce(ctx, builder, signer, signedNonce) nlog := log.With().Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() switch verdict { - case nonceUnknown: + case txflow.NonceUnknown: nlog.Debug().Msg("could not fetch finalized nonce, will retry next tick") - case nonceConsumed: - nlog.Info().Msg("EVM outbound tx not found and nonce already finalized → REVERT") + case txflow.NonceConsumed: + nlog.Debug().Msg("EVM outbound tx not found and nonce already finalized → REVERT") _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", - "tx not found on destination chain and nonce consumed by another tx") - case nonceAvailable: + "tx not executed on destination chain") + case txflow.NonceAvailable: r.rewindToSigned(event, chainID, signedNonce, finalizedNonce) } } @@ -145,7 +100,11 @@ func (r *Resolver) resolveOutboundEVM(ctx context.Context, event *store.Event, c // success/failure path uses the fund-migration voting helper which both votes // and marks the event in a single step. func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Event, chainID, rawTxHash string, migrationID uint64) { - log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID).Logger() + log := r.logger.With(). + Str("event_id", event.EventID). + Str("type", event.Type). + Str("chain", chainID). + Str("tx_hash", rawTxHash).Logger() builder, err := r.getBuilder(chainID) if err != nil { @@ -167,20 +126,20 @@ func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Eve return } - signer, signedNonce, ok := readFundMigrationSigner(event) + signer, signedNonce, ok := txflow.ReadFundMigrationSigner(event) if !ok { log.Warn().Msg("fund migration tx not found and signer info unavailable, staying BROADCASTED") return } - verdict, finalizedNonce := checkNonce(ctx, builder, signer, signedNonce) + verdict, finalizedNonce := txflow.CheckNonce(ctx, builder, signer, signedNonce) nlog := log.With().Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() switch verdict { - case nonceUnknown: + case txflow.NonceUnknown: nlog.Debug().Msg("could not fetch finalized nonce, will retry next tick") - case nonceConsumed: - nlog.Info().Msg("EVM fund migration tx not found and nonce already finalized → REVERT") + case txflow.NonceConsumed: + nlog.Debug().Msg("EVM fund migration tx not found and nonce already finalized → REVERT") r.voteFundMigrationAndMark(ctx, event, migrationID, "", false) - case nonceAvailable: + case txflow.NonceAvailable: r.rewindToSigned(event, chainID, signedNonce, finalizedNonce) } } @@ -191,7 +150,7 @@ func (r *Resolver) resolveFundMigrationEVM(ctx context.Context, event *store.Eve func (r *Resolver) outboundSigner(ctx context.Context, event *store.Event) (string, uint64, bool) { log := r.logger.With().Str("event_id", event.EventID).Logger() - signedNonce, ok := readSignedNonce(event) + signedNonce, ok := txflow.ReadSignedNonce(event) if !ok { log.Warn().Msg("EVM tx not found and signed nonce unavailable, staying BROADCASTED") return "", 0, false @@ -212,12 +171,16 @@ func (r *Resolver) outboundSigner(ctx context.Context, event *store.Event) (stri // will re-broadcast on the next tick. Used when the EVM tx hash isn't visible // on chain but the signed nonce is still available — covers mempool drops. func (r *Resolver) rewindToSigned(event *store.Event, chainID string, signedNonce, finalizedNonce uint64) { - log := r.logger.With().Str("event_id", event.EventID).Str("chain", chainID). - Uint64("signed_nonce", signedNonce).Uint64("finalized_nonce", finalizedNonce).Logger() + log := r.logger.With(). + Str("event_id", event.EventID). + Str("type", event.Type). + Str("chain", chainID). + Uint64("signed_nonce", signedNonce). + Uint64("finalized_nonce", finalizedNonce).Logger() if err := r.eventStore.Update(event.EventID, map[string]any{"status": store.StatusSigned}); err != nil { log.Warn().Err(err).Msg("failed to rewind event to SIGNED for re-broadcast") return } - log.Info().Msg("EVM tx not found and nonce un-consumed, rewound to SIGNED for re-broadcast") + log.Debug().Msg("event marked as SIGNED") } diff --git a/universalClient/tss/txresolver/resolver.go b/universalClient/tss/txresolver/resolver.go index 1795f95aa..cbf2e628d 100644 --- a/universalClient/tss/txresolver/resolver.go +++ b/universalClient/tss/txresolver/resolver.go @@ -19,19 +19,12 @@ import ( "github.com/pushchain/push-chain-node/universalClient/tss/eventstore" ) -// --------------------------------------------------------------------------- -// Resolver -// --------------------------------------------------------------------------- - -// Config holds configuration for the tx resolver. type Config struct { EventStore *eventstore.Store Chains *chains.Chains PushSigner *pushsigner.Signer CheckInterval time.Duration Logger zerolog.Logger - // GetTSSAddress returns the current TSS ECDSA address — used by the EVM - // resolver path to compare on-chain finalized nonce against the signed nonce GetTSSAddress func(ctx context.Context) (string, error) } @@ -45,7 +38,6 @@ type Resolver struct { getTSSAddress func(ctx context.Context) (string, error) } -// NewResolver creates a new tx resolver. func NewResolver(cfg Config) *Resolver { interval := cfg.CheckInterval if interval == 0 { @@ -233,8 +225,11 @@ func (r *Resolver) voteOutboundFailureAndMarkReverted(ctx context.Context, event return fmt.Errorf("failed to mark event %s as reverted: %w", event.EventID, err) } r.logger.Info(). - Str("event_id", event.EventID).Str("tx_id", txID). - Str("error_msg", errorMsg).Msg("voted outbound failure and marked REVERTED") + Str("event_id", event.EventID). + Str("type", event.Type). + Str("vote_tx_hash", voteTxHash). + Str("error_msg", errorMsg). + Msg("event marked as REVERTED") return nil } @@ -263,7 +258,9 @@ func (r *Resolver) voteFundMigrationAndMark(ctx context.Context, event *store.Ev } r.logger.Info(). - Str("event_id", event.EventID).Uint64("migration_id", migrationID). - Str("tx_hash", txHash).Bool("success", success).Str("status", newStatus). - Msg("voted fund migration and updated status") + Str("event_id", event.EventID). + Str("type", event.Type). + Uint64("migration_id", migrationID). + Str("vote_tx_hash", voteTxHash). + Msg("event marked as " + newStatus) } diff --git a/universalClient/tss/txresolver/resolver_test.go b/universalClient/tss/txresolver/resolver_test.go index 84e62bd77..1b741b59b 100644 --- a/universalClient/tss/txresolver/resolver_test.go +++ b/universalClient/tss/txresolver/resolver_test.go @@ -282,10 +282,11 @@ func TestSVM_PDAExists_MarksCompleted(t *testing.T) { require.Equal(t, store.StatusCompleted, updated.Status) } -func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) { - // PDA not found → vote failure → REVERTED. - // Note: orphaned StoredIxData PDAs are reclaimed by the periodic - // RentReclaimer in svm/rent_reclaimer.go, not from this hot path. +func TestSVM_PDAAbsent_DeadlineZero_ClusterFresh_Reverts(t *testing.T) { + // Legacy event with no deadline (=0). PDA absent + fresh cluster time + // (>> 0) satisfies `clusterTime > deadline + slack` → reaches REVERT. + // No PushSigner → vote returns nil, status stays BROADCASTED. The point + // is that the resolver reaches the vote path (not defers). evtStore, db := setupTestDB(t) builder := &mockTxBuilder{} client := &mockChainClient{builder: builder} @@ -294,17 +295,15 @@ func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) { eventData := makeOutboundEventData("tx-123", "utx-456", "solana:mainnet") insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:", eventData) - // Event data has no SigningDeadline → legacy eager-revert path; no cluster check. - builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, int64(0), nil) + builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, time.Now().Unix(), nil) - // No PushSigner — voteFailure logs a warning and returns nil without marking REVERTED. resolver := newResolver(evtStore, ch) ev := getEvent(t, db, "ev-1") resolver.resolveSVM(context.Background(), &ev, "solana:mainnet") updated := getEvent(t, db, "ev-1") - require.Equal(t, store.StatusBroadcasted, updated.Status) - builder.AssertExpectations(t) + require.Equal(t, store.StatusBroadcasted, updated.Status) // no PushSigner → vote skipped + builder.AssertCalled(t, "IsAlreadyExecuted", mock.Anything, "tx-123") } func TestSVM_PDACheckFails_StaysBroadcasted(t *testing.T) { diff --git a/universalClient/tss/txresolver/svm.go b/universalClient/tss/txresolver/svm.go index c4e636a7b..7ad38dba0 100644 --- a/universalClient/tss/txresolver/svm.go +++ b/universalClient/tss/txresolver/svm.go @@ -2,16 +2,16 @@ package txresolver import ( "context" - "encoding/json" "time" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/tss/txflow" ) // svmRevertSlackSeconds is the buffer past the signed deadline before the // resolver finalizes REVERT. Gives an in-flight tx that's already confirmed // time to reach `finalized` before we vote against it. -const svmRevertSlackSeconds int64 = 60 +const svmRevertSlackSeconds int64 = 30 // svmClusterStaleSeconds is how far the latest finalized block's timestamp // can lag wall-clock before the cluster is treated as halted or stalled — @@ -19,24 +19,6 @@ const svmRevertSlackSeconds int64 = 60 // txs, so we defer REVERT. const svmClusterStaleSeconds int64 = 120 -// svmEventEnvelope is the slice of the persisted outbound event the resolver -// needs to make a REVERT decision: just the chain-emitted signing deadline. -type svmEventEnvelope struct { - SigningDeadline int64 `json:"signing_deadline,omitempty"` -} - -// extractSVMDeadline returns the unix-second deadline emitted by Push chain on -// the OutboundCreatedEvent. Zero means the destination chain didn't configure -// a deadline window — caller falls back to the pre-deadline eager-revert -// behavior. -func extractSVMDeadline(event *store.Event) int64 { - var env svmEventEnvelope - if err := json.Unmarshal(event.EventData, &env); err != nil { - return 0 - } - return env.SigningDeadline -} - // resolveSVM checks the on-chain ExecutedTx PDA and moves the event to COMPLETED or REVERTED. // // The REVERT decision is gated on the cluster's own clock (latest finalized @@ -51,11 +33,11 @@ func extractSVMDeadline(event *store.Event) int64 { // - PDA absent + cluster stale (>120s old) → stay BROADCASTED, retry. // - PDA absent + cluster says still in window → stay BROADCASTED, retry. // - PDA absent + cluster confirms past deadline → REVERT. -// -// Legacy events (deadline = 0) preserve the pre-deadline eager-revert path — -// REVERT as soon as PDA is absent, no cluster check needed. func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID string) { - log := r.logger.With().Str("event_id", event.EventID).Str("chain_id", chainID).Logger() + log := r.logger.With(). + Str("event_id", event.EventID). + Str("type", event.Type). + Str("chain_id", chainID).Logger() txID, utxID, err := extractOutboundIDs(event) if err != nil { @@ -86,18 +68,13 @@ func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID s log.Warn().Err(err).Msg("failed to mark SVM event COMPLETED") return } - log.Info().Msg("SVM ExecutedTx PDA found, marked COMPLETED") + log.Info().Msg("event marked as COMPLETED") return } // PDA absent. Decide REVERT using the cluster's own clock so we don't // false-revert during halt/stall or host clock skew. - deadline := extractSVMDeadline(event) - if deadline == 0 { - // Legacy event: no deadline, fall back to eager revert. - _ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", "tx not executed on destination chain") - return - } + deadline := txflow.ReadSigningDeadline(event) dlog := log.With().Int64("signing_deadline", deadline).Int64("cluster_block_time", clusterTime).Logger() switch { From c3635e5a683b27cdf76ab7786027b51c4b4d6834 Mon Sep 17 00:00:00 2001 From: aman035 Date: Wed, 27 May 2026 15:08:44 +0530 Subject: [PATCH 17/17] route internal messages via sessionManager --- universalClient/tss/dkls/keygen.go | 9 ------- universalClient/tss/dkls/keyrefresh.go | 8 ------- universalClient/tss/dkls/quorumchange.go | 7 ------ universalClient/tss/dkls/sign.go | 7 ------ .../tss/sessionmanager/sessionmanager.go | 24 ++++++------------- 5 files changed, 7 insertions(+), 48 deletions(-) diff --git a/universalClient/tss/dkls/keygen.go b/universalClient/tss/dkls/keygen.go index 9bf7b2516..476d813f2 100644 --- a/universalClient/tss/dkls/keygen.go +++ b/universalClient/tss/dkls/keygen.go @@ -91,14 +91,6 @@ func (s *keygenSession) Step() ([]Message, bool, error) { break } - // If receiver is self, queue locally for next step - if receiver == s.partyID { - if err := s.InputMessage(msgData); err != nil { - return nil, false, fmt.Errorf("failed to queue local message: %w", err) - } - continue - } - messages = append(messages, Message{ Receiver: receiver, Data: msgData, @@ -109,7 +101,6 @@ func (s *keygenSession) Step() ([]Message, bool, error) { return messages, false, nil } -// InputMessage processes an incoming protocol message. func (s *keygenSession) InputMessage(data []byte) error { buf := make([]byte, len(data)) copy(buf, data) diff --git a/universalClient/tss/dkls/keyrefresh.go b/universalClient/tss/dkls/keyrefresh.go index 615973285..fb87bffcc 100644 --- a/universalClient/tss/dkls/keyrefresh.go +++ b/universalClient/tss/dkls/keyrefresh.go @@ -100,13 +100,6 @@ func (s *keyrefreshSession) Step() ([]Message, bool, error) { break } - if receiver == s.partyID { - if err := s.InputMessage(msgData); err != nil { - return nil, false, fmt.Errorf("failed to queue local message: %w", err) - } - continue - } - messages = append(messages, Message{ Receiver: receiver, Data: msgData, @@ -117,7 +110,6 @@ func (s *keyrefreshSession) Step() ([]Message, bool, error) { return messages, false, nil } -// InputMessage processes an incoming protocol message. func (s *keyrefreshSession) InputMessage(data []byte) error { buf := make([]byte, len(data)) copy(buf, data) diff --git a/universalClient/tss/dkls/quorumchange.go b/universalClient/tss/dkls/quorumchange.go index 90f60537f..c1048d1ef 100644 --- a/universalClient/tss/dkls/quorumchange.go +++ b/universalClient/tss/dkls/quorumchange.go @@ -118,13 +118,6 @@ func (s *quorumchangeSession) Step() ([]Message, bool, error) { break } - if receiver == s.partyID { - if err := s.InputMessage(msgData); err != nil { - return nil, false, fmt.Errorf("failed to queue local message: %w", err) - } - continue - } - messages = append(messages, Message{ Receiver: receiver, Data: msgData, diff --git a/universalClient/tss/dkls/sign.go b/universalClient/tss/dkls/sign.go index d706d6b40..4a656f4c9 100644 --- a/universalClient/tss/dkls/sign.go +++ b/universalClient/tss/dkls/sign.go @@ -125,13 +125,6 @@ func (s *signSession) Step() ([]Message, bool, error) { break } - if receiver == s.partyID { - if err := s.InputMessage(msgData); err != nil { - return nil, false, fmt.Errorf("failed to queue local message: %w", err) - } - continue - } - messages = append(messages, Message{ Receiver: receiver, Data: msgData, diff --git a/universalClient/tss/sessionmanager/sessionmanager.go b/universalClient/tss/sessionmanager/sessionmanager.go index 00c9f7d7b..f93aeb895 100644 --- a/universalClient/tss/sessionmanager/sessionmanager.go +++ b/universalClient/tss/sessionmanager/sessionmanager.go @@ -279,35 +279,27 @@ func (sm *SessionManager) processSessionStep(ctx context.Context, eventID string return fmt.Errorf("session for event %s does not exist", eventID) } - session := state.session - - // Step the session (serialize to prevent concurrent access - DKLS may not be thread-safe) state.stepMu.Lock() - messages, finished, err := session.Step() + messages, finished, err := state.session.Step() state.stepMu.Unlock() if err != nil { return fmt.Errorf("failed to step session %s: %w", eventID, err) } - // Send output messages for _, dklsMsg := range messages { - // Find peerID for receiver partyID peerID, err := sm.coordinator.GetPeerIDFromPartyID(ctx, dklsMsg.Receiver) if err != nil { - sm.logger.Warn(). - Err(err). + sm.logger.Warn().Err(err). Str("receiver_party_id", dklsMsg.Receiver). Msg("failed to get peerID for receiver") continue } - // Create coordinator message coordMsg := coordinator.Message{ - Type: "step", - EventID: eventID, - Payload: dklsMsg.Data, - Participants: nil, // Participants not needed for step messages + Type: "step", + EventID: eventID, + Payload: dklsMsg.Data, } msgBytes, err := json.Marshal(coordMsg) if err != nil { @@ -315,10 +307,9 @@ func (sm *SessionManager) processSessionStep(ctx context.Context, eventID string continue } - // Send message if err := sm.send(ctx, peerID, msgBytes); err != nil { - sm.logger.Warn(). - Err(err). + sm.logger.Warn().Err(err). + Str("event_id", eventID). Str("receiver", dklsMsg.Receiver). Str("peer_id", peerID). Msg("failed to send step message") @@ -331,7 +322,6 @@ func (sm *SessionManager) processSessionStep(ctx context.Context, eventID string Msg("sent step message") } - // If finished, handle result if finished { return sm.handleSessionFinished(ctx, eventID, state) }