diff --git a/buf.lock b/buf.lock new file mode 100644 index 00000000..a7653dd2 --- /dev/null +++ b/buf.lock @@ -0,0 +1,6 @@ +# Generated by buf. DO NOT EDIT. +version: v2 +deps: + - name: buf.build/googleapis/googleapis + commit: 72c8614f3bd0466ea67931ef2c43d608 + digest: b5:13efeea24e633fd45327390bdee941207a8727e96cf01affb84c1e4100fd8f48a42bbd508df11930cd2884629bafad685df1ac3111bc78cdaefcd38c9371c6b1 diff --git a/migrations/025_attestation_liveness_by_entity_epoch_head.down.sql b/migrations/025_attestation_liveness_by_entity_epoch_head.down.sql new file mode 100644 index 00000000..b2cdbbfd --- /dev/null +++ b/migrations/025_attestation_liveness_by_entity_epoch_head.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head ON CLUSTER '{cluster}'; +DROP TABLE IF EXISTS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head_local ON CLUSTER '{cluster}'; diff --git a/migrations/025_attestation_liveness_by_entity_epoch_head.up.sql b/migrations/025_attestation_liveness_by_entity_epoch_head.up.sql new file mode 100644 index 00000000..5152b658 --- /dev/null +++ b/migrations/025_attestation_liveness_by_entity_epoch_head.up.sql @@ -0,0 +1,36 @@ +-- Create local table with ReplicatedReplacingMergeTree +CREATE TABLE `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head_local ON CLUSTER '{cluster}' ( + `updated_date_time` DateTime COMMENT 'Timestamp when the record was last updated' CODEC(DoubleDelta, ZSTD(1)), + `epoch` UInt32 COMMENT 'The epoch number' CODEC(DoubleDelta, ZSTD(1)), + `epoch_start_date_time` DateTime COMMENT 'The wall clock time when the epoch started' CODEC(DoubleDelta, ZSTD(1)), + `entity` String COMMENT 'The entity (staking provider) associated with the validators, unknown if not mapped' CODEC(ZSTD(1)), + `status` String COMMENT 'Attestation status: attested or missed' CODEC(ZSTD(1)), + `attestation_count` UInt64 COMMENT 'Sum of attestations for this epoch/entity/status combination' CODEC(ZSTD(1)) +) ENGINE = ReplicatedReplacingMergeTree( + '/clickhouse/{installation}/{cluster}/tables/{shard}/{database}/{table}', + '{replica}', + `updated_date_time` +) PARTITION BY toStartOfMonth(epoch_start_date_time) +ORDER BY + (`epoch_start_date_time`, `entity`, `status`) +SETTINGS + deduplicate_merge_projection_mode = 'rebuild', + min_age_to_force_merge_seconds = 384, + min_age_to_force_merge_on_partition_only=false +COMMENT 'Attestation liveness aggregated by entity and epoch for the head chain. Reduces slot-level data by ~32x.'; + +-- Create distributed table wrapper +CREATE TABLE `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head ON CLUSTER '{cluster}' AS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head_local ENGINE = Distributed( + '{cluster}', + '${NETWORK_NAME}', + fct_attestation_liveness_by_entity_epoch_head_local, + cityHash64(`epoch`) +); + +-- Add secondary projection for efficient epoch number queries +ALTER TABLE `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_epoch_head_local ON CLUSTER '{cluster}' +ADD PROJECTION p_by_epoch +( + SELECT * + ORDER BY (`epoch`) +); diff --git a/migrations/026_attestation_liveness_by_entity_day_head.down.sql b/migrations/026_attestation_liveness_by_entity_day_head.down.sql new file mode 100644 index 00000000..2f875d4c --- /dev/null +++ b/migrations/026_attestation_liveness_by_entity_day_head.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_day_head ON CLUSTER '{cluster}'; +DROP TABLE IF EXISTS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_day_head_local ON CLUSTER '{cluster}'; diff --git a/migrations/026_attestation_liveness_by_entity_day_head.up.sql b/migrations/026_attestation_liveness_by_entity_day_head.up.sql new file mode 100644 index 00000000..71dbb4d0 --- /dev/null +++ b/migrations/026_attestation_liveness_by_entity_day_head.up.sql @@ -0,0 +1,27 @@ +-- Create local table with ReplicatedReplacingMergeTree +CREATE TABLE `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_day_head_local ON CLUSTER '{cluster}' ( + `updated_date_time` DateTime COMMENT 'Timestamp when the record was last updated' CODEC(DoubleDelta, ZSTD(1)), + `date` Date COMMENT 'The calendar date' CODEC(DoubleDelta, ZSTD(1)), + `entity` String COMMENT 'The entity (staking provider) associated with the validators, unknown if not mapped' CODEC(ZSTD(1)), + `status` String COMMENT 'Attestation status: attested or missed' CODEC(ZSTD(1)), + `attestation_count` UInt64 COMMENT 'Sum of attestations for this date/entity/status combination' CODEC(ZSTD(1)) +) ENGINE = ReplicatedReplacingMergeTree( + '/clickhouse/{installation}/{cluster}/tables/{shard}/{database}/{table}', + '{replica}', + `updated_date_time` +) PARTITION BY toStartOfMonth(date) +ORDER BY + (`date`, `entity`, `status`) +SETTINGS + deduplicate_merge_projection_mode = 'rebuild', + min_age_to_force_merge_seconds = 384, + min_age_to_force_merge_on_partition_only=false +COMMENT 'Attestation liveness aggregated by entity and day for the head chain. Reduces epoch-level data by ~225x.'; + +-- Create distributed table wrapper +CREATE TABLE `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_day_head ON CLUSTER '{cluster}' AS `${NETWORK_NAME}`.fct_attestation_liveness_by_entity_day_head_local ENGINE = Distributed( + '{cluster}', + '${NETWORK_NAME}', + fct_attestation_liveness_by_entity_day_head_local, + cityHash64(`date`) +); diff --git a/models/transformations/fct_attestation_liveness_by_entity_day_head.sql b/models/transformations/fct_attestation_liveness_by_entity_day_head.sql new file mode 100644 index 00000000..1d00b5fd --- /dev/null +++ b/models/transformations/fct_attestation_liveness_by_entity_day_head.sql @@ -0,0 +1,17 @@ +--- +table: fct_attestation_liveness_by_entity_day_head +type: incremental +interval: + type: slot + max: 50000 +--- +INSERT INTO `{{ .self.database }}`.`{{ .self.table }}` +SELECT + fromUnixTimestamp({{ .task.start }}) as updated_date_time, + toDate(epoch_start_date_time) as date, + entity, + status, + SUM(attestation_count) as attestation_count +FROM {{ index .dep "{{transformation}}" "fct_attestation_liveness_by_entity_epoch_head" "helpers" "from" }} FINAL +WHERE epoch_start_date_time BETWEEN fromUnixTimestamp({{ .bounds.start }}) AND fromUnixTimestamp({{ .bounds.end }}) +GROUP BY toDate(epoch_start_date_time), entity, status diff --git a/models/transformations/fct_attestation_liveness_by_entity_epoch_head.sql b/models/transformations/fct_attestation_liveness_by_entity_epoch_head.sql new file mode 100644 index 00000000..5d588167 --- /dev/null +++ b/models/transformations/fct_attestation_liveness_by_entity_epoch_head.sql @@ -0,0 +1,18 @@ +--- +table: fct_attestation_liveness_by_entity_epoch_head +type: incremental +interval: + type: slot + max: 50000 +--- +INSERT INTO `{{ .self.database }}`.`{{ .self.table }}` +SELECT + fromUnixTimestamp({{ .task.start }}) as updated_date_time, + epoch, + epoch_start_date_time, + entity, + status, + SUM(attestation_count) as attestation_count +FROM {{ index .dep "{{transformation}}" "fct_attestation_liveness_by_entity_head" "helpers" "from" }} FINAL +WHERE slot_start_date_time BETWEEN fromUnixTimestamp({{ .bounds.start }}) AND fromUnixTimestamp({{ .bounds.end }}) +GROUP BY epoch, epoch_start_date_time, entity, status diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.go b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.go new file mode 100644 index 00000000..15ab19e1 --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.go @@ -0,0 +1,238 @@ +// Code generated by clickhouse-proto-gen. DO NOT EDIT. +// SQL query builder for fct_attestation_liveness_by_entity_day_head + +package clickhouse + +import ( + "fmt" +) + +// BuildListFctAttestationLivenessByEntityDayHeadQuery constructs a parameterized SQL query from a ListFctAttestationLivenessByEntityDayHeadRequest +func BuildListFctAttestationLivenessByEntityDayHeadQuery(req *ListFctAttestationLivenessByEntityDayHeadRequest, options ...QueryOption) (SQLQuery, error) { + // Validate that at least one primary key is provided + // Primary keys can come from base table or projections + if req.Date == nil { + return SQLQuery{}, fmt.Errorf("primary key field date is required") + } + + // Build query using QueryBuilder + qb := NewQueryBuilder() + + // Add primary key filter + switch filter := req.Date.Filter.(type) { + case *StringFilter_Eq: + qb.AddCondition("date", "=", filter.Eq) + case *StringFilter_Ne: + qb.AddCondition("date", "!=", filter.Ne) + case *StringFilter_Contains: + qb.AddLikeCondition("date", "%" + filter.Contains + "%") + case *StringFilter_StartsWith: + qb.AddLikeCondition("date", filter.StartsWith + "%") + case *StringFilter_EndsWith: + qb.AddLikeCondition("date", "%" + filter.EndsWith) + case *StringFilter_Like: + qb.AddLikeCondition("date", filter.Like) + case *StringFilter_NotLike: + qb.AddNotLikeCondition("date", filter.NotLike) + case *StringFilter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("date", StringSliceToInterface(filter.In.Values)) + } + case *StringFilter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("date", StringSliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + + // Add filter for column: updated_date_time + if req.UpdatedDateTime != nil { + switch filter := req.UpdatedDateTime.Filter.(type) { + case *UInt32Filter_Eq: + qb.AddCondition("updated_date_time", "=", DateTimeValue{filter.Eq}) + case *UInt32Filter_Ne: + qb.AddCondition("updated_date_time", "!=", DateTimeValue{filter.Ne}) + case *UInt32Filter_Lt: + qb.AddCondition("updated_date_time", "<", DateTimeValue{filter.Lt}) + case *UInt32Filter_Lte: + qb.AddCondition("updated_date_time", "<=", DateTimeValue{filter.Lte}) + case *UInt32Filter_Gt: + qb.AddCondition("updated_date_time", ">", DateTimeValue{filter.Gt}) + case *UInt32Filter_Gte: + qb.AddCondition("updated_date_time", ">=", DateTimeValue{filter.Gte}) + case *UInt32Filter_Between: + qb.AddBetweenCondition("updated_date_time", DateTimeValue{filter.Between.Min}, DateTimeValue{filter.Between.Max.GetValue()}) + case *UInt32Filter_In: + if len(filter.In.Values) > 0 { + converted := make([]interface{}, len(filter.In.Values)) + for i, v := range filter.In.Values { + converted[i] = DateTimeValue{v} + } + qb.AddInCondition("updated_date_time", converted) + } + case *UInt32Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + converted := make([]interface{}, len(filter.NotIn.Values)) + for i, v := range filter.NotIn.Values { + converted[i] = DateTimeValue{v} + } + qb.AddNotInCondition("updated_date_time", converted) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: entity + if req.Entity != nil { + switch filter := req.Entity.Filter.(type) { + case *StringFilter_Eq: + qb.AddCondition("entity", "=", filter.Eq) + case *StringFilter_Ne: + qb.AddCondition("entity", "!=", filter.Ne) + case *StringFilter_Contains: + qb.AddLikeCondition("entity", "%" + filter.Contains + "%") + case *StringFilter_StartsWith: + qb.AddLikeCondition("entity", filter.StartsWith + "%") + case *StringFilter_EndsWith: + qb.AddLikeCondition("entity", "%" + filter.EndsWith) + case *StringFilter_Like: + qb.AddLikeCondition("entity", filter.Like) + case *StringFilter_NotLike: + qb.AddNotLikeCondition("entity", filter.NotLike) + case *StringFilter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("entity", StringSliceToInterface(filter.In.Values)) + } + case *StringFilter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("entity", StringSliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: status + if req.Status != nil { + switch filter := req.Status.Filter.(type) { + case *StringFilter_Eq: + qb.AddCondition("status", "=", filter.Eq) + case *StringFilter_Ne: + qb.AddCondition("status", "!=", filter.Ne) + case *StringFilter_Contains: + qb.AddLikeCondition("status", "%" + filter.Contains + "%") + case *StringFilter_StartsWith: + qb.AddLikeCondition("status", filter.StartsWith + "%") + case *StringFilter_EndsWith: + qb.AddLikeCondition("status", "%" + filter.EndsWith) + case *StringFilter_Like: + qb.AddLikeCondition("status", filter.Like) + case *StringFilter_NotLike: + qb.AddNotLikeCondition("status", filter.NotLike) + case *StringFilter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("status", StringSliceToInterface(filter.In.Values)) + } + case *StringFilter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("status", StringSliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: attestation_count + if req.AttestationCount != nil { + switch filter := req.AttestationCount.Filter.(type) { + case *UInt64Filter_Eq: + qb.AddCondition("attestation_count", "=", filter.Eq) + case *UInt64Filter_Ne: + qb.AddCondition("attestation_count", "!=", filter.Ne) + case *UInt64Filter_Lt: + qb.AddCondition("attestation_count", "<", filter.Lt) + case *UInt64Filter_Lte: + qb.AddCondition("attestation_count", "<=", filter.Lte) + case *UInt64Filter_Gt: + qb.AddCondition("attestation_count", ">", filter.Gt) + case *UInt64Filter_Gte: + qb.AddCondition("attestation_count", ">=", filter.Gte) + case *UInt64Filter_Between: + qb.AddBetweenCondition("attestation_count", filter.Between.Min, filter.Between.Max.GetValue()) + case *UInt64Filter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("attestation_count", UInt64SliceToInterface(filter.In.Values)) + } + case *UInt64Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("attestation_count", UInt64SliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Handle pagination per AIP-132 + // Validate page size + if req.PageSize < 0 { + return SQLQuery{}, fmt.Errorf("page_size must be non-negative, got %d", req.PageSize) + } + if req.PageSize > 10000 { + return SQLQuery{}, fmt.Errorf("page_size must not exceed %d, got %d", 10000, req.PageSize) + } + + var limit, offset uint32 + limit = 100 // Default page size + if req.PageSize > 0 { + limit = uint32(req.PageSize) + } + if req.PageToken != "" { + decodedOffset, err := DecodePageToken(req.PageToken) + if err != nil { + return SQLQuery{}, fmt.Errorf("invalid page_token: %w", err) + } + offset = decodedOffset + } + + // Handle custom ordering if provided + var orderByClause string + if req.OrderBy != "" { + validFields := []string{"updated_date_time", "date", "entity", "status", "attestation_count"} + orderFields, err := ParseOrderBy(req.OrderBy, validFields) + if err != nil { + return SQLQuery{}, fmt.Errorf("invalid order_by: %w", err) + } + orderByClause = BuildOrderByClause(orderFields) + } else { + // Default sorting by primary key + orderByClause = " ORDER BY date" + ", entity" + ", status" + } + + // Build column list + columns := []string{"toUnixTimestamp(`updated_date_time`) AS `updated_date_time`", "toString(`date`) AS `date`", "entity", "status", "attestation_count"} + + return BuildParameterizedQuery("fct_attestation_liveness_by_entity_day_head", columns, qb, orderByClause, limit, offset, options...) +} + +// BuildGetFctAttestationLivenessByEntityDayHeadQuery constructs a parameterized SQL query from a GetFctAttestationLivenessByEntityDayHeadRequest +func BuildGetFctAttestationLivenessByEntityDayHeadQuery(req *GetFctAttestationLivenessByEntityDayHeadRequest, options ...QueryOption) (SQLQuery, error) { + // Validate primary key is provided + if req.Date == "" { + return SQLQuery{}, fmt.Errorf("primary key field date is required") + } + + // Build query with primary key condition + qb := NewQueryBuilder() + qb.AddCondition("date", "=", req.Date) + + // Build ORDER BY clause + orderByClause := " ORDER BY date, entity, status" + + // Build column list + columns := []string{"toUnixTimestamp(`updated_date_time`) AS `updated_date_time`", "toString(`date`) AS `date`", "entity", "status", "attestation_count"} + + // Return single record + return BuildParameterizedQuery("fct_attestation_liveness_by_entity_day_head", columns, qb, orderByClause, 1, 0, options...) +} diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.pb.go b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.pb.go new file mode 100644 index 00000000..2f6a654e --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.pb.go @@ -0,0 +1,622 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: fct_attestation_liveness_by_entity_day_head.proto + +package clickhouse + +import ( + _ "github.com/ethpandaops/xatu-cbt/pkg/proto/clickhouse/clickhouse" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FctAttestationLivenessByEntityDayHead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Timestamp when the record was last updated + UpdatedDateTime uint32 `protobuf:"varint,11,opt,name=updated_date_time,json=updatedDateTime,proto3" json:"updated_date_time,omitempty"` + // The calendar date + Date string `protobuf:"bytes,12,opt,name=date,proto3" json:"date,omitempty"` + // The entity (staking provider) associated with the validators, unknown if not mapped + Entity string `protobuf:"bytes,13,opt,name=entity,proto3" json:"entity,omitempty"` + // Attestation status: attested or missed + Status string `protobuf:"bytes,14,opt,name=status,proto3" json:"status,omitempty"` + // Sum of attestations for this date/entity/status combination + AttestationCount uint64 `protobuf:"varint,15,opt,name=attestation_count,json=attestationCount,proto3" json:"attestation_count,omitempty"` +} + +func (x *FctAttestationLivenessByEntityDayHead) Reset() { + *x = FctAttestationLivenessByEntityDayHead{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FctAttestationLivenessByEntityDayHead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FctAttestationLivenessByEntityDayHead) ProtoMessage() {} + +func (x *FctAttestationLivenessByEntityDayHead) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FctAttestationLivenessByEntityDayHead.ProtoReflect.Descriptor instead. +func (*FctAttestationLivenessByEntityDayHead) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP(), []int{0} +} + +func (x *FctAttestationLivenessByEntityDayHead) GetUpdatedDateTime() uint32 { + if x != nil { + return x.UpdatedDateTime + } + return 0 +} + +func (x *FctAttestationLivenessByEntityDayHead) GetDate() string { + if x != nil { + return x.Date + } + return "" +} + +func (x *FctAttestationLivenessByEntityDayHead) GetEntity() string { + if x != nil { + return x.Entity + } + return "" +} + +func (x *FctAttestationLivenessByEntityDayHead) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *FctAttestationLivenessByEntityDayHead) GetAttestationCount() uint64 { + if x != nil { + return x.AttestationCount + } + return 0 +} + +// Request for listing fct_attestation_liveness_by_entity_day_head records +type ListFctAttestationLivenessByEntityDayHeadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Filter by date - The calendar date (PRIMARY KEY - required) + Date *StringFilter `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"` + // Filter by entity - The entity (staking provider) associated with the validators, unknown if not mapped (ORDER BY column 2 - optional) + Entity *StringFilter `protobuf:"bytes,2,opt,name=entity,proto3" json:"entity,omitempty"` + // Filter by status - Attestation status: attested or missed (ORDER BY column 3 - optional) + Status *StringFilter `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + // Filter by updated_date_time - Timestamp when the record was last updated (optional) + UpdatedDateTime *UInt32Filter `protobuf:"bytes,4,opt,name=updated_date_time,json=updatedDateTime,proto3" json:"updated_date_time,omitempty"` + // Filter by attestation_count - Sum of attestations for this date/entity/status combination (optional) + AttestationCount *UInt64Filter `protobuf:"bytes,5,opt,name=attestation_count,json=attestationCount,proto3" json:"attestation_count,omitempty"` + // The maximum number of fct_attestation_liveness_by_entity_day_head to return. + // If unspecified, at most 100 items will be returned. + // The maximum value is 10000; values above 10000 will be coerced to 10000. + PageSize int32 `protobuf:"varint,6,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // A page token, received from a previous `ListFctAttestationLivenessByEntityDayHead` call. + // Provide this to retrieve the subsequent page. + PageToken string `protobuf:"bytes,7,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + // The order of results. Format: comma-separated list of fields. + // Example: "foo,bar" or "foo desc,bar" for descending order on foo. + // If unspecified, results will be returned in the default order. + OrderBy string `protobuf:"bytes,8,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) Reset() { + *x = ListFctAttestationLivenessByEntityDayHeadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFctAttestationLivenessByEntityDayHeadRequest) ProtoMessage() {} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFctAttestationLivenessByEntityDayHeadRequest.ProtoReflect.Descriptor instead. +func (*ListFctAttestationLivenessByEntityDayHeadRequest) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP(), []int{1} +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetDate() *StringFilter { + if x != nil { + return x.Date + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetEntity() *StringFilter { + if x != nil { + return x.Entity + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetStatus() *StringFilter { + if x != nil { + return x.Status + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetUpdatedDateTime() *UInt32Filter { + if x != nil { + return x.UpdatedDateTime + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetAttestationCount() *UInt64Filter { + if x != nil { + return x.AttestationCount + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +func (x *ListFctAttestationLivenessByEntityDayHeadRequest) GetOrderBy() string { + if x != nil { + return x.OrderBy + } + return "" +} + +// Response for listing fct_attestation_liveness_by_entity_day_head records +type ListFctAttestationLivenessByEntityDayHeadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of fct_attestation_liveness_by_entity_day_head. + FctAttestationLivenessByEntityDayHead []*FctAttestationLivenessByEntityDayHead `protobuf:"bytes,1,rep,name=fct_attestation_liveness_by_entity_day_head,json=fctAttestationLivenessByEntityDayHead,proto3" json:"fct_attestation_liveness_by_entity_day_head,omitempty"` + // A token, which can be sent as `page_token` to retrieve the next page. + // If this field is omitted, there are no subsequent pages. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` +} + +func (x *ListFctAttestationLivenessByEntityDayHeadResponse) Reset() { + *x = ListFctAttestationLivenessByEntityDayHeadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFctAttestationLivenessByEntityDayHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFctAttestationLivenessByEntityDayHeadResponse) ProtoMessage() {} + +func (x *ListFctAttestationLivenessByEntityDayHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFctAttestationLivenessByEntityDayHeadResponse.ProtoReflect.Descriptor instead. +func (*ListFctAttestationLivenessByEntityDayHeadResponse) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP(), []int{2} +} + +func (x *ListFctAttestationLivenessByEntityDayHeadResponse) GetFctAttestationLivenessByEntityDayHead() []*FctAttestationLivenessByEntityDayHead { + if x != nil { + return x.FctAttestationLivenessByEntityDayHead + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityDayHeadResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +// Request for getting a single fct_attestation_liveness_by_entity_day_head record by primary key +type GetFctAttestationLivenessByEntityDayHeadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The calendar date + Date string `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"` // Primary key (required) +} + +func (x *GetFctAttestationLivenessByEntityDayHeadRequest) Reset() { + *x = GetFctAttestationLivenessByEntityDayHeadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFctAttestationLivenessByEntityDayHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFctAttestationLivenessByEntityDayHeadRequest) ProtoMessage() {} + +func (x *GetFctAttestationLivenessByEntityDayHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFctAttestationLivenessByEntityDayHeadRequest.ProtoReflect.Descriptor instead. +func (*GetFctAttestationLivenessByEntityDayHeadRequest) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP(), []int{3} +} + +func (x *GetFctAttestationLivenessByEntityDayHeadRequest) GetDate() string { + if x != nil { + return x.Date + } + return "" +} + +// Response for getting a single fct_attestation_liveness_by_entity_day_head record +type GetFctAttestationLivenessByEntityDayHeadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *FctAttestationLivenessByEntityDayHead `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *GetFctAttestationLivenessByEntityDayHeadResponse) Reset() { + *x = GetFctAttestationLivenessByEntityDayHeadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFctAttestationLivenessByEntityDayHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFctAttestationLivenessByEntityDayHeadResponse) ProtoMessage() {} + +func (x *GetFctAttestationLivenessByEntityDayHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFctAttestationLivenessByEntityDayHeadResponse.ProtoReflect.Descriptor instead. +func (*GetFctAttestationLivenessByEntityDayHeadResponse) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP(), []int{4} +} + +func (x *GetFctAttestationLivenessByEntityDayHeadResponse) GetItem() *FctAttestationLivenessByEntityDayHead { + if x != nil { + return x.Item + } + return nil +} + +var File_fct_attestation_liveness_by_entity_day_head_proto protoreflect.FileDescriptor + +var file_fct_attestation_liveness_by_entity_day_head_proto_rawDesc = []byte{ + 0x0a, 0x31, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x63, 0x62, 0x74, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x68, 0x6f, 0x75, 0x73, + 0x65, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x01, 0x0a, 0x25, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, + 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x12, 0x2a, 0x0a, + 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, + 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xbc, 0x03, 0x0a, 0x30, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x39, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x63, 0x62, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x42, 0x12, 0xe0, 0x41, 0x02, 0x9a, 0xb5, 0x18, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, + 0x41, 0x01, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, + 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, + 0x41, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x55, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x0f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x43, + 0x0a, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, + 0x01, 0x52, 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x08, 0x70, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x09, + 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, 0x0a, 0x08, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x01, + 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x22, 0xe4, 0x01, 0x0a, 0x31, 0x4c, 0x69, + 0x73, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x86, 0x01, 0x0a, 0x2b, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x5f, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x46, 0x63, 0x74, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, + 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, + 0x64, 0x52, 0x25, 0x66, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, + 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0x45, 0x0a, 0x2f, 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x22, 0x72, 0x0a, 0x30, 0x47, 0x65, 0x74, 0x46, 0x63, + 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, + 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x62, 0x74, 0x2e, + 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, + 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x9c, 0x03, 0x0a, 0x2c, + 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, + 0x79, 0x48, 0x65, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb2, 0x01, 0x0a, + 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x35, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, + 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x63, + 0x62, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x12, 0x33, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, + 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x12, 0xb6, 0x01, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x34, 0x2e, 0x63, 0x62, 0x74, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x35, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, + 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x44, 0x61, 0x79, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x12, 0x3a, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, + 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x65, 0x7d, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x70, 0x61, 0x6e, 0x64, + 0x61, 0x6f, 0x70, 0x73, 0x2f, 0x78, 0x61, 0x74, 0x75, 0x2d, 0x63, 0x62, 0x74, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x68, 0x6f, 0x75, + 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_fct_attestation_liveness_by_entity_day_head_proto_rawDescOnce sync.Once + file_fct_attestation_liveness_by_entity_day_head_proto_rawDescData = file_fct_attestation_liveness_by_entity_day_head_proto_rawDesc +) + +func file_fct_attestation_liveness_by_entity_day_head_proto_rawDescGZIP() []byte { + file_fct_attestation_liveness_by_entity_day_head_proto_rawDescOnce.Do(func() { + file_fct_attestation_liveness_by_entity_day_head_proto_rawDescData = protoimpl.X.CompressGZIP(file_fct_attestation_liveness_by_entity_day_head_proto_rawDescData) + }) + return file_fct_attestation_liveness_by_entity_day_head_proto_rawDescData +} + +var file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_fct_attestation_liveness_by_entity_day_head_proto_goTypes = []any{ + (*FctAttestationLivenessByEntityDayHead)(nil), // 0: cbt.FctAttestationLivenessByEntityDayHead + (*ListFctAttestationLivenessByEntityDayHeadRequest)(nil), // 1: cbt.ListFctAttestationLivenessByEntityDayHeadRequest + (*ListFctAttestationLivenessByEntityDayHeadResponse)(nil), // 2: cbt.ListFctAttestationLivenessByEntityDayHeadResponse + (*GetFctAttestationLivenessByEntityDayHeadRequest)(nil), // 3: cbt.GetFctAttestationLivenessByEntityDayHeadRequest + (*GetFctAttestationLivenessByEntityDayHeadResponse)(nil), // 4: cbt.GetFctAttestationLivenessByEntityDayHeadResponse + (*StringFilter)(nil), // 5: cbt.StringFilter + (*UInt32Filter)(nil), // 6: cbt.UInt32Filter + (*UInt64Filter)(nil), // 7: cbt.UInt64Filter +} +var file_fct_attestation_liveness_by_entity_day_head_proto_depIdxs = []int32{ + 5, // 0: cbt.ListFctAttestationLivenessByEntityDayHeadRequest.date:type_name -> cbt.StringFilter + 5, // 1: cbt.ListFctAttestationLivenessByEntityDayHeadRequest.entity:type_name -> cbt.StringFilter + 5, // 2: cbt.ListFctAttestationLivenessByEntityDayHeadRequest.status:type_name -> cbt.StringFilter + 6, // 3: cbt.ListFctAttestationLivenessByEntityDayHeadRequest.updated_date_time:type_name -> cbt.UInt32Filter + 7, // 4: cbt.ListFctAttestationLivenessByEntityDayHeadRequest.attestation_count:type_name -> cbt.UInt64Filter + 0, // 5: cbt.ListFctAttestationLivenessByEntityDayHeadResponse.fct_attestation_liveness_by_entity_day_head:type_name -> cbt.FctAttestationLivenessByEntityDayHead + 0, // 6: cbt.GetFctAttestationLivenessByEntityDayHeadResponse.item:type_name -> cbt.FctAttestationLivenessByEntityDayHead + 1, // 7: cbt.FctAttestationLivenessByEntityDayHeadService.List:input_type -> cbt.ListFctAttestationLivenessByEntityDayHeadRequest + 3, // 8: cbt.FctAttestationLivenessByEntityDayHeadService.Get:input_type -> cbt.GetFctAttestationLivenessByEntityDayHeadRequest + 2, // 9: cbt.FctAttestationLivenessByEntityDayHeadService.List:output_type -> cbt.ListFctAttestationLivenessByEntityDayHeadResponse + 4, // 10: cbt.FctAttestationLivenessByEntityDayHeadService.Get:output_type -> cbt.GetFctAttestationLivenessByEntityDayHeadResponse + 9, // [9:11] is the sub-list for method output_type + 7, // [7:9] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_fct_attestation_liveness_by_entity_day_head_proto_init() } +func file_fct_attestation_liveness_by_entity_day_head_proto_init() { + if File_fct_attestation_liveness_by_entity_day_head_proto != nil { + return + } + file_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*FctAttestationLivenessByEntityDayHead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*ListFctAttestationLivenessByEntityDayHeadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*ListFctAttestationLivenessByEntityDayHeadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*GetFctAttestationLivenessByEntityDayHeadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*GetFctAttestationLivenessByEntityDayHeadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_fct_attestation_liveness_by_entity_day_head_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_fct_attestation_liveness_by_entity_day_head_proto_goTypes, + DependencyIndexes: file_fct_attestation_liveness_by_entity_day_head_proto_depIdxs, + MessageInfos: file_fct_attestation_liveness_by_entity_day_head_proto_msgTypes, + }.Build() + File_fct_attestation_liveness_by_entity_day_head_proto = out.File + file_fct_attestation_liveness_by_entity_day_head_proto_rawDesc = nil + file_fct_attestation_liveness_by_entity_day_head_proto_goTypes = nil + file_fct_attestation_liveness_by_entity_day_head_proto_depIdxs = nil +} diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.proto b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.proto new file mode 100644 index 00000000..08a86b5c --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_day_head.proto @@ -0,0 +1,89 @@ +syntax = "proto3"; + +package cbt; + +import "common.proto"; +import "google/api/annotations.proto"; +import "google/api/field_behavior.proto"; +import "clickhouse/annotations.proto"; + +option go_package = "github.com/ethpandaops/xatu-cbt/pkg/proto/clickhouse"; +// Attestation liveness aggregated by entity and day for the head chain. Reduces epoch-level data by ~225x. + +message FctAttestationLivenessByEntityDayHead { + // Timestamp when the record was last updated + uint32 updated_date_time = 11; + // The calendar date + string date = 12; + // The entity (staking provider) associated with the validators, unknown if not mapped + string entity = 13; + // Attestation status: attested or missed + string status = 14; + // Sum of attestations for this date/entity/status combination + uint64 attestation_count = 15; +} + +// Request for listing fct_attestation_liveness_by_entity_day_head records +message ListFctAttestationLivenessByEntityDayHeadRequest { + // Filter by date - The calendar date (PRIMARY KEY - required) + StringFilter date = 1 [(google.api.field_behavior) = REQUIRED, (clickhouse.v1.required_group) = "primary_key"]; + + // Filter by entity - The entity (staking provider) associated with the validators, unknown if not mapped (ORDER BY column 2 - optional) + StringFilter entity = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Filter by status - Attestation status: attested or missed (ORDER BY column 3 - optional) + StringFilter status = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Filter by updated_date_time - Timestamp when the record was last updated (optional) + UInt32Filter updated_date_time = 4 [(google.api.field_behavior) = OPTIONAL]; + // Filter by attestation_count - Sum of attestations for this date/entity/status combination (optional) + UInt64Filter attestation_count = 5 [(google.api.field_behavior) = OPTIONAL]; + + // The maximum number of fct_attestation_liveness_by_entity_day_head to return. + // If unspecified, at most 100 items will be returned. + // The maximum value is 10000; values above 10000 will be coerced to 10000. + int32 page_size = 6 [(google.api.field_behavior) = OPTIONAL]; + // A page token, received from a previous `ListFctAttestationLivenessByEntityDayHead` call. + // Provide this to retrieve the subsequent page. + string page_token = 7 [(google.api.field_behavior) = OPTIONAL]; + // The order of results. Format: comma-separated list of fields. + // Example: "foo,bar" or "foo desc,bar" for descending order on foo. + // If unspecified, results will be returned in the default order. + string order_by = 8 [(google.api.field_behavior) = OPTIONAL]; +} + +// Response for listing fct_attestation_liveness_by_entity_day_head records +message ListFctAttestationLivenessByEntityDayHeadResponse { + // The list of fct_attestation_liveness_by_entity_day_head. + repeated FctAttestationLivenessByEntityDayHead fct_attestation_liveness_by_entity_day_head = 1; + // A token, which can be sent as `page_token` to retrieve the next page. + // If this field is omitted, there are no subsequent pages. + string next_page_token = 2; +} + +// Request for getting a single fct_attestation_liveness_by_entity_day_head record by primary key +message GetFctAttestationLivenessByEntityDayHeadRequest { + // The calendar date + string date = 1; // Primary key (required) +} + +// Response for getting a single fct_attestation_liveness_by_entity_day_head record +message GetFctAttestationLivenessByEntityDayHeadResponse { + FctAttestationLivenessByEntityDayHead item = 1; +} + +// Query fct_attestation_liveness_by_entity_day_head data +service FctAttestationLivenessByEntityDayHeadService { + // List records | Retrieve paginated results with optional filtering + rpc List(ListFctAttestationLivenessByEntityDayHeadRequest) returns (ListFctAttestationLivenessByEntityDayHeadResponse) { + option (google.api.http) = { + get: "/api/v1/fct_attestation_liveness_by_entity_day_head" + }; + } + // Get record | Retrieve a single record by date + rpc Get(GetFctAttestationLivenessByEntityDayHeadRequest) returns (GetFctAttestationLivenessByEntityDayHeadResponse) { + option (google.api.http) = { + get: "/api/v1/fct_attestation_liveness_by_entity_day_head/{date}" + }; + } +} diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.go b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.go new file mode 100644 index 00000000..c4d9d16b --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.go @@ -0,0 +1,283 @@ +// Code generated by clickhouse-proto-gen. DO NOT EDIT. +// SQL query builder for fct_attestation_liveness_by_entity_epoch_head + +package clickhouse + +import ( + "fmt" +) + +// BuildListFctAttestationLivenessByEntityEpochHeadQuery constructs a parameterized SQL query from a ListFctAttestationLivenessByEntityEpochHeadRequest +// +// Available projections: +// - p_by_epoch (primary key: epoch) +// +// Use WithProjection() option to select a specific projection. +func BuildListFctAttestationLivenessByEntityEpochHeadQuery(req *ListFctAttestationLivenessByEntityEpochHeadRequest, options ...QueryOption) (SQLQuery, error) { + // Validate that at least one primary key is provided + // Primary keys can come from base table or projections + if req.Epoch == nil && req.EpochStartDateTime == nil { + return SQLQuery{}, fmt.Errorf("at least one primary key field is required: epoch, epoch_start_date_time") + } + + // Build query using QueryBuilder + qb := NewQueryBuilder() + + // Add primary key filter + if req.EpochStartDateTime != nil { + switch filter := req.EpochStartDateTime.Filter.(type) { + case *UInt32Filter_Eq: + qb.AddCondition("epoch_start_date_time", "=", DateTimeValue{filter.Eq}) + case *UInt32Filter_Ne: + qb.AddCondition("epoch_start_date_time", "!=", DateTimeValue{filter.Ne}) + case *UInt32Filter_Lt: + qb.AddCondition("epoch_start_date_time", "<", DateTimeValue{filter.Lt}) + case *UInt32Filter_Lte: + qb.AddCondition("epoch_start_date_time", "<=", DateTimeValue{filter.Lte}) + case *UInt32Filter_Gt: + qb.AddCondition("epoch_start_date_time", ">", DateTimeValue{filter.Gt}) + case *UInt32Filter_Gte: + qb.AddCondition("epoch_start_date_time", ">=", DateTimeValue{filter.Gte}) + case *UInt32Filter_Between: + qb.AddBetweenCondition("epoch_start_date_time", DateTimeValue{filter.Between.Min}, DateTimeValue{filter.Between.Max.GetValue()}) + case *UInt32Filter_In: + if len(filter.In.Values) > 0 { + converted := make([]interface{}, len(filter.In.Values)) + for i, v := range filter.In.Values { + converted[i] = DateTimeValue{v} + } + qb.AddInCondition("epoch_start_date_time", converted) + } + case *UInt32Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + converted := make([]interface{}, len(filter.NotIn.Values)) + for i, v := range filter.NotIn.Values { + converted[i] = DateTimeValue{v} + } + qb.AddNotInCondition("epoch_start_date_time", converted) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: updated_date_time + if req.UpdatedDateTime != nil { + switch filter := req.UpdatedDateTime.Filter.(type) { + case *UInt32Filter_Eq: + qb.AddCondition("updated_date_time", "=", DateTimeValue{filter.Eq}) + case *UInt32Filter_Ne: + qb.AddCondition("updated_date_time", "!=", DateTimeValue{filter.Ne}) + case *UInt32Filter_Lt: + qb.AddCondition("updated_date_time", "<", DateTimeValue{filter.Lt}) + case *UInt32Filter_Lte: + qb.AddCondition("updated_date_time", "<=", DateTimeValue{filter.Lte}) + case *UInt32Filter_Gt: + qb.AddCondition("updated_date_time", ">", DateTimeValue{filter.Gt}) + case *UInt32Filter_Gte: + qb.AddCondition("updated_date_time", ">=", DateTimeValue{filter.Gte}) + case *UInt32Filter_Between: + qb.AddBetweenCondition("updated_date_time", DateTimeValue{filter.Between.Min}, DateTimeValue{filter.Between.Max.GetValue()}) + case *UInt32Filter_In: + if len(filter.In.Values) > 0 { + converted := make([]interface{}, len(filter.In.Values)) + for i, v := range filter.In.Values { + converted[i] = DateTimeValue{v} + } + qb.AddInCondition("updated_date_time", converted) + } + case *UInt32Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + converted := make([]interface{}, len(filter.NotIn.Values)) + for i, v := range filter.NotIn.Values { + converted[i] = DateTimeValue{v} + } + qb.AddNotInCondition("updated_date_time", converted) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: epoch + if req.Epoch != nil { + switch filter := req.Epoch.Filter.(type) { + case *UInt32Filter_Eq: + qb.AddCondition("epoch", "=", filter.Eq) + case *UInt32Filter_Ne: + qb.AddCondition("epoch", "!=", filter.Ne) + case *UInt32Filter_Lt: + qb.AddCondition("epoch", "<", filter.Lt) + case *UInt32Filter_Lte: + qb.AddCondition("epoch", "<=", filter.Lte) + case *UInt32Filter_Gt: + qb.AddCondition("epoch", ">", filter.Gt) + case *UInt32Filter_Gte: + qb.AddCondition("epoch", ">=", filter.Gte) + case *UInt32Filter_Between: + qb.AddBetweenCondition("epoch", filter.Between.Min, filter.Between.Max.GetValue()) + case *UInt32Filter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("epoch", UInt32SliceToInterface(filter.In.Values)) + } + case *UInt32Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("epoch", UInt32SliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: entity + if req.Entity != nil { + switch filter := req.Entity.Filter.(type) { + case *StringFilter_Eq: + qb.AddCondition("entity", "=", filter.Eq) + case *StringFilter_Ne: + qb.AddCondition("entity", "!=", filter.Ne) + case *StringFilter_Contains: + qb.AddLikeCondition("entity", "%" + filter.Contains + "%") + case *StringFilter_StartsWith: + qb.AddLikeCondition("entity", filter.StartsWith + "%") + case *StringFilter_EndsWith: + qb.AddLikeCondition("entity", "%" + filter.EndsWith) + case *StringFilter_Like: + qb.AddLikeCondition("entity", filter.Like) + case *StringFilter_NotLike: + qb.AddNotLikeCondition("entity", filter.NotLike) + case *StringFilter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("entity", StringSliceToInterface(filter.In.Values)) + } + case *StringFilter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("entity", StringSliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: status + if req.Status != nil { + switch filter := req.Status.Filter.(type) { + case *StringFilter_Eq: + qb.AddCondition("status", "=", filter.Eq) + case *StringFilter_Ne: + qb.AddCondition("status", "!=", filter.Ne) + case *StringFilter_Contains: + qb.AddLikeCondition("status", "%" + filter.Contains + "%") + case *StringFilter_StartsWith: + qb.AddLikeCondition("status", filter.StartsWith + "%") + case *StringFilter_EndsWith: + qb.AddLikeCondition("status", "%" + filter.EndsWith) + case *StringFilter_Like: + qb.AddLikeCondition("status", filter.Like) + case *StringFilter_NotLike: + qb.AddNotLikeCondition("status", filter.NotLike) + case *StringFilter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("status", StringSliceToInterface(filter.In.Values)) + } + case *StringFilter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("status", StringSliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Add filter for column: attestation_count + if req.AttestationCount != nil { + switch filter := req.AttestationCount.Filter.(type) { + case *UInt64Filter_Eq: + qb.AddCondition("attestation_count", "=", filter.Eq) + case *UInt64Filter_Ne: + qb.AddCondition("attestation_count", "!=", filter.Ne) + case *UInt64Filter_Lt: + qb.AddCondition("attestation_count", "<", filter.Lt) + case *UInt64Filter_Lte: + qb.AddCondition("attestation_count", "<=", filter.Lte) + case *UInt64Filter_Gt: + qb.AddCondition("attestation_count", ">", filter.Gt) + case *UInt64Filter_Gte: + qb.AddCondition("attestation_count", ">=", filter.Gte) + case *UInt64Filter_Between: + qb.AddBetweenCondition("attestation_count", filter.Between.Min, filter.Between.Max.GetValue()) + case *UInt64Filter_In: + if len(filter.In.Values) > 0 { + qb.AddInCondition("attestation_count", UInt64SliceToInterface(filter.In.Values)) + } + case *UInt64Filter_NotIn: + if len(filter.NotIn.Values) > 0 { + qb.AddNotInCondition("attestation_count", UInt64SliceToInterface(filter.NotIn.Values)) + } + default: + // Unsupported filter type + } + } + + // Handle pagination per AIP-132 + // Validate page size + if req.PageSize < 0 { + return SQLQuery{}, fmt.Errorf("page_size must be non-negative, got %d", req.PageSize) + } + if req.PageSize > 10000 { + return SQLQuery{}, fmt.Errorf("page_size must not exceed %d, got %d", 10000, req.PageSize) + } + + var limit, offset uint32 + limit = 100 // Default page size + if req.PageSize > 0 { + limit = uint32(req.PageSize) + } + if req.PageToken != "" { + decodedOffset, err := DecodePageToken(req.PageToken) + if err != nil { + return SQLQuery{}, fmt.Errorf("invalid page_token: %w", err) + } + offset = decodedOffset + } + + // Handle custom ordering if provided + var orderByClause string + if req.OrderBy != "" { + validFields := []string{"updated_date_time", "epoch", "epoch_start_date_time", "entity", "status", "attestation_count"} + orderFields, err := ParseOrderBy(req.OrderBy, validFields) + if err != nil { + return SQLQuery{}, fmt.Errorf("invalid order_by: %w", err) + } + orderByClause = BuildOrderByClause(orderFields) + } else { + // Default sorting by primary key + orderByClause = " ORDER BY epoch_start_date_time" + ", entity" + ", status" + } + + // Build column list + columns := []string{"toUnixTimestamp(`updated_date_time`) AS `updated_date_time`", "epoch", "toUnixTimestamp(`epoch_start_date_time`) AS `epoch_start_date_time`", "entity", "status", "attestation_count"} + + return BuildParameterizedQuery("fct_attestation_liveness_by_entity_epoch_head", columns, qb, orderByClause, limit, offset, options...) +} + +// BuildGetFctAttestationLivenessByEntityEpochHeadQuery constructs a parameterized SQL query from a GetFctAttestationLivenessByEntityEpochHeadRequest +func BuildGetFctAttestationLivenessByEntityEpochHeadQuery(req *GetFctAttestationLivenessByEntityEpochHeadRequest, options ...QueryOption) (SQLQuery, error) { + // Validate primary key is provided + if req.EpochStartDateTime == 0 { + return SQLQuery{}, fmt.Errorf("primary key field epoch_start_date_time is required") + } + + // Build query with primary key condition + qb := NewQueryBuilder() + qb.AddCondition("epoch_start_date_time", "=", req.EpochStartDateTime) + + // Build ORDER BY clause + orderByClause := " ORDER BY epoch_start_date_time, entity, status" + + // Build column list + columns := []string{"toUnixTimestamp(`updated_date_time`) AS `updated_date_time`", "epoch", "toUnixTimestamp(`epoch_start_date_time`) AS `epoch_start_date_time`", "entity", "status", "attestation_count"} + + // Return single record + return BuildParameterizedQuery("fct_attestation_liveness_by_entity_epoch_head", columns, qb, orderByClause, 1, 0, options...) +} diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.pb.go b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.pb.go new file mode 100644 index 00000000..3a0de3c4 --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.pb.go @@ -0,0 +1,658 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: fct_attestation_liveness_by_entity_epoch_head.proto + +package clickhouse + +import ( + _ "github.com/ethpandaops/xatu-cbt/pkg/proto/clickhouse/clickhouse" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FctAttestationLivenessByEntityEpochHead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Timestamp when the record was last updated + UpdatedDateTime uint32 `protobuf:"varint,11,opt,name=updated_date_time,json=updatedDateTime,proto3" json:"updated_date_time,omitempty"` + // The epoch number + Epoch uint32 `protobuf:"varint,12,opt,name=epoch,proto3" json:"epoch,omitempty"` + // The wall clock time when the epoch started + EpochStartDateTime uint32 `protobuf:"varint,13,opt,name=epoch_start_date_time,json=epochStartDateTime,proto3" json:"epoch_start_date_time,omitempty"` + // The entity (staking provider) associated with the validators, unknown if not mapped + Entity string `protobuf:"bytes,14,opt,name=entity,proto3" json:"entity,omitempty"` + // Attestation status: attested or missed + Status string `protobuf:"bytes,15,opt,name=status,proto3" json:"status,omitempty"` + // Sum of attestations for this epoch/entity/status combination + AttestationCount uint64 `protobuf:"varint,16,opt,name=attestation_count,json=attestationCount,proto3" json:"attestation_count,omitempty"` +} + +func (x *FctAttestationLivenessByEntityEpochHead) Reset() { + *x = FctAttestationLivenessByEntityEpochHead{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FctAttestationLivenessByEntityEpochHead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FctAttestationLivenessByEntityEpochHead) ProtoMessage() {} + +func (x *FctAttestationLivenessByEntityEpochHead) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FctAttestationLivenessByEntityEpochHead.ProtoReflect.Descriptor instead. +func (*FctAttestationLivenessByEntityEpochHead) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP(), []int{0} +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetUpdatedDateTime() uint32 { + if x != nil { + return x.UpdatedDateTime + } + return 0 +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetEpoch() uint32 { + if x != nil { + return x.Epoch + } + return 0 +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetEpochStartDateTime() uint32 { + if x != nil { + return x.EpochStartDateTime + } + return 0 +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetEntity() string { + if x != nil { + return x.Entity + } + return "" +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *FctAttestationLivenessByEntityEpochHead) GetAttestationCount() uint64 { + if x != nil { + return x.AttestationCount + } + return 0 +} + +// Request for listing fct_attestation_liveness_by_entity_epoch_head records +type ListFctAttestationLivenessByEntityEpochHeadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Filter by epoch_start_date_time - The wall clock time when the epoch started (PRIMARY KEY - required unless using alternatives: epoch) + EpochStartDateTime *UInt32Filter `protobuf:"bytes,1,opt,name=epoch_start_date_time,json=epochStartDateTime,proto3" json:"epoch_start_date_time,omitempty"` + // Filter by entity - The entity (staking provider) associated with the validators, unknown if not mapped (ORDER BY column 2 - optional) + Entity *StringFilter `protobuf:"bytes,2,opt,name=entity,proto3" json:"entity,omitempty"` + // Filter by status - Attestation status: attested or missed (ORDER BY column 3 - optional) + Status *StringFilter `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + // Filter by updated_date_time - Timestamp when the record was last updated (optional) + UpdatedDateTime *UInt32Filter `protobuf:"bytes,4,opt,name=updated_date_time,json=updatedDateTime,proto3" json:"updated_date_time,omitempty"` + // Filter by epoch - The epoch number (PROJECTION: p_by_epoch - alternative to epoch_start_date_time) + Epoch *UInt32Filter `protobuf:"bytes,5,opt,name=epoch,proto3" json:"epoch,omitempty"` + // Filter by attestation_count - Sum of attestations for this epoch/entity/status combination (optional) + AttestationCount *UInt64Filter `protobuf:"bytes,6,opt,name=attestation_count,json=attestationCount,proto3" json:"attestation_count,omitempty"` + // The maximum number of fct_attestation_liveness_by_entity_epoch_head to return. + // If unspecified, at most 100 items will be returned. + // The maximum value is 10000; values above 10000 will be coerced to 10000. + PageSize int32 `protobuf:"varint,7,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // A page token, received from a previous `ListFctAttestationLivenessByEntityEpochHead` call. + // Provide this to retrieve the subsequent page. + PageToken string `protobuf:"bytes,8,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + // The order of results. Format: comma-separated list of fields. + // Example: "foo,bar" or "foo desc,bar" for descending order on foo. + // If unspecified, results will be returned in the default order. + OrderBy string `protobuf:"bytes,9,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) Reset() { + *x = ListFctAttestationLivenessByEntityEpochHeadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFctAttestationLivenessByEntityEpochHeadRequest) ProtoMessage() {} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFctAttestationLivenessByEntityEpochHeadRequest.ProtoReflect.Descriptor instead. +func (*ListFctAttestationLivenessByEntityEpochHeadRequest) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP(), []int{1} +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetEpochStartDateTime() *UInt32Filter { + if x != nil { + return x.EpochStartDateTime + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetEntity() *StringFilter { + if x != nil { + return x.Entity + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetStatus() *StringFilter { + if x != nil { + return x.Status + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetUpdatedDateTime() *UInt32Filter { + if x != nil { + return x.UpdatedDateTime + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetEpoch() *UInt32Filter { + if x != nil { + return x.Epoch + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetAttestationCount() *UInt64Filter { + if x != nil { + return x.AttestationCount + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadRequest) GetOrderBy() string { + if x != nil { + return x.OrderBy + } + return "" +} + +// Response for listing fct_attestation_liveness_by_entity_epoch_head records +type ListFctAttestationLivenessByEntityEpochHeadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of fct_attestation_liveness_by_entity_epoch_head. + FctAttestationLivenessByEntityEpochHead []*FctAttestationLivenessByEntityEpochHead `protobuf:"bytes,1,rep,name=fct_attestation_liveness_by_entity_epoch_head,json=fctAttestationLivenessByEntityEpochHead,proto3" json:"fct_attestation_liveness_by_entity_epoch_head,omitempty"` + // A token, which can be sent as `page_token` to retrieve the next page. + // If this field is omitted, there are no subsequent pages. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadResponse) Reset() { + *x = ListFctAttestationLivenessByEntityEpochHeadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFctAttestationLivenessByEntityEpochHeadResponse) ProtoMessage() {} + +func (x *ListFctAttestationLivenessByEntityEpochHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFctAttestationLivenessByEntityEpochHeadResponse.ProtoReflect.Descriptor instead. +func (*ListFctAttestationLivenessByEntityEpochHeadResponse) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP(), []int{2} +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadResponse) GetFctAttestationLivenessByEntityEpochHead() []*FctAttestationLivenessByEntityEpochHead { + if x != nil { + return x.FctAttestationLivenessByEntityEpochHead + } + return nil +} + +func (x *ListFctAttestationLivenessByEntityEpochHeadResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +// Request for getting a single fct_attestation_liveness_by_entity_epoch_head record by primary key +type GetFctAttestationLivenessByEntityEpochHeadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The wall clock time when the epoch started + EpochStartDateTime uint32 `protobuf:"varint,1,opt,name=epoch_start_date_time,json=epochStartDateTime,proto3" json:"epoch_start_date_time,omitempty"` // Primary key (required) +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadRequest) Reset() { + *x = GetFctAttestationLivenessByEntityEpochHeadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFctAttestationLivenessByEntityEpochHeadRequest) ProtoMessage() {} + +func (x *GetFctAttestationLivenessByEntityEpochHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFctAttestationLivenessByEntityEpochHeadRequest.ProtoReflect.Descriptor instead. +func (*GetFctAttestationLivenessByEntityEpochHeadRequest) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP(), []int{3} +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadRequest) GetEpochStartDateTime() uint32 { + if x != nil { + return x.EpochStartDateTime + } + return 0 +} + +// Response for getting a single fct_attestation_liveness_by_entity_epoch_head record +type GetFctAttestationLivenessByEntityEpochHeadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *FctAttestationLivenessByEntityEpochHead `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadResponse) Reset() { + *x = GetFctAttestationLivenessByEntityEpochHeadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFctAttestationLivenessByEntityEpochHeadResponse) ProtoMessage() {} + +func (x *GetFctAttestationLivenessByEntityEpochHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFctAttestationLivenessByEntityEpochHeadResponse.ProtoReflect.Descriptor instead. +func (*GetFctAttestationLivenessByEntityEpochHeadResponse) Descriptor() ([]byte, []int) { + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP(), []int{4} +} + +func (x *GetFctAttestationLivenessByEntityEpochHeadResponse) GetItem() *FctAttestationLivenessByEntityEpochHead { + if x != nil { + return x.Item + } + return nil +} + +var File_fct_attestation_liveness_by_entity_epoch_head_proto protoreflect.FileDescriptor + +var file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDesc = []byte{ + 0x0a, 0x33, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x63, 0x62, 0x74, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x68, 0x6f, + 0x75, 0x73, 0x65, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfb, 0x01, 0x0a, 0x27, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, + 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, + 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x12, 0x31, 0x0a, 0x15, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x12, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0xc1, 0x04, 0x0a, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x63, 0x74, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, + 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, 0x15, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x12, 0xe0, 0x41, + 0x01, 0x9a, 0xb5, 0x18, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, + 0x52, 0x12, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x06, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x62, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x55, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x39, 0xe0, 0x41, 0x01, 0x8a, + 0xb5, 0x18, 0x15, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x92, 0xb5, 0x18, 0x0a, 0x70, 0x5f, 0x62, 0x79, + 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x9a, 0xb5, 0x18, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x43, 0x0a, 0x11, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x55, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, + 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x05, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x09, 0x70, 0x61, + 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x5f, 0x62, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x01, 0x52, 0x07, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x22, 0xec, 0x01, 0x0a, 0x33, 0x4c, 0x69, 0x73, 0x74, + 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, + 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x8c, 0x01, 0x0a, 0x2d, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x5f, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x46, 0x63, + 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, + 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x27, 0x66, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x12, 0x26, + 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, + 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x66, 0x0a, 0x31, 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, + 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, + 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x65, + 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x76, + 0x0a, 0x32, 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, + 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xbb, 0x03, 0x0a, 0x2e, 0x46, 0x63, 0x74, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, + 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, + 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xb8, 0x01, 0x0a, 0x04, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x37, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x63, 0x74, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, + 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, + 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x62, + 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x12, 0x35, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, + 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, + 0x68, 0x65, 0x61, 0x64, 0x12, 0xcd, 0x01, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x36, 0x2e, 0x63, + 0x62, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x63, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x62, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x63, + 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, + 0x6e, 0x65, 0x73, 0x73, 0x42, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x4f, 0x12, 0x4d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x66, + 0x63, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, + 0x69, 0x76, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x2f, 0x7b, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x7d, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x70, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x70, 0x73, 0x2f, 0x78, + 0x61, 0x74, 0x75, 0x2d, 0x63, 0x62, 0x74, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x68, 0x6f, 0x75, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescOnce sync.Once + file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescData = file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDesc +) + +func file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescGZIP() []byte { + file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescOnce.Do(func() { + file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescData = protoimpl.X.CompressGZIP(file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescData) + }) + return file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDescData +} + +var file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_fct_attestation_liveness_by_entity_epoch_head_proto_goTypes = []any{ + (*FctAttestationLivenessByEntityEpochHead)(nil), // 0: cbt.FctAttestationLivenessByEntityEpochHead + (*ListFctAttestationLivenessByEntityEpochHeadRequest)(nil), // 1: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest + (*ListFctAttestationLivenessByEntityEpochHeadResponse)(nil), // 2: cbt.ListFctAttestationLivenessByEntityEpochHeadResponse + (*GetFctAttestationLivenessByEntityEpochHeadRequest)(nil), // 3: cbt.GetFctAttestationLivenessByEntityEpochHeadRequest + (*GetFctAttestationLivenessByEntityEpochHeadResponse)(nil), // 4: cbt.GetFctAttestationLivenessByEntityEpochHeadResponse + (*UInt32Filter)(nil), // 5: cbt.UInt32Filter + (*StringFilter)(nil), // 6: cbt.StringFilter + (*UInt64Filter)(nil), // 7: cbt.UInt64Filter +} +var file_fct_attestation_liveness_by_entity_epoch_head_proto_depIdxs = []int32{ + 5, // 0: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.epoch_start_date_time:type_name -> cbt.UInt32Filter + 6, // 1: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.entity:type_name -> cbt.StringFilter + 6, // 2: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.status:type_name -> cbt.StringFilter + 5, // 3: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.updated_date_time:type_name -> cbt.UInt32Filter + 5, // 4: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.epoch:type_name -> cbt.UInt32Filter + 7, // 5: cbt.ListFctAttestationLivenessByEntityEpochHeadRequest.attestation_count:type_name -> cbt.UInt64Filter + 0, // 6: cbt.ListFctAttestationLivenessByEntityEpochHeadResponse.fct_attestation_liveness_by_entity_epoch_head:type_name -> cbt.FctAttestationLivenessByEntityEpochHead + 0, // 7: cbt.GetFctAttestationLivenessByEntityEpochHeadResponse.item:type_name -> cbt.FctAttestationLivenessByEntityEpochHead + 1, // 8: cbt.FctAttestationLivenessByEntityEpochHeadService.List:input_type -> cbt.ListFctAttestationLivenessByEntityEpochHeadRequest + 3, // 9: cbt.FctAttestationLivenessByEntityEpochHeadService.Get:input_type -> cbt.GetFctAttestationLivenessByEntityEpochHeadRequest + 2, // 10: cbt.FctAttestationLivenessByEntityEpochHeadService.List:output_type -> cbt.ListFctAttestationLivenessByEntityEpochHeadResponse + 4, // 11: cbt.FctAttestationLivenessByEntityEpochHeadService.Get:output_type -> cbt.GetFctAttestationLivenessByEntityEpochHeadResponse + 10, // [10:12] is the sub-list for method output_type + 8, // [8:10] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_fct_attestation_liveness_by_entity_epoch_head_proto_init() } +func file_fct_attestation_liveness_by_entity_epoch_head_proto_init() { + if File_fct_attestation_liveness_by_entity_epoch_head_proto != nil { + return + } + file_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*FctAttestationLivenessByEntityEpochHead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*ListFctAttestationLivenessByEntityEpochHeadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*ListFctAttestationLivenessByEntityEpochHeadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*GetFctAttestationLivenessByEntityEpochHeadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*GetFctAttestationLivenessByEntityEpochHeadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_fct_attestation_liveness_by_entity_epoch_head_proto_goTypes, + DependencyIndexes: file_fct_attestation_liveness_by_entity_epoch_head_proto_depIdxs, + MessageInfos: file_fct_attestation_liveness_by_entity_epoch_head_proto_msgTypes, + }.Build() + File_fct_attestation_liveness_by_entity_epoch_head_proto = out.File + file_fct_attestation_liveness_by_entity_epoch_head_proto_rawDesc = nil + file_fct_attestation_liveness_by_entity_epoch_head_proto_goTypes = nil + file_fct_attestation_liveness_by_entity_epoch_head_proto_depIdxs = nil +} diff --git a/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.proto b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.proto new file mode 100644 index 00000000..ad170201 --- /dev/null +++ b/pkg/proto/clickhouse/fct_attestation_liveness_by_entity_epoch_head.proto @@ -0,0 +1,93 @@ +syntax = "proto3"; + +package cbt; + +import "common.proto"; +import "google/api/annotations.proto"; +import "google/api/field_behavior.proto"; +import "clickhouse/annotations.proto"; + +option go_package = "github.com/ethpandaops/xatu-cbt/pkg/proto/clickhouse"; +// Attestation liveness aggregated by entity and epoch for the head chain. Reduces slot-level data by ~32x. + +message FctAttestationLivenessByEntityEpochHead { + // Timestamp when the record was last updated + uint32 updated_date_time = 11; + // The epoch number + uint32 epoch = 12; + // The wall clock time when the epoch started + uint32 epoch_start_date_time = 13; + // The entity (staking provider) associated with the validators, unknown if not mapped + string entity = 14; + // Attestation status: attested or missed + string status = 15; + // Sum of attestations for this epoch/entity/status combination + uint64 attestation_count = 16; +} + +// Request for listing fct_attestation_liveness_by_entity_epoch_head records +message ListFctAttestationLivenessByEntityEpochHeadRequest { + // Filter by epoch_start_date_time - The wall clock time when the epoch started (PRIMARY KEY - required unless using alternatives: epoch) + UInt32Filter epoch_start_date_time = 1 [(google.api.field_behavior) = OPTIONAL, (clickhouse.v1.required_group) = "primary_key"]; + + // Filter by entity - The entity (staking provider) associated with the validators, unknown if not mapped (ORDER BY column 2 - optional) + StringFilter entity = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Filter by status - Attestation status: attested or missed (ORDER BY column 3 - optional) + StringFilter status = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Filter by updated_date_time - Timestamp when the record was last updated (optional) + UInt32Filter updated_date_time = 4 [(google.api.field_behavior) = OPTIONAL]; + // Filter by epoch - The epoch number (PROJECTION: p_by_epoch - alternative to epoch_start_date_time) + UInt32Filter epoch = 5 [(google.api.field_behavior) = OPTIONAL, (clickhouse.v1.projection_name) = "p_by_epoch", (clickhouse.v1.projection_alternative_for) = "epoch_start_date_time", (clickhouse.v1.required_group) = "primary_key"]; + // Filter by attestation_count - Sum of attestations for this epoch/entity/status combination (optional) + UInt64Filter attestation_count = 6 [(google.api.field_behavior) = OPTIONAL]; + + // The maximum number of fct_attestation_liveness_by_entity_epoch_head to return. + // If unspecified, at most 100 items will be returned. + // The maximum value is 10000; values above 10000 will be coerced to 10000. + int32 page_size = 7 [(google.api.field_behavior) = OPTIONAL]; + // A page token, received from a previous `ListFctAttestationLivenessByEntityEpochHead` call. + // Provide this to retrieve the subsequent page. + string page_token = 8 [(google.api.field_behavior) = OPTIONAL]; + // The order of results. Format: comma-separated list of fields. + // Example: "foo,bar" or "foo desc,bar" for descending order on foo. + // If unspecified, results will be returned in the default order. + string order_by = 9 [(google.api.field_behavior) = OPTIONAL]; +} + +// Response for listing fct_attestation_liveness_by_entity_epoch_head records +message ListFctAttestationLivenessByEntityEpochHeadResponse { + // The list of fct_attestation_liveness_by_entity_epoch_head. + repeated FctAttestationLivenessByEntityEpochHead fct_attestation_liveness_by_entity_epoch_head = 1; + // A token, which can be sent as `page_token` to retrieve the next page. + // If this field is omitted, there are no subsequent pages. + string next_page_token = 2; +} + +// Request for getting a single fct_attestation_liveness_by_entity_epoch_head record by primary key +message GetFctAttestationLivenessByEntityEpochHeadRequest { + // The wall clock time when the epoch started + uint32 epoch_start_date_time = 1; // Primary key (required) +} + +// Response for getting a single fct_attestation_liveness_by_entity_epoch_head record +message GetFctAttestationLivenessByEntityEpochHeadResponse { + FctAttestationLivenessByEntityEpochHead item = 1; +} + +// Query fct_attestation_liveness_by_entity_epoch_head data +service FctAttestationLivenessByEntityEpochHeadService { + // List records | Retrieve paginated results with optional filtering + rpc List(ListFctAttestationLivenessByEntityEpochHeadRequest) returns (ListFctAttestationLivenessByEntityEpochHeadResponse) { + option (google.api.http) = { + get: "/api/v1/fct_attestation_liveness_by_entity_epoch_head" + }; + } + // Get record | Retrieve a single record by epoch_start_date_time + rpc Get(GetFctAttestationLivenessByEntityEpochHeadRequest) returns (GetFctAttestationLivenessByEntityEpochHeadResponse) { + option (google.api.http) = { + get: "/api/v1/fct_attestation_liveness_by_entity_epoch_head/{epoch_start_date_time}" + }; + } +} diff --git a/tests/pectra/assertions/fct_attestation_liveness_by_entity_day_head.yaml b/tests/pectra/assertions/fct_attestation_liveness_by_entity_day_head.yaml new file mode 100644 index 00000000..0dc1f519 --- /dev/null +++ b/tests/pectra/assertions/fct_attestation_liveness_by_entity_day_head.yaml @@ -0,0 +1,15 @@ +- name: "Day table has data" + sql: | + SELECT + COUNT(*) AS row_count + FROM fct_attestation_liveness_by_entity_day_head FINAL + expected: + row_count: 0 + +- name: "No NULL entities" + sql: | + SELECT count(*) as null_count + FROM fct_attestation_liveness_by_entity_day_head FINAL + WHERE entity IS NULL OR entity = '' + expected: + null_count: 0 diff --git a/tests/pectra/assertions/fct_attestation_liveness_by_entity_epoch_head.yaml b/tests/pectra/assertions/fct_attestation_liveness_by_entity_epoch_head.yaml new file mode 100644 index 00000000..fc0299ad --- /dev/null +++ b/tests/pectra/assertions/fct_attestation_liveness_by_entity_epoch_head.yaml @@ -0,0 +1,15 @@ +- name: "Epoch table has data" + sql: | + SELECT + COUNT(*) AS row_count + FROM fct_attestation_liveness_by_entity_epoch_head FINAL + expected: + row_count: 0 + +- name: "No NULL entities" + sql: | + SELECT count(*) as null_count + FROM fct_attestation_liveness_by_entity_epoch_head FINAL + WHERE entity IS NULL OR entity = '' + expected: + null_count: 0