diff --git a/api/openapi.yaml b/api/openapi.yaml index 6a78ad8..679ee75 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -467,14 +467,25 @@ components: root: $ref: "#/components/schemas/Root" + PayloadStatus: + type: string + description: | + The Gloas payload status of a fork-choice node. A Gloas block has a + `pending` node, which is the parent of its `empty` node (the block + without its execution payload) and its `full` node (the block with it). + enum: [pending, empty, full] + ForkChoiceNode: type: object + description: | + A fork-choice node. Pre-Gloas, and for beacon nodes without the v2 + fork-choice endpoint, there is one node per block. Post-Gloas a block can + have a `pending`, an `empty` and a `full` node, which share `slot`, + `block_root` and `parent_root`. required: - slot - block_root - parent_root - - justified_epoch - - finalized_epoch - weight - validity - execution_block_hash @@ -498,6 +509,24 @@ components: enum: [valid, invalid, optimistic, unknown] execution_block_hash: $ref: "#/components/schemas/Root" + payload_status: + $ref: "#/components/schemas/PayloadStatus" + parent_payload_status: + $ref: "#/components/schemas/PayloadStatus" + description: | + The payload status of the parent fork-choice node: for a `pending` + node, whether the block was built on its parent block's `empty` or + `full` payload; for `empty` and `full` nodes, `pending`. Absent if + unknown. + payload_attester_count: + type: string + description: Number of Payload Timeliness Committee positions with a recorded vote. + payload_availability_yes_count: + type: string + description: Number of PTC positions voting that the payload was received on time. + payload_data_availability_yes_count: + type: string + description: Number of PTC positions voting that the blob data is available. extra_data: type: object additionalProperties: true @@ -515,6 +544,12 @@ components: type: array items: $ref: "#/components/schemas/ForkChoiceNode" + extra_data: + type: object + description: | + Client-specific values of the fork-choice store, such as the + unrealized justified checkpoint. + additionalProperties: true Frame: type: object diff --git a/api/rest/oas_json_gen.go b/api/rest/oas_json_gen.go index 33952ec..67f9fd7 100644 --- a/api/rest/oas_json_gen.go +++ b/api/rest/oas_json_gen.go @@ -690,12 +690,19 @@ func (s *ForkChoice) encodeFields(e *jx.Encoder) { } e.ArrEnd() } + { + if s.ExtraData.Set { + e.FieldStart("extra_data") + s.ExtraData.Encode(e) + } + } } -var jsonFieldsNameOfForkChoice = [3]string{ +var jsonFieldsNameOfForkChoice = [4]string{ 0: "justified_checkpoint", 1: "finalized_checkpoint", 2: "fork_choice_nodes", + 3: "extra_data", } // Decode decodes ForkChoice from json. @@ -745,6 +752,16 @@ func (s *ForkChoice) Decode(d *jx.Decoder) error { }(); err != nil { return errors.Wrap(err, "decode field \"fork_choice_nodes\"") } + case "extra_data": + if err := func() error { + s.ExtraData.Reset() + if err := s.ExtraData.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"extra_data\"") + } default: return d.Skip() } @@ -801,6 +818,64 @@ func (s *ForkChoice) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s ForkChoiceExtraData) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields implements json.Marshaler. +func (s ForkChoiceExtraData) encodeFields(e *jx.Encoder) { + for k, elem := range s { + e.FieldStart(k) + + if len(elem) != 0 { + e.Raw(elem) + } + } +} + +// Decode decodes ForkChoiceExtraData from json. +func (s *ForkChoiceExtraData) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode ForkChoiceExtraData to nil") + } + m := s.init() + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + var elem jx.Raw + if err := func() error { + v, err := d.RawAppend(nil) + elem = jx.Raw(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrapf(err, "decode field %q", k) + } + m[string(k)] = elem + return nil + }); err != nil { + return errors.Wrap(err, "decode ForkChoiceExtraData") + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s ForkChoiceExtraData) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *ForkChoiceExtraData) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s *ForkChoiceNode) Encode(e *jx.Encoder) { e.ObjStart() @@ -823,12 +898,16 @@ func (s *ForkChoiceNode) encodeFields(e *jx.Encoder) { s.ParentRoot.Encode(e) } { - e.FieldStart("justified_epoch") - s.JustifiedEpoch.Encode(e) + if s.JustifiedEpoch.Set { + e.FieldStart("justified_epoch") + s.JustifiedEpoch.Encode(e) + } } { - e.FieldStart("finalized_epoch") - s.FinalizedEpoch.Encode(e) + if s.FinalizedEpoch.Set { + e.FieldStart("finalized_epoch") + s.FinalizedEpoch.Encode(e) + } } { e.FieldStart("weight") @@ -842,6 +921,36 @@ func (s *ForkChoiceNode) encodeFields(e *jx.Encoder) { e.FieldStart("execution_block_hash") s.ExecutionBlockHash.Encode(e) } + { + if s.PayloadStatus.Set { + e.FieldStart("payload_status") + s.PayloadStatus.Encode(e) + } + } + { + if s.ParentPayloadStatus.Set { + e.FieldStart("parent_payload_status") + s.ParentPayloadStatus.Encode(e) + } + } + { + if s.PayloadAttesterCount.Set { + e.FieldStart("payload_attester_count") + s.PayloadAttesterCount.Encode(e) + } + } + { + if s.PayloadAvailabilityYesCount.Set { + e.FieldStart("payload_availability_yes_count") + s.PayloadAvailabilityYesCount.Encode(e) + } + } + { + if s.PayloadDataAvailabilityYesCount.Set { + e.FieldStart("payload_data_availability_yes_count") + s.PayloadDataAvailabilityYesCount.Encode(e) + } + } { if s.ExtraData.Set { e.FieldStart("extra_data") @@ -850,16 +959,21 @@ func (s *ForkChoiceNode) encodeFields(e *jx.Encoder) { } } -var jsonFieldsNameOfForkChoiceNode = [9]string{ - 0: "slot", - 1: "block_root", - 2: "parent_root", - 3: "justified_epoch", - 4: "finalized_epoch", - 5: "weight", - 6: "validity", - 7: "execution_block_hash", - 8: "extra_data", +var jsonFieldsNameOfForkChoiceNode = [14]string{ + 0: "slot", + 1: "block_root", + 2: "parent_root", + 3: "justified_epoch", + 4: "finalized_epoch", + 5: "weight", + 6: "validity", + 7: "execution_block_hash", + 8: "payload_status", + 9: "parent_payload_status", + 10: "payload_attester_count", + 11: "payload_availability_yes_count", + 12: "payload_data_availability_yes_count", + 13: "extra_data", } // Decode decodes ForkChoiceNode from json. @@ -902,8 +1016,8 @@ func (s *ForkChoiceNode) Decode(d *jx.Decoder) error { return errors.Wrap(err, "decode field \"parent_root\"") } case "justified_epoch": - requiredBitSet[0] |= 1 << 3 if err := func() error { + s.JustifiedEpoch.Reset() if err := s.JustifiedEpoch.Decode(d); err != nil { return err } @@ -912,8 +1026,8 @@ func (s *ForkChoiceNode) Decode(d *jx.Decoder) error { return errors.Wrap(err, "decode field \"justified_epoch\"") } case "finalized_epoch": - requiredBitSet[0] |= 1 << 4 if err := func() error { + s.FinalizedEpoch.Reset() if err := s.FinalizedEpoch.Decode(d); err != nil { return err } @@ -953,6 +1067,56 @@ func (s *ForkChoiceNode) Decode(d *jx.Decoder) error { }(); err != nil { return errors.Wrap(err, "decode field \"execution_block_hash\"") } + case "payload_status": + if err := func() error { + s.PayloadStatus.Reset() + if err := s.PayloadStatus.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payload_status\"") + } + case "parent_payload_status": + if err := func() error { + s.ParentPayloadStatus.Reset() + if err := s.ParentPayloadStatus.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"parent_payload_status\"") + } + case "payload_attester_count": + if err := func() error { + s.PayloadAttesterCount.Reset() + if err := s.PayloadAttesterCount.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payload_attester_count\"") + } + case "payload_availability_yes_count": + if err := func() error { + s.PayloadAvailabilityYesCount.Reset() + if err := s.PayloadAvailabilityYesCount.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payload_availability_yes_count\"") + } + case "payload_data_availability_yes_count": + if err := func() error { + s.PayloadDataAvailabilityYesCount.Reset() + if err := s.PayloadDataAvailabilityYesCount.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payload_data_availability_yes_count\"") + } case "extra_data": if err := func() error { s.ExtraData.Reset() @@ -973,7 +1137,7 @@ func (s *ForkChoiceNode) Decode(d *jx.Decoder) error { // Validate required fields. var failures []validate.FieldError for i, mask := range [2]uint8{ - 0b11111111, + 0b11100111, 0b00000000, } { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { @@ -3252,6 +3416,73 @@ func (s *OptEpoch) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode encodes EpochString as json. +func (o OptEpochString) Encode(e *jx.Encoder) { + if !o.Set { + return + } + o.Value.Encode(e) +} + +// Decode decodes EpochString from json. +func (o *OptEpochString) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptEpochString to nil") + } + o.Set = true + if err := o.Value.Decode(d); err != nil { + return err + } + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptEpochString) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptEpochString) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes ForkChoiceExtraData as json. +func (o OptForkChoiceExtraData) Encode(e *jx.Encoder) { + if !o.Set { + return + } + o.Value.Encode(e) +} + +// Decode decodes ForkChoiceExtraData from json. +func (o *OptForkChoiceExtraData) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptForkChoiceExtraData to nil") + } + o.Set = true + o.Value = make(ForkChoiceExtraData) + if err := o.Value.Decode(d); err != nil { + return err + } + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptForkChoiceExtraData) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptForkChoiceExtraData) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes ForkChoiceNodeExtraData as json. func (o OptForkChoiceNodeExtraData) Encode(e *jx.Encoder) { if !o.Set { @@ -3387,6 +3618,39 @@ func (s *OptPaginationCursor) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode encodes PayloadStatus as json. +func (o OptPayloadStatus) Encode(e *jx.Encoder) { + if !o.Set { + return + } + e.Str(string(o.Value)) +} + +// Decode decodes PayloadStatus from json. +func (o *OptPayloadStatus) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptPayloadStatus to nil") + } + o.Set = true + if err := o.Value.Decode(d); err != nil { + return err + } + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptPayloadStatus) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptPayloadStatus) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes Slot as json. func (o OptSlot) Encode(e *jx.Encoder) { if !o.Set { @@ -3632,6 +3896,48 @@ func (s *PaginationResponse) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode encodes PayloadStatus as json. +func (s PayloadStatus) Encode(e *jx.Encoder) { + e.Str(string(s)) +} + +// Decode decodes PayloadStatus from json. +func (s *PayloadStatus) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode PayloadStatus to nil") + } + v, err := d.StrBytes() + if err != nil { + return err + } + // Try to use constant string. + switch PayloadStatus(v) { + case PayloadStatusPending: + *s = PayloadStatusPending + case PayloadStatusEmpty: + *s = PayloadStatusEmpty + case PayloadStatusFull: + *s = PayloadStatusFull + default: + *s = PayloadStatus(v) + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s PayloadStatus) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *PayloadStatus) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes Root as json. func (s Root) Encode(e *jx.Encoder) { unwrapped := string(s) diff --git a/api/rest/oas_schemas_gen.go b/api/rest/oas_schemas_gen.go index 42f9ccd..9efce57 100644 --- a/api/rest/oas_schemas_gen.go +++ b/api/rest/oas_schemas_gen.go @@ -176,6 +176,9 @@ type ForkChoice struct { JustifiedCheckpoint Checkpoint `json:"justified_checkpoint"` FinalizedCheckpoint Checkpoint `json:"finalized_checkpoint"` ForkChoiceNodes []ForkChoiceNode `json:"fork_choice_nodes"` + // Client-specific values of the fork-choice store, such as the + // unrealized justified checkpoint. + ExtraData OptForkChoiceExtraData `json:"extra_data"` } // GetJustifiedCheckpoint returns the value of JustifiedCheckpoint. @@ -193,6 +196,11 @@ func (s *ForkChoice) GetForkChoiceNodes() []ForkChoiceNode { return s.ForkChoiceNodes } +// GetExtraData returns the value of ExtraData. +func (s *ForkChoice) GetExtraData() OptForkChoiceExtraData { + return s.ExtraData +} + // SetJustifiedCheckpoint sets the value of JustifiedCheckpoint. func (s *ForkChoice) SetJustifiedCheckpoint(val Checkpoint) { s.JustifiedCheckpoint = val @@ -208,19 +216,53 @@ func (s *ForkChoice) SetForkChoiceNodes(val []ForkChoiceNode) { s.ForkChoiceNodes = val } +// SetExtraData sets the value of ExtraData. +func (s *ForkChoice) SetExtraData(val OptForkChoiceExtraData) { + s.ExtraData = val +} + +// Client-specific values of the fork-choice store, such as the +// unrealized justified checkpoint. +type ForkChoiceExtraData map[string]jx.Raw + +func (s *ForkChoiceExtraData) init() ForkChoiceExtraData { + m := *s + if m == nil { + m = map[string]jx.Raw{} + *s = m + } + return m +} + +// A fork-choice node. Pre-Gloas, and for beacon nodes without the v2 +// fork-choice endpoint, there is one node per block. Post-Gloas a block can +// have a `pending`, an `empty` and a `full` node, which share `slot`, +// `block_root` and `parent_root`. // Ref: #/components/schemas/ForkChoiceNode type ForkChoiceNode struct { - Slot SlotString `json:"slot"` - BlockRoot Root `json:"block_root"` - ParentRoot Root `json:"parent_root"` - JustifiedEpoch EpochString `json:"justified_epoch"` - FinalizedEpoch EpochString `json:"finalized_epoch"` + Slot SlotString `json:"slot"` + BlockRoot Root `json:"block_root"` + ParentRoot Root `json:"parent_root"` + JustifiedEpoch OptEpochString `json:"justified_epoch"` + FinalizedEpoch OptEpochString `json:"finalized_epoch"` // The node's fork-choice weight, serialized as a string. Weight string `json:"weight"` // The node's validity status. - Validity ForkChoiceNodeValidity `json:"validity"` - ExecutionBlockHash Root `json:"execution_block_hash"` - ExtraData OptForkChoiceNodeExtraData `json:"extra_data"` + Validity ForkChoiceNodeValidity `json:"validity"` + ExecutionBlockHash Root `json:"execution_block_hash"` + PayloadStatus OptPayloadStatus `json:"payload_status"` + // The payload status of the parent fork-choice node: for a `pending` + // node, whether the block was built on its parent block's `empty` or + // `full` payload; for `empty` and `full` nodes, `pending`. Absent if + // unknown. + ParentPayloadStatus OptPayloadStatus `json:"parent_payload_status"` + // Number of Payload Timeliness Committee positions with a recorded vote. + PayloadAttesterCount OptString `json:"payload_attester_count"` + // Number of PTC positions voting that the payload was received on time. + PayloadAvailabilityYesCount OptString `json:"payload_availability_yes_count"` + // Number of PTC positions voting that the blob data is available. + PayloadDataAvailabilityYesCount OptString `json:"payload_data_availability_yes_count"` + ExtraData OptForkChoiceNodeExtraData `json:"extra_data"` } // GetSlot returns the value of Slot. @@ -239,12 +281,12 @@ func (s *ForkChoiceNode) GetParentRoot() Root { } // GetJustifiedEpoch returns the value of JustifiedEpoch. -func (s *ForkChoiceNode) GetJustifiedEpoch() EpochString { +func (s *ForkChoiceNode) GetJustifiedEpoch() OptEpochString { return s.JustifiedEpoch } // GetFinalizedEpoch returns the value of FinalizedEpoch. -func (s *ForkChoiceNode) GetFinalizedEpoch() EpochString { +func (s *ForkChoiceNode) GetFinalizedEpoch() OptEpochString { return s.FinalizedEpoch } @@ -263,6 +305,31 @@ func (s *ForkChoiceNode) GetExecutionBlockHash() Root { return s.ExecutionBlockHash } +// GetPayloadStatus returns the value of PayloadStatus. +func (s *ForkChoiceNode) GetPayloadStatus() OptPayloadStatus { + return s.PayloadStatus +} + +// GetParentPayloadStatus returns the value of ParentPayloadStatus. +func (s *ForkChoiceNode) GetParentPayloadStatus() OptPayloadStatus { + return s.ParentPayloadStatus +} + +// GetPayloadAttesterCount returns the value of PayloadAttesterCount. +func (s *ForkChoiceNode) GetPayloadAttesterCount() OptString { + return s.PayloadAttesterCount +} + +// GetPayloadAvailabilityYesCount returns the value of PayloadAvailabilityYesCount. +func (s *ForkChoiceNode) GetPayloadAvailabilityYesCount() OptString { + return s.PayloadAvailabilityYesCount +} + +// GetPayloadDataAvailabilityYesCount returns the value of PayloadDataAvailabilityYesCount. +func (s *ForkChoiceNode) GetPayloadDataAvailabilityYesCount() OptString { + return s.PayloadDataAvailabilityYesCount +} + // GetExtraData returns the value of ExtraData. func (s *ForkChoiceNode) GetExtraData() OptForkChoiceNodeExtraData { return s.ExtraData @@ -284,12 +351,12 @@ func (s *ForkChoiceNode) SetParentRoot(val Root) { } // SetJustifiedEpoch sets the value of JustifiedEpoch. -func (s *ForkChoiceNode) SetJustifiedEpoch(val EpochString) { +func (s *ForkChoiceNode) SetJustifiedEpoch(val OptEpochString) { s.JustifiedEpoch = val } // SetFinalizedEpoch sets the value of FinalizedEpoch. -func (s *ForkChoiceNode) SetFinalizedEpoch(val EpochString) { +func (s *ForkChoiceNode) SetFinalizedEpoch(val OptEpochString) { s.FinalizedEpoch = val } @@ -308,6 +375,31 @@ func (s *ForkChoiceNode) SetExecutionBlockHash(val Root) { s.ExecutionBlockHash = val } +// SetPayloadStatus sets the value of PayloadStatus. +func (s *ForkChoiceNode) SetPayloadStatus(val OptPayloadStatus) { + s.PayloadStatus = val +} + +// SetParentPayloadStatus sets the value of ParentPayloadStatus. +func (s *ForkChoiceNode) SetParentPayloadStatus(val OptPayloadStatus) { + s.ParentPayloadStatus = val +} + +// SetPayloadAttesterCount sets the value of PayloadAttesterCount. +func (s *ForkChoiceNode) SetPayloadAttesterCount(val OptString) { + s.PayloadAttesterCount = val +} + +// SetPayloadAvailabilityYesCount sets the value of PayloadAvailabilityYesCount. +func (s *ForkChoiceNode) SetPayloadAvailabilityYesCount(val OptString) { + s.PayloadAvailabilityYesCount = val +} + +// SetPayloadDataAvailabilityYesCount sets the value of PayloadDataAvailabilityYesCount. +func (s *ForkChoiceNode) SetPayloadDataAvailabilityYesCount(val OptString) { + s.PayloadDataAvailabilityYesCount = val +} + // SetExtraData sets the value of ExtraData. func (s *ForkChoiceNode) SetExtraData(val OptForkChoiceNodeExtraData) { s.ExtraData = val @@ -1162,6 +1254,98 @@ func (o OptEpoch) Or(d Epoch) Epoch { return d } +// NewOptEpochString returns new OptEpochString with value set to v. +func NewOptEpochString(v EpochString) OptEpochString { + return OptEpochString{ + Value: v, + Set: true, + } +} + +// OptEpochString is optional EpochString. +type OptEpochString struct { + Value EpochString + Set bool +} + +// IsSet returns true if OptEpochString was set. +func (o OptEpochString) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptEpochString) Reset() { + var v EpochString + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptEpochString) SetTo(v EpochString) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptEpochString) Get() (v EpochString, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptEpochString) Or(d EpochString) EpochString { + if v, ok := o.Get(); ok { + return v + } + return d +} + +// NewOptForkChoiceExtraData returns new OptForkChoiceExtraData with value set to v. +func NewOptForkChoiceExtraData(v ForkChoiceExtraData) OptForkChoiceExtraData { + return OptForkChoiceExtraData{ + Value: v, + Set: true, + } +} + +// OptForkChoiceExtraData is optional ForkChoiceExtraData. +type OptForkChoiceExtraData struct { + Value ForkChoiceExtraData + Set bool +} + +// IsSet returns true if OptForkChoiceExtraData was set. +func (o OptForkChoiceExtraData) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptForkChoiceExtraData) Reset() { + var v ForkChoiceExtraData + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptForkChoiceExtraData) SetTo(v ForkChoiceExtraData) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptForkChoiceExtraData) Get() (v ForkChoiceExtraData, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptForkChoiceExtraData) Or(d ForkChoiceExtraData) ForkChoiceExtraData { + if v, ok := o.Get(); ok { + return v + } + return d +} + // NewOptForkChoiceNodeExtraData returns new OptForkChoiceNodeExtraData with value set to v. func NewOptForkChoiceNodeExtraData(v ForkChoiceNodeExtraData) OptForkChoiceNodeExtraData { return OptForkChoiceNodeExtraData{ @@ -1346,6 +1530,52 @@ func (o OptPaginationCursor) Or(d PaginationCursor) PaginationCursor { return d } +// NewOptPayloadStatus returns new OptPayloadStatus with value set to v. +func NewOptPayloadStatus(v PayloadStatus) OptPayloadStatus { + return OptPayloadStatus{ + Value: v, + Set: true, + } +} + +// OptPayloadStatus is optional PayloadStatus. +type OptPayloadStatus struct { + Value PayloadStatus + Set bool +} + +// IsSet returns true if OptPayloadStatus was set. +func (o OptPayloadStatus) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptPayloadStatus) Reset() { + var v PayloadStatus + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptPayloadStatus) SetTo(v PayloadStatus) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptPayloadStatus) Get() (v PayloadStatus, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptPayloadStatus) Or(d PayloadStatus) PayloadStatus { + if v, ok := o.Get(); ok { + return v + } + return d +} + // NewOptSlot returns new OptSlot with value set to v. func NewOptSlot(v Slot) OptSlot { return OptSlot{ @@ -1483,6 +1713,58 @@ func (s *PaginationResponse) SetTotal(val int64) { s.Total = val } +// The Gloas payload status of a fork-choice node. A Gloas block has a +// `pending` node, which is the parent of its `empty` node (the block +// without its execution payload) and its `full` node (the block with it). +// Ref: #/components/schemas/PayloadStatus +type PayloadStatus string + +const ( + PayloadStatusPending PayloadStatus = "pending" + PayloadStatusEmpty PayloadStatus = "empty" + PayloadStatusFull PayloadStatus = "full" +) + +// AllValues returns all PayloadStatus values. +func (PayloadStatus) AllValues() []PayloadStatus { + return []PayloadStatus{ + PayloadStatusPending, + PayloadStatusEmpty, + PayloadStatusFull, + } +} + +// MarshalText implements encoding.TextMarshaler. +func (s PayloadStatus) MarshalText() ([]byte, error) { + switch s { + case PayloadStatusPending: + return []byte(s), nil + case PayloadStatusEmpty: + return []byte(s), nil + case PayloadStatusFull: + return []byte(s), nil + default: + return nil, errors.Errorf("invalid value: %q", s) + } +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (s *PayloadStatus) UnmarshalText(data []byte) error { + switch PayloadStatus(data) { + case PayloadStatusPending: + *s = PayloadStatusPending + return nil + case PayloadStatusEmpty: + *s = PayloadStatusEmpty + return nil + case PayloadStatusFull: + *s = PayloadStatusFull + return nil + default: + return errors.Errorf("invalid value: %q", data) + } +} + type Root string type Slot int64 diff --git a/api/rest/oas_validators_gen.go b/api/rest/oas_validators_gen.go index 9610601..9fbb37e 100644 --- a/api/rest/oas_validators_gen.go +++ b/api/rest/oas_validators_gen.go @@ -118,6 +118,42 @@ func (s *ForkChoiceNode) Validate() error { Error: err, }) } + if err := func() error { + if value, ok := s.PayloadStatus.Get(); ok { + if err := func() error { + if err := value.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return err + } + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "payload_status", + Error: err, + }) + } + if err := func() error { + if value, ok := s.ParentPayloadStatus.Get(); ok { + if err := func() error { + if err := value.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return err + } + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "parent_payload_status", + Error: err, + }) + } if len(failures) > 0 { return &validate.Error{Fields: failures} } @@ -784,6 +820,19 @@ func (s *MetadataQuery) Validate() error { return nil } +func (s PayloadStatus) Validate() error { + switch s { + case "pending": + return nil + case "empty": + return nil + case "full": + return nil + default: + return errors.Errorf("invalid value: %v", s) + } +} + func (s Slot) Validate() error { alias := (int64)(s) if err := (validate.Int{ diff --git a/go.mod b/go.mod index a58606e..337dde9 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,34 @@ module github.com/ethpandaops/forky -go 1.26.2 +go 1.26.8 replace github.com/probe-lab/hermes => github.com/ethpandaops/hermes v0.0.4-0.20250715094801-db6021d1c703 -replace github.com/attestantio/go-eth2-client => github.com/attestantio/go-eth2-client v0.26.1-0.20250721122214-dc2928832acc - require ( - github.com/attestantio/go-eth2-client v0.26.0 - github.com/aws/aws-sdk-go-v2 v1.36.3 - github.com/aws/aws-sdk-go-v2/credentials v1.17.62 - github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 - github.com/aws/smithy-go v1.22.3 + github.com/aws/aws-sdk-go-v2 v1.39.6 + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 + github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4 + github.com/aws/smithy-go v1.23.2 github.com/cenkalti/backoff/v4 v4.3.0 github.com/creasty/defaults v1.8.0 github.com/ethpandaops/ethwallclock v0.4.0 - github.com/ethpandaops/xatu v1.3.2 + github.com/ethpandaops/xatu v1.24.4-0.20261008044555-a71a264dc8d7 github.com/glebarez/sqlite v1.10.0 github.com/go-co-op/gocron v1.37.0 github.com/go-faster/errors v0.7.1 github.com/go-faster/jx v1.2.0 github.com/google/uuid v1.6.0 - github.com/jellydator/ttlcache/v3 v3.4.0 + github.com/jellydator/ttlcache/v3 v3.4.1 github.com/ogen-go/ogen v1.20.3 - github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/client_golang v1.24.1 github.com/rs/zerolog v1.34.0 - github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.9.1 - github.com/stretchr/testify v1.11.1 - go.opentelemetry.io/otel v1.42.0 - go.opentelemetry.io/otel/metric v1.42.0 - go.opentelemetry.io/otel/trace v1.42.0 - google.golang.org/protobuf v1.36.6 + github.com/sirupsen/logrus v1.9.4 + github.com/spf13/cobra v1.10.1 + github.com/stretchr/testify v1.12.1 + go.opentelemetry.io/otel v1.46.0 + go.opentelemetry.io/otel/metric v1.46.0 + go.opentelemetry.io/otel/trace v1.46.0 + google.golang.org/protobuf v1.36.12 gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/postgres v1.5.4 @@ -40,188 +37,300 @@ require ( ) require ( - github.com/OffchainLabs/prysm/v6 v6.0.5-0.20250610223352-6087875da526 // indirect + cuelang.org/go v0.15.3 // indirect + filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 // indirect + filippo.io/keygen v1.0.0 // indirect + github.com/ClickHouse/ch-go v0.71.0 // indirect + github.com/IBM/sarama v1.46.2 // indirect + github.com/Jeffail/checkpoint v1.1.0 // indirect + github.com/Jeffail/gabs/v2 v2.7.0 // indirect + github.com/Jeffail/grok v1.1.0 // indirect + github.com/Jeffail/shutdown v1.1.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/OffchainLabs/go-bitfield v0.0.0-20260504143531-5cbb6d0f5f2e // indirect + github.com/OffchainLabs/prysm/v6 v6.1.4 // indirect + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/VictoriaMetrics/fastcache v1.13.0 // indirect github.com/andybalholm/brotli v1.2.0 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.9 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect - github.com/aws/aws-sdk-go-v2/service/kinesis v1.33.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect + github.com/avast/retry-go/v4 v4.6.1 // indirect + github.com/beevik/ntp v1.4.3 // indirect github.com/benbjohnson/clock v1.3.5 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/containerd/cgroups v1.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/bits-and-blooms/bitset v1.24.4 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/consensys/gnark-crypto v0.18.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect + github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/dennis-tra/go-kinesis v0.0.0-20240326083914-7acf5f8dc24e // indirect - github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/elastic/gosigar v0.14.3 // indirect - github.com/emicklei/dot v1.8.0 // indirect - github.com/ethereum/go-ethereum v1.16.1 // indirect - github.com/fatih/color v1.19.0 // indirect - github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dmarkham/enumer v1.6.3 // indirect + github.com/dunglas/httpsfv v1.1.1 // indirect + github.com/eapache/go-resiliency v1.7.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/emicklei/dot v1.9.0 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.8 // indirect + github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect + github.com/ethpandaops/beacon v0.71.0 // indirect + github.com/ethpandaops/ethcore v0.0.0-20260807105509-b0a20c27b6e7 // indirect + github.com/failsafe-go/failsafe-go v0.9.6 // indirect + github.com/felixge/httpsnoop v1.1.0 // indirect + github.com/ferranbt/fastssz v1.0.0 // indirect + github.com/filecoin-project/go-clock v0.1.0 // indirect + github.com/fjl/jsonw v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/glebarez/go-sqlite v1.22.0 // indirect - github.com/go-faster/yaml v0.4.6 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/goccy/go-yaml v1.17.1 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-co-op/gocron/v2 v2.22.0 // indirect + github.com/go-faster/city v1.0.1 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-redsync/redsync/v4 v4.13.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/gofrs/uuid/v5 v5.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/snappy v1.0.1-0.20260716114414-9ae09f520e93 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/govalues/decimal v0.1.36 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/holiman/uint256 v1.3.2 // indirect - github.com/huandu/go-clone v1.7.3 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/huandu/go-assert v1.1.6 // indirect + github.com/huandu/go-sqlbuilder v1.36.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/huin/goupnp v1.3.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/go-cid v0.5.0 // indirect - github.com/ipfs/go-log/v2 v2.6.0 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect - github.com/jackc/pgx/v5 v5.5.2 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/influxdata/go-syslog/v3 v3.0.0 // indirect + github.com/influxdata/tdigest v0.0.1 // indirect + github.com/ipfs/go-cid v0.6.2 // indirect + github.com/ipfs/go-log/v2 v2.8.0 // indirect + github.com/itchyny/gojq v0.12.18 // indirect + github.com/itchyny/timefmt-go v0.1.7 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.18.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect - github.com/koron/go-ssdp v0.0.6 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect + github.com/kilic/bls12-381 v0.1.0 // indirect + github.com/klauspost/compress v1.19.1 // indirect + github.com/klauspost/crc32 v1.3.0 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect + github.com/koron/go-ssdp v0.9.1 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-flow-metrics v0.2.0 // indirect - github.com/libp2p/go-libp2p v0.41.1 // indirect + github.com/libp2p/go-flow-metrics v0.3.0 // indirect + github.com/libp2p/go-libp2p v0.50.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-pubsub v0.13.1 // indirect + github.com/libp2p/go-libp2p-mplex v0.11.0 // indirect + github.com/libp2p/go-mplex v0.7.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect - github.com/libp2p/go-netroute v0.2.2 // indirect + github.com/libp2p/go-netroute v0.4.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect - github.com/libp2p/go-yamux/v5 v5.0.0 // indirect + github.com/libp2p/go-yamux/v5 v5.1.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/miekg/dns v1.1.66 // indirect + github.com/matoous/go-nanoid/v2 v2.1.0 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/crc64nvme v1.1.1 // indirect + github.com/minio/highwayhash v1.0.3 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio-go/v7 v7.1.0 // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/mr-tron/base58 v1.3.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.16.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr v0.16.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.6.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multibase v0.3.0 // indirect + github.com/multiformats/go-multicodec v0.10.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect - github.com/multiformats/go-multistream v0.6.0 // indirect - github.com/multiformats/go-varint v0.0.7 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/ginkgo/v2 v2.23.4 // indirect - github.com/opencontainers/runtime-spec v1.2.1 // indirect - github.com/parquet-go/parquet-go v0.25.0 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect + github.com/multiformats/go-varint v0.1.0 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 // indirect + github.com/oschwald/maxminddb-golang v1.13.1 // indirect + github.com/parquet-go/bitpack v1.0.0 // indirect + github.com/parquet-go/jsonlite v1.0.0 // indirect + github.com/parquet-go/parquet-go v0.30.1 // indirect + github.com/pascaldekloe/name v1.0.1 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/pierrec/lz4/v4 v4.1.25 // indirect github.com/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v2 v2.2.12 // indirect - github.com/pion/dtls/v3 v3.0.6 // indirect + github.com/pion/dtls/v3 v3.1.4 // indirect github.com/pion/ice/v4 v4.0.10 // indirect - github.com/pion/interceptor v0.1.37 // indirect - github.com/pion/logging v0.2.3 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.4 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.15 // indirect + github.com/pion/rtcp v1.2.16 // indirect + github.com/pion/rtp v1.8.21 // indirect github.com/pion/sctp v1.8.39 // indirect - github.com/pion/sdp/v3 v3.0.11 // indirect - github.com/pion/srtp/v3 v3.0.4 // indirect - github.com/pion/stun v0.6.1 // indirect - github.com/pion/stun/v3 v3.0.0 // indirect - github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/sdp/v3 v3.0.18 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun/v3 v3.1.5 // indirect github.com/pion/transport/v3 v3.0.7 // indirect - github.com/pion/turn/v4 v4.0.1 // indirect - github.com/pion/webrtc/v4 v4.1.0 // indirect - github.com/pk910/dynamic-ssz v0.0.6 // indirect + github.com/pion/transport/v4 v4.0.2 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect + github.com/pk910/hashtree-bindings v0.2.6 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/protolambda/bls12-381-util v0.1.0 // indirect + github.com/protolambda/zrnt v0.34.1 // indirect + github.com/protolambda/ztyp v0.2.2 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20260421202104-7a6eb71e6e45 // indirect + github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 // indirect + github.com/prysmaticlabs/gohashtree v0.0.5-beta // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.62.0 // indirect + github.com/quic-go/webtransport-go v0.13.0 // indirect + github.com/quipo/dependencysolver v0.0.0-20170801134659-2b009cb4ddcc // indirect + github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect + github.com/redis/go-redis/v9 v9.14.0 // indirect + github.com/redpanda-data/benthos/v4 v4.64.1 // indirect + github.com/redpanda-data/connect/v4 v4.80.1 // indirect + github.com/rickb777/period v1.0.22 // indirect + github.com/rickb777/plural v1.4.7 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/segmentio/ksuid v1.0.4 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/sony/gobreaker/v2 v2.1.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/supranational/blst v0.3.16 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect + github.com/tilinna/z85 v1.0.0 // indirect + github.com/tinylib/msgp v1.6.1 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect + github.com/twmb/franz-go v1.20.6 // indirect + github.com/twmb/franz-go/pkg/kadm v1.17.1 // indirect + github.com/twmb/franz-go/pkg/kmsg v1.12.0 // indirect + github.com/twmb/franz-go/pkg/sr v1.6.0 // indirect + github.com/twpayne/go-geom v1.6.1 // indirect + github.com/urfave/cli/v2 v2.27.7 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zeebo/xxh3 v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0 // indirect + go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect + go.uber.org/mock v0.6.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260819154853-08b0e4226688 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.4.1 // indirect +) + +require ( + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ethereum/go-ethereum v1.17.6 // indirect + github.com/ethpandaops/go-eth2-client v0.1.10 + github.com/fatih/color v1.19.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect + github.com/go-faster/yaml v0.4.6 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/google/pprof v0.0.0-20251002213607-436353cc1ee6 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/huandu/go-clone v1.7.3 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.6 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/cpuid/v2 v2.4.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/probe-lab/hermes v0.0.0-20250328140724-f552d3382c38 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516 // indirect - github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 // indirect - github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.51.0 // indirect - github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect + github.com/prometheus/common v0.70.1 // indirect + github.com/prometheus/procfs v0.21.1 // indirect github.com/r3labs/sse/v2 v2.10.0 // indirect - github.com/raulk/go-watchdog v1.3.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/pflag v1.0.6 // indirect - github.com/thejerf/suture/v4 v4.0.6 // indirect - github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect - github.com/wlynxg/anet v0.0.5 // indirect + github.com/spf13/pflag v1.0.10 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect - go.opentelemetry.io/otel/sdk v1.42.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/dig v1.18.2 // indirect - go.uber.org/fx v1.23.0 // indirect - go.uber.org/mock v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.1 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect - golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.42.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect - google.golang.org/grpc v1.72.1 // indirect - gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect + go.uber.org/zap v1.28.0 // indirect + golang.org/x/crypto v0.56.0 // indirect + golang.org/x/exp v0.0.0-20260718201538-764159d718ef // indirect + golang.org/x/mod v0.39.0 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/tools v0.49.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect + google.golang.org/grpc v1.83.2 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.4.1 // indirect - modernc.org/libc v1.40.7 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/sqlite v1.28.0 // indirect + modernc.org/libc v1.66.10 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.39.1 // indirect ) tool github.com/ogen-go/ogen/cmd/ogen diff --git a/go.sum b/go.sum index 645672a..204e44c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -33,16 +31,39 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +cuelang.org/go v0.15.3 h1:JKR/lZVwuIGlLTGIaJ0jONz9+CK3UDx06sQ6DDxNkaE= +cuelang.org/go v0.15.3/go.mod h1:NYw6n4akZcTjA7QQwJ1/gqWrrhsN4aZwhcAL0jv9rZE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 h1:JA0fFr+kxpqTdxR9LOBiTWpGNchqmkcsgmdeJZRclZ0= +filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI= +filippo.io/keygen v1.0.0 h1:u0/Fhxlgz3uPv+XxhfgTq3BJt5VesIPM5ue/OuG7qjQ= +filippo.io/keygen v1.0.0/go.mod h1:9nnw1SlYHYuPSo/3wjQzNjSbeHlq2NsKo5iEtfJPWP0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OffchainLabs/prysm/v6 v6.0.5-0.20250610223352-6087875da526 h1:gDCuYZ1lOBLNhUVGuOuGZDB9a+tlDAgZIRB/X6bWl1s= -github.com/OffchainLabs/prysm/v6 v6.0.5-0.20250610223352-6087875da526/go.mod h1:ROlX1RFSH7sCgQ2fd2CC1QCBWacEvpvLsPS10qKJu7g= +github.com/ClickHouse/ch-go v0.71.0 h1:bUdZ/EZj/LcVHsMqaRUP2holqygrPWQKeMjc6nZoyRM= +github.com/ClickHouse/ch-go v0.71.0/go.mod h1:NwbNc+7jaqfY58dmdDUbG4Jl22vThgx1cYjBw0vtgXw= +github.com/IBM/sarama v1.46.2 h1:65JJmZpxKUWe/7HEHmc56upTfAvgoxuyu4Ek+TcevDE= +github.com/IBM/sarama v1.46.2/go.mod h1:PDOGmVeKmW744c/0d4CZ0MfrzmcIYtpmS5+KIWs1zHQ= +github.com/Jeffail/checkpoint v1.1.0 h1:xcjJV5Eli6hFwD5r0WDhqszNniWDlGRjbiWCY+CE994= +github.com/Jeffail/checkpoint v1.1.0/go.mod h1:wzqZ22J7jgpf+sNf6Um6xn7ufB/ashFkkSVu9anzmSY= +github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= +github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= +github.com/Jeffail/grok v1.1.0 h1:kiHmZ+0J5w/XUihRgU3DY9WIxKrNQCDjnfAb6bMLFaE= +github.com/Jeffail/grok v1.1.0/go.mod h1:dm0hLksrDwOMa6To7ORXCuLbuNtASIZTfYheavLpsuE= +github.com/Jeffail/shutdown v1.1.0 h1:5Bm3llKt0hnRjmTUlxgBnFg/snFfwqTOUMp3So8jCLo= +github.com/Jeffail/shutdown v1.1.0/go.mod h1:5dT4Y1oe60SJELCkmAB1pr9uQyHBhh6cwDLQTfmuO5U= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/OffchainLabs/go-bitfield v0.0.0-20260504143531-5cbb6d0f5f2e h1:M9Ia6En5b8/imySo4xQeeoJPG2tOOJEKk/GnfBI86Hk= +github.com/OffchainLabs/go-bitfield v0.0.0-20260504143531-5cbb6d0f5f2e/go.mod h1:6TZI4FU6zT8x6ZfWa1J8YQ2NgW0wLV/W3fHRca8ISBo= +github.com/OffchainLabs/prysm/v6 v6.1.4 h1:gwLBbGaYsEh9zpZL337uVjXPX3F86UNJ7KIiFiPsY7c= +github.com/OffchainLabs/prysm/v6 v6.1.4/go.mod h1:VbMsULGbZkgM4kwbKxb/ywhhR/EtebwGPIAszIU59Ws= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= +github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -53,142 +74,159 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/attestantio/go-eth2-client v0.26.1-0.20250721122214-dc2928832acc h1:rLrx/o7WeRqmE9iXvhyBMDrugOPZRhNBLQD/4XbG6hU= -github.com/attestantio/go-eth2-client v0.26.1-0.20250721122214-dc2928832acc/go.mod h1:fvULSL9WtNskkOB4i+Yyr6BKpNHXvmpGZj9969fCrfY= -github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= -github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= -github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0= -github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U= -github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= -github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.33.1 h1:tv91hjCds3xbPR5jZcdNvUbqrMGZF3WdfqQc+mlDZgc= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.33.1/go.mod h1:dJngkoVMrq0K7QvRkdRZYM4NUp6cdWa2GBdpm8zoY8U= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 h1:jIiopHEV22b4yQP2q36Y0OmwLbsxNWdWwfZRR5QRRO4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= -github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= -github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= +github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA= +github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= +github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9 h1:w9LnHqTq8MEdlnyhV4Bwfizd65lfNCNgdlNC6mM5paE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.9/go.mod h1:LGEP6EK4nj+bwWNdrvX/FnDTFowdBNwcSPuZu/ouFys= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0 h1:X0FveUndcZ3lKbSpIC6rMYGRiQTcUVRNH6X4yYtIrlU= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.0/go.mod h1:IWjQYlqw4EX9jw2g3qnEPPWvCE6bS8fKzhMed1OK7c8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9 h1:wuZ5uW2uhJR63zwNlqWH2W4aL4ZjeJP3o92/W+odDY4= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.9/go.mod h1:/G58M2fGszCrOzvJUkDdY8O9kycodunH4VdT5oBAqls= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4 h1:mUI3b885qJgfqKDUSj6RgbRqLdX0wGmg8ruM03zNfQA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.88.4/go.mod h1:6v8ukAxc7z4x4oBjGUsLnH7KGLY9Uhcgij19UJNkiMg= +github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= +github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= +github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= +github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4= +github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI= +github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc= +github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= -github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= -github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dennis-tra/go-kinesis v0.0.0-20240326083914-7acf5f8dc24e h1:y6QyYh8YZyRQDdfnQqUgC5tRBmEwUFAjavnybKboCm4= -github.com/dennis-tra/go-kinesis v0.0.0-20240326083914-7acf5f8dc24e/go.mod h1:5Hm3EOeNP1/lYm9qcFwWgYgjixQilwcZA+hZ05bUz54= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dmarkham/enumer v1.6.3 h1:B4aV4OsfzbrS5rvjILt4mMjiWBA//cKxJUMsvHZ8mEI= +github.com/dmarkham/enumer v1.6.3/go.mod h1:DyjXaqCglj4GhELF73oWiparNkYkXvmOBLza/o4kO74= +github.com/dunglas/httpsfv v1.1.1 h1:HoSs101zIE9I23DlqlmljJ/OIi7ILwrH347pXhRZdxI= +github.com/dunglas/httpsfv v1.1.1/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= -github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/emicklei/dot v1.8.0 h1:HnD60yAKFAevNeT+TPYr9pb8VB9bqdeSo0nzwIW6IOI= -github.com/emicklei/dot v1.8.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= +github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/emicklei/dot v1.9.0 h1:FyaJNctdMfaEIbTQ1FkKZ1UCZyJJSkyvkrXOVoNZPKU= +github.com/emicklei/dot v1.9.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.16.1 h1:7684NfKCb1+IChudzdKyZJ12l1Tq4ybPZOITiCDXqCk= -github.com/ethereum/go-ethereum v1.16.1/go.mod h1:ngYIvmMAYdo4sGW9cGzLvSsPGhDOOzL0jK5S5iXpj0g= +github.com/ethereum/c-kzg-4844/v2 v2.1.8 h1:oQ48q/TMe2SKU8qBE3N7e4/HlG3EpJftom6EsPQgJ58= +github.com/ethereum/c-kzg-4844/v2 v2.1.8/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= +github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= +github.com/ethereum/go-ethereum v1.17.6 h1:27mdzjoN/bjz+rgjjZPGnD6E44W/Nd+vG+FKQFd/heg= +github.com/ethereum/go-ethereum v1.17.6/go.mod h1:nl9wZjMuIjAottU6bq82UihXPbyY0jHHwkYXhnYhmU4= +github.com/ethpandaops/beacon v0.71.0 h1:tWAbQ187sfWiRWBywkeYynxL1q/SeUu+9vad6WyaI5w= +github.com/ethpandaops/beacon v0.71.0/go.mod h1:ePrsXdiJ7e7JqXUrlV6tsEXv3iPT9NJfssxU8MNm/Us= +github.com/ethpandaops/ethcore v0.0.0-20260807105509-b0a20c27b6e7 h1:BpbY4wxDlLH+adeEB6SzXyGmYOWWK4kc3z1u+PJa2cg= +github.com/ethpandaops/ethcore v0.0.0-20260807105509-b0a20c27b6e7/go.mod h1:my+mlolmidi2VzoaGhchtcxDVbp1ZNVQJtnkk3oe6+M= github.com/ethpandaops/ethwallclock v0.4.0 h1:+sgnhf4pk6hLPukP076VxkiLloE4L0Yk1yat+ZyHh1g= github.com/ethpandaops/ethwallclock v0.4.0/go.mod h1:y0Cu+mhGLlem19vnAV2x0hpFS5KZ7oOi2SWYayv9l24= -github.com/ethpandaops/hermes v0.0.4-0.20250715094801-db6021d1c703 h1:kgbDt5VGaukBMVRFlNOKKCwYyJp7rt9ZVhom/MY8sOA= -github.com/ethpandaops/hermes v0.0.4-0.20250715094801-db6021d1c703/go.mod h1:btvQ5XIwNLN1xKq0/G4HE+wxa7C93IFnxqfkM4PjMZA= -github.com/ethpandaops/xatu v1.3.2 h1:2ieq1Db9GoAFngv6u/HCWo8U3Cz6BAAqY2UPF+7Macg= -github.com/ethpandaops/xatu v1.3.2/go.mod h1:kld9Upqa0DRoNNyKzufXpYvY1YAnEU3s4ATJb1MaY94= +github.com/ethpandaops/go-eth2-client v0.1.9-0.20261007031839-0b35f0981d55 h1:W1Tc0Vd2JTQheYcKhTQJUzjJgdSqq9GTd1aTfj2f8jE= +github.com/ethpandaops/go-eth2-client v0.1.9-0.20261007031839-0b35f0981d55/go.mod h1:sIVbaug0GsltsJS+hj8ID4dmWBIt8TXoNIesvuBCGt0= +github.com/ethpandaops/go-eth2-client v0.1.10 h1:AGROjkS5jMkveL1E46wyWNv5JLJNWWyZE/hIVBibIrg= +github.com/ethpandaops/go-eth2-client v0.1.10/go.mod h1:sIVbaug0GsltsJS+hj8ID4dmWBIt8TXoNIesvuBCGt0= +github.com/ethpandaops/xatu v1.24.4-0.20261007033258-20c9c651e62c h1:BqblsGPJvC9hhmkMlS7PTgnJXO/wR0u05+RwAb2Mh+A= +github.com/ethpandaops/xatu v1.24.4-0.20261007033258-20c9c651e62c/go.mod h1:dUHUlJktAVaFBPN6zYSWgakJov/jcnpRYSZzGQwq8ug= +github.com/ethpandaops/xatu v1.24.4-0.20261008044555-a71a264dc8d7 h1:KcSI+WZ/kbEEGQIc5L6pqTGM/3QGJomPQrPZSI1GM3I= +github.com/ethpandaops/xatu v1.24.4-0.20261008044555-a71a264dc8d7/go.mod h1:ntnC7nu9jGEguwMSn/36cfwYQZMgtku/AuMgkma03xs= +github.com/failsafe-go/failsafe-go v0.9.6 h1:vPSH2cry0Ee5cnR9wc9qshCDO6jdrMA9elBJNwyo4Uk= +github.com/failsafe-go/failsafe-go v0.9.6/go.mod h1:IeRpglkcwzKagjDMh90ZhN2l4Ovt3+jemQBUbThag54= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= -github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= +github.com/ferranbt/fastssz v1.0.0 h1:9EXXYsracSqQRBQiHeaVsG/KQeYblPf40hsQPb9Dzk8= +github.com/ferranbt/fastssz v1.0.0/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= +github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= +github.com/fjl/jsonw v0.1.0 h1:V3MyR79fjLpn/+bMgvegdGUIhoJOzjmqWcKDgcOmY1I= +github.com/fjl/jsonw v0.1.0/go.mod h1:2KMLevM6FXEJnfhtk7naXu9vZdVfOma1GlnGdPRlumU= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-co-op/gocron/v2 v2.22.0 h1:uEuH2F7k7VoESb1BYSaffuuV+T0kkpzsC0aXk7/z79I= +github.com/go-co-op/gocron/v2 v2.22.0/go.mod h1:hiH/U9RMhTi1BBZJmef9s3KC9QwhpBF6PFrvUKaXY9M= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= github.com/go-faster/jx v1.2.0 h1:T2YHJPrFaYu21fJtUxC9GzmluKu8rVIFDwwGBKTDseI= @@ -198,6 +236,8 @@ github.com/go-faster/yaml v0.4.6/go.mod h1:390dRIvV4zbnO7qC9FGo6YYutc+wyyUSHBgbX github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -208,28 +248,33 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA= +github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= -github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0= +github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -256,8 +301,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= -github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.1-0.20260716114414-9ae09f520e93 h1:GpQQr4L8jsBtJSURCDqQboOdgpVMU6vR9REjc8nR4Qc= +github.com/golang/snappy v1.0.1-0.20260716114414-9ae09f520e93/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -267,18 +313,13 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -288,65 +329,117 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= -github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20251002213607-436353cc1ee6 h1:/WHh/1k4thM/w+PAZEIiZK9NwCMFahw5tUzKUCnUtds= +github.com/google/pprof v0.0.0-20251002213607-436353cc1ee6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/govalues/decimal v0.1.36 h1:dojDpsSvrk0ndAx8+saW5h9WDIHdWpIwrH/yhl9olyU= +github.com/govalues/decimal v0.1.36/go.mod h1:Ee7eI3Llf7hfqDZtpj8Q6NCIgJy1iY3kH1pSwDrNqlM= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ= +github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= -github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/go-assert v1.1.6 h1:oaAfYxq9KNDi9qswn/6aE0EydfxSa+tWZC1KabNitYs= +github.com/huandu/go-assert v1.1.6/go.mod h1:JuIfbmYG9ykwvuxoJ3V8TB5QP+3+ajIA54Y44TmkMxs= github.com/huandu/go-clone v1.7.3 h1:rtQODA+ABThEn6J5LBTppJfKmZy/FwfpMUWa8d01TTQ= github.com/huandu/go-clone v1.7.3/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE= github.com/huandu/go-clone/generic v1.6.0 h1:Wgmt/fUZ28r16F2Y3APotFD59sHk1p78K0XLdbUYN5U= github.com/huandu/go-clone/generic v1.6.0/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= +github.com/huandu/go-sqlbuilder v1.36.0 h1:Bn64Exk1izEBbQ8uDC0YHoFxebMyVRBHw3Rwt25g9EE= +github.com/huandu/go-sqlbuilder v1.36.0/go.mod h1:59Zjq93ndlKI6O5kHmkXQpDgriBAPMKuhByEOy6xYK8= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= -github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= -github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= -github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= +github.com/influxdata/go-syslog/v3 v3.0.0 h1:jichmjSZlYK0VMmlz+k4WeOQd7z745YLsvGMqwtYt4I= +github.com/influxdata/go-syslog/v3 v3.0.0/go.mod h1:tulsOp+CecTAYC27u9miMgq21GqXRW6VdKbOG+QSP4Q= +github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= +github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= +github.com/ipfs/go-cid v0.6.2 h1:VuGwJd+KJTaMJ4S4d5EEf9SXc17YUblS5axCbocn9YE= +github.com/ipfs/go-cid v0.6.2/go.mod h1:Xhwg8NzHeK9xPCEZkCw4idzPiuNMpX3fARuI5Iwj1Lo= +github.com/ipfs/go-log/v2 v2.8.0 h1:SptNTPJQV3s5EF4FdrTu/yVdOKfGbDgn1EBZx4til2o= +github.com/ipfs/go-log/v2 v2.8.0/go.mod h1:2LEEhdv8BGubPeSFTyzbqhCqrwqxCbuTNTLWqgNAipo= +github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= +github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= +github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= +github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA= -github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= +github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= -github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jellydator/ttlcache/v3 v3.4.1 h1:bOdXmXiycyK6E6Qjyuj5vl+/vU3SCOoDs8a86NbHjAQ= +github.com/jellydator/ttlcache/v3 v3.4.1/go.mod h1:j7LO12PNghFg5+0v9budMAT4rDK4JY969jb9vOdOBBk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -356,17 +449,23 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= +github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= +github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= +github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= +github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= -github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= +github.com/koron/go-ssdp v0.9.1 h1:zvxbAAuJftJIZ8Jh8mda+LI7V92hYZf/sKprmOxpxwA= +github.com/koron/go-ssdp v0.9.1/go.mod h1:C43c047jWkDaeg9YuZlSh/QGqOieuWV6dbhWi/jcaLk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -374,61 +473,66 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= -github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= -github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQZE= -github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= +github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= +github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= +github.com/libp2p/go-libp2p v0.50.0 h1:A0tBP6mr6GV7l5ip2Q04a/IxPqVYYaCiif5s8JuXkN4= +github.com/libp2p/go-libp2p v0.50.0/go.mod h1:RjqB+dxCWNZ33yw0yeK5Pu157oNxKuaTZY6vLed4t8w= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-pubsub v0.13.1 h1:tV3ttzzZSCk0EtEXnxVmWIXgjVxXx+20Jwjbs/Ctzjo= -github.com/libp2p/go-libp2p-pubsub v0.13.1/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= -github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= -github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-libp2p-mplex v0.11.0 h1:0vwpLXRSfkTzshEjETIEgJaVxXvg+orbxYoIb3Ty5qM= +github.com/libp2p/go-libp2p-mplex v0.11.0/go.mod h1:QrsdNY3lzjpdo9V1goJfPb0O65Nms0sUR8CDAO18f6k= +github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= +github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8= -github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE= +github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= +github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= -github.com/libp2p/go-yamux/v5 v5.0.0 h1:2djUh96d3Jiac/JpGkKs4TO49YhsfLopAoryfPmf+Po= -github.com/libp2p/go-yamux/v5 v5.0.0/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/libp2p/go-yamux/v5 v5.1.0 h1:8Qlxj4E9JGJAQVW6+uj2o7mqkqsIVlSUGmTWhlXzoHE= +github.com/libp2p/go-yamux/v5 v5.1.0/go.mod h1:tgIQ07ObtRR/I0IWsFOyQIL9/dR5UXgc2s8xKmNZv1o= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE= +github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= -github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= -github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= +github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8= +github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -436,110 +540,116 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI= +github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= -github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= -github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= -github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= +github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.6.0 h1:yKIW08WJHSPJ8bDAT2O/5fypCaUu9Bjl8r/1eJ4XAW8= +github.com/multiformats/go-multiaddr-dns v0.6.0/go.mod h1:dwIQwdORZfnNQCeS7xLXyn+7626oRmMsVP30Uronhf0= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= -github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= -github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= -github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multibase v0.3.0 h1:8helZD2+4Db7NNWFiktk2NePbF0boolBe6bDQvM4r68= +github.com/multiformats/go-multibase v0.3.0/go.mod h1:MoBLQPCkRTOL3eveIPO81860j2AQY8JwcnNlRkGRUfI= +github.com/multiformats/go-multicodec v0.10.0 h1:UpP223cig/Cx8J76jWt91njpK3GTAO1w02sdcjZDSuc= +github.com/multiformats/go-multicodec v0.10.0/go.mod h1:wg88pM+s2kZJEQfRCKBNU+g32F5aWBEjyFHXvZLTcLI= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA= -github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg= -github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= -github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= +github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= +github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ogen-go/ogen v1.20.3 h1:1tvJuJE0BnQ7Nukd6ykiTOP0ucfL0yrAjHUg3S1DCQk= github.com/ogen-go/ogen v1.20.3/go.mod h1:sJ1pJVp4S1RcSZlYIiMLo0QSMSt2pls4zfrc+hNKnzk= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= -github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= -github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/parquet-go/parquet-go v0.25.0 h1:GwKy11MuF+al/lV6nUsFw8w8HCiPOSAx1/y8yFxjH5c= -github.com/parquet-go/parquet-go v0.25.0/go.mod h1:OqBBRGBl7+llplCvDMql8dEKaDqjaFA/VAPw+OJiNiw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= +github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= +github.com/parquet-go/bitpack v1.0.0 h1:AUqzlKzPPXf2bCdjfj4sTeacrUwsT7NlcYDMUQxPcQA= +github.com/parquet-go/bitpack v1.0.0/go.mod h1:XnVk9TH+O40eOOmvpAVZ7K2ocQFrQwysLMnc6M/8lgs= +github.com/parquet-go/jsonlite v1.0.0 h1:87QNdi56wOfsE5bdgas0vRzHPxfJgzrXGml1zZdd7VU= +github.com/parquet-go/jsonlite v1.0.0/go.mod h1:nDjpkpL4EOtqs6NQugUsi0Rleq9sW/OtC1NnZEnxzF0= +github.com/parquet-go/parquet-go v0.30.1 h1:Oy6ganNrAdFiVwy7wNmWagfPTWA2X9Z3tVHBc7JtuX8= +github.com/parquet-go/parquet-go v0.30.1/go.mod h1:navtkAYr2LGoJVp141oXPlO/sxLvaOe3la2JEoD8+rg= +github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= +github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= -github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= +github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= -github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= -github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= -github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= -github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/dtls/v3 v3.1.4 h1:QhvtMflMfu9Kf0RcDC5BJBle4caPskByrKQR6uuYqpY= +github.com/pion/dtls/v3 v3.1.4/go.mod h1:cr/qotLISUw/9C1m83ZPNZtj9WnXkYLpfCptPqbkInc= github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= -github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= -github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= -github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= -github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= +github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= -github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.15 h1:MuhuGn1cxpVCPLNY1lI7F1tQ8Spntpgf12ob+pOYT8s= -github.com/pion/rtp v1.8.15/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= +github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= +github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y= +github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= -github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= -github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= -github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= -github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= -github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= -github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= -github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= -github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= -github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI= +github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun/v3 v3.1.5 h1:Y1FHlhaI6+4UoC5i/zQf4F7JvdZtB24/05oyy/GF1x8= +github.com/pion/stun/v3 v3.1.5/go.mod h1:zRUghXSQU32Lx5orJsz3uYMkIihweXb3mu5gIns02fs= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.1 h1:01UTBhYToe8PDC8piB++i66q1mmctfhhoeguaFqB84c= -github.com/pion/turn/v4 v4.0.1/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= -github.com/pion/webrtc/v4 v4.1.0 h1:yq/p0G5nKGbHISf0YKNA8Yk+kmijbblBvuSLwaJ4QYg= -github.com/pion/webrtc/v4 v4.1.0/go.mod h1:cgEGkcpxGkT6Di2ClBYO5lP9mFXbCfEOrkYUpjjCQO4= -github.com/pk910/dynamic-ssz v0.0.6 h1:Tu97LSc2TtCyqRfoSbhG9XuR/FbA7CkKeAnlkgUydFY= -github.com/pk910/dynamic-ssz v0.0.6/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c= +github.com/pion/transport/v4 v4.0.2 h1:ifYlPqNwsy6aKQ9y8yzxXlHae5431ZrH2avkD/Rn6Tk= +github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= +github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718 h1:ctdxKDq6c360JEH5gkS6jupRpyRO9BYTTPEdE6RUDuQ= +github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718/go.mod h1:mnRbhjX+4YMyP4jYMncTUA3aDpMzBLqAbjcvfCyOyRI= +github.com/pk910/hashtree-bindings v0.2.6 h1:BjRQho04nWzGrXwBCLVTyQRNV30O59iPcvQRcidFt2o= +github.com/pk910/hashtree-bindings v0.2.6/go.mod h1:zrWt88783JmhBfcgni6kkIMYRdXTZi/FL//OyI5T/l4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -548,8 +658,8 @@ github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -559,7 +669,6 @@ github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJ github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -567,9 +676,8 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -578,29 +686,44 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516 h1:xuVAdtz5ShYblG2sPyb4gw01DF8InbOI/kBCQjk7NiM= -github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516/go.mod h1:h2OlIZD/M6wFvV3YMZbW16lFgh3Rsye00G44J2cwLyU= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= +github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk= +github.com/protolambda/bls12-381-util v0.1.0/go.mod h1:cdkysJTRpeFeuUVx/TXGDQNMTiRAalk1vQw3TYTHcE4= +github.com/protolambda/zrnt v0.34.1 h1:qW55rnhZJDnOb3TwFiFRJZi3yTXFrJdGOFQM7vCwYGg= +github.com/protolambda/zrnt v0.34.1/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= +github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY= +github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU= +github.com/prysmaticlabs/fastssz v0.0.0-20260421202104-7a6eb71e6e45 h1:UkcU1rqEBOInBQdP1mCE4Smo90Eux82VEOyBhEK9TA0= +github.com/prysmaticlabs/fastssz v0.0.0-20260421202104-7a6eb71e6e45/go.mod h1:6lF+KRHYePGWv4zcbdfmq+EoCy1rch1Ew/oyZmi6AZ0= github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 h1:lC8kiphgdOBTcbTvo8MwkvpKjO0SlAgjv4xIK5FGJ94= github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15/go.mod h1:8svFBIKKu31YriBG/pNizo9N0Jr9i5PQ+dFkxWg3x5k= -github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1:VK7thFOnhxAZ/5aolr5Os4beiubuD08WiuiHyRqgwks= -github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= -github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= -github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= -github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= +github.com/prysmaticlabs/gohashtree v0.0.5-beta h1:ct41mg7HyIZd7uoSM/ud23f+3DxQG9tlMlQG+BVX23c= +github.com/prysmaticlabs/gohashtree v0.0.5-beta/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.62.0 h1:ZHDjCk5OacATwGvs8PWE97CTvX7AqZiVoW7++ZOXTf8= +github.com/quic-go/quic-go v0.62.0/go.mod h1:RAro2j2yN9a9EiPACLHT9IB2NXCvGQmmo/alT0yYI0w= +github.com/quic-go/webtransport-go v0.13.0 h1:RJLrTUHlTj8jJaQlQJUy0z0Mf7u1fVM0I6L1b9pe2M0= +github.com/quic-go/webtransport-go v0.13.0/go.mod h1:K83X9YHbAqgSLO6ikS6BXCMdWOvqh9JTHALulvb2JVk= +github.com/quipo/dependencysolver v0.0.0-20170801134659-2b009cb4ddcc h1:hK577yxEJ2f5s8w2iy2KimZmgrdAUZUNftE1ESmg2/Q= +github.com/quipo/dependencysolver v0.0.0-20170801134659-2b009cb4ddcc/go.mod h1:OQt6Zo5B3Zs+C49xul8kcHo+fZ1mCLPvd0LFxiZ2DHc= github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= -github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= -github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= +github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redpanda-data/benthos/v4 v4.64.1 h1:lM4VpUKmd3g66AhtJNsqpAyYrI4E51JZXJgb3T3ESyU= +github.com/redpanda-data/benthos/v4 v4.64.1/go.mod h1:ZYNLTH9i9apC4RlPv9pE2uDTSLqBIk30gB7CPAuScrw= +github.com/redpanda-data/connect/v4 v4.80.1 h1:Og5ewnYsIivWRQU8MiIqN0xbm8hiZNLGQxcm0BmLzxY= +github.com/redpanda-data/connect/v4 v4.80.1/go.mod h1:+cDRh9BcE7XfyT0HwyfpN3GahQb0W3YNdfyznF20VaM= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rickb777/period v1.0.22 h1:/X41JreTYsjifLDGBCaVN+s5r5/G0sTbKoHBaISv2ns= +github.com/rickb777/period v1.0.22/go.mod h1:liTmui1MSVgOqkJemF3K6c35CqiEHp0oGHCNZIXnIMA= +github.com/rickb777/plural v1.4.7 h1:rBRAxp9aTFYzWTLWIE/UTwKcaqSSAV2ml7aOUFYpAGo= +github.com/rickb777/plural v1.4.7/go.mod h1:DB19dtrplGS5s6VJVHn7tvmFYPoE83p1xqio3oVnNRM= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -610,54 +733,36 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= +github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sony/gobreaker/v2 v2.1.0 h1:av2BnjtRmVPWBvy5gSFPytm1J8BmN5AGhq875FfGKDM= +github.com/sony/gobreaker/v2 v2.1.0/go.mod h1:dO3Q/nCzxZj6ICjH6J/gM0r4oAwBMVLY8YAQf+NTtUg= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -665,35 +770,73 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8= -github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/tilinna/z85 v1.0.0 h1:uqFnJBlD01dosSeo5sK1G1YGbPuwqVHqR+12OJDRjUw= +github.com/tilinna/z85 v1.0.0/go.mod h1:EfpFU/DUY4ddEy6CRvk2l+UQNEzHbh+bqBQS+04Nkxs= +github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= +github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/trivago/grok v1.0.0/go.mod h1:9t59xLInhrncYq9a3J7488NgiBZi5y5yC7bss+w4NHM= +github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= +github.com/twmb/franz-go v1.20.6 h1:TpQTt4QcixJ1cHEmQGPOERvTzo99s8jAutmS7rbSD6w= +github.com/twmb/franz-go v1.20.6/go.mod h1:u+FzH2sInp7b9HNVv2cZN8AxdXy6y/AQ1Bkptu4c0FM= +github.com/twmb/franz-go/pkg/kadm v1.17.1 h1:Bt02Y/RLgnFO2NP2HVP1kd2TFtGRiJZx+fSArjZDtpw= +github.com/twmb/franz-go/pkg/kadm v1.17.1/go.mod h1:s4duQmrDbloVW9QTMXhs6mViTepze7JLG43xwPcAeTg= +github.com/twmb/franz-go/pkg/kmsg v1.12.0 h1:CbatD7ers1KzDNgJqPbKOq0Bz/WLBdsTH75wgzeVaPc= +github.com/twmb/franz-go/pkg/kmsg v1.12.0/go.mod h1:+DPt4NC8RmI6hqb8G09+3giKObE6uD2Eya6CfqBpeJY= +github.com/twmb/franz-go/pkg/sr v1.6.0 h1:YcnD65hmdEuJljSM4O9Hldr/0oi+vrjPGHaRUuwwusA= +github.com/twmb/franz-go/pkg/sr v1.6.0/go.mod h1:64CsHlsQnyFRq1sYPcCmlRrEG3PlLPb6cDddx2wGr28= +github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= +github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -701,47 +844,47 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= -go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk= -go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c= -go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= -go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= -go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= -go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= -go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= -go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= -go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= -go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 h1:oECp5f+hN7nkwjU/8BxQ/q23bGPb8FIrD839owX222E= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0/go.mod h1:DqEFwLumhzMBDQv9PcWbyoDxHI/4lAk6CM4nJBH39sc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04= +go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= +go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0 h1:KrC1YrQeSt46ITMWAbgQx1M1eV1/1TKzttrBzymPmss= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0/go.mod h1:zDSEzoEqsOrgBeGvH66KRgxh90VonFyJqBHA0Pk3+rM= +go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= +go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= +go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= +go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= +go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= +go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= +go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= +go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/dig v1.18.2 h1:HElIfvmw0jYfmbgk+OU/1vbpYQFcImnuvaUEeuILS2c= -go.uber.org/dig v1.18.2/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= -go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -750,11 +893,10 @@ golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -765,11 +907,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/exp v0.0.0-20260718201538-764159d718ef h1:LkZ48HFgy/TvhTI0bcWkjgFkgLyKUwcTbDjS0DUjw+A= +golang.org/x/exp v0.0.0-20260718201538-764159d718ef/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -790,18 +931,15 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= +golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -821,6 +959,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -828,21 +967,18 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -851,7 +987,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -867,17 +1004,14 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -885,14 +1019,16 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -908,37 +1044,36 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5 h1:ZUSxONxc981v7AW7QUg+I9WwZzSTTJ019ENBYr5pV/Q= +golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5/go.mod h1:LVehoXe41cL5SCVQilsV7Gg6BNG+Js6P9PhSbYTIUkQ= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -946,22 +1081,18 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1002,18 +1133,21 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1031,8 +1165,6 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1040,10 +1172,6 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1072,13 +1200,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 h1:WvBuA5rjZx9SNIzgcU53OohgZy6lKSus++uY4xLaWKc= -google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:W3S/3np0/dPWsWLi1h/UymYctGXaGBM2StwzD0y140U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20251002232023-7c0ddcbb5797 h1:06qNPeHxbfl+OJluwQ2zOiTP6di3mvADTHnMYQuOKDQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260819154853-08b0e4226688 h1:ax2KzoSRIZU/M0cIxri3pKxy99vniH1PVxWC6si/eZI= +google.golang.org/genproto/googleapis/api v0.0.0-20260819154853-08b0e4226688/go.mod h1:1RJ9BQGyNdZwkGc1eTqkErfRZ6RJyYPHZo73BZ1vQqI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1091,8 +1217,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= +google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1109,12 +1235,10 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= -gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc= -gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= @@ -1124,11 +1248,15 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -1142,8 +1270,6 @@ gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/plugin/prometheus v0.1.0 h1:kDQwAfCUsT9D6jDUpIp7pnc7bCJu/6voM8I/BmFjxUQ= gorm.io/plugin/prometheus v0.1.0/go.mod h1:5nrc/JrWCUNoDXCY4eOae/FK/J5WjQ0axXuFusCzdTc= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1153,16 +1279,32 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= -modernc.org/libc v1.40.7 h1:oeLS0G067ZqUu+v143Dqad0btMfKmNS7SuOsnkq0Ysg= -modernc.org/libc v1.40.7/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= -modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4= +modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A= +modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q= +modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= +modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A= +modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4= +modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/internal/api/frames.go b/internal/api/frames.go index 2c804d1..e15dd9a 100644 --- a/internal/api/frames.go +++ b/internal/api/frames.go @@ -8,8 +8,8 @@ import ( "net/http" "strconv" - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/go-faster/jx" "github.com/ethpandaops/forky/api/rest" @@ -88,44 +88,103 @@ func frameMetadataToRest(m *types.FrameMetadata) rest.FrameMetadata { // forkChoiceToRest maps a beacon node fork-choice dump onto the spec's // ForkChoice schema, following the beacon-API string serialization for // slots, epochs and weights. -func forkChoiceToRest(fc *v1.ForkChoice) (rest.ForkChoice, error) { +func forkChoiceToRest(fc *types.ForkChoice) (rest.ForkChoice, error) { nodes := make([]rest.ForkChoiceNode, 0, len(fc.ForkChoiceNodes)) for _, node := range fc.ForkChoiceNodes { - restNode := rest.ForkChoiceNode{ - Slot: rest.SlotString(strconv.FormatUint(uint64(node.Slot), 10)), - BlockRoot: rest.Root(node.BlockRoot.String()), - ParentRoot: rest.Root(node.ParentRoot.String()), - JustifiedEpoch: rest.EpochString(strconv.FormatUint(uint64(node.JustifiedEpoch), 10)), - FinalizedEpoch: rest.EpochString(strconv.FormatUint(uint64(node.FinalizedEpoch), 10)), - Weight: strconv.FormatUint(node.Weight, 10), - Validity: rest.ForkChoiceNodeValidity(node.Validity.String()), - ExecutionBlockHash: rest.Root(node.ExecutionBlockHash.String()), + restNode, err := forkChoiceNodeToRest(node) + if err != nil { + return rest.ForkChoice{}, err } - if len(node.ExtraData) > 0 { - extra := make(rest.ForkChoiceNodeExtraData, len(node.ExtraData)) + nodes = append(nodes, restNode) + } + + restFC := rest.ForkChoice{ + JustifiedCheckpoint: checkpointToRest(fc.JustifiedCheckpoint), + FinalizedCheckpoint: checkpointToRest(fc.FinalizedCheckpoint), + ForkChoiceNodes: nodes, + } - for key, value := range node.ExtraData { - raw, err := json.Marshal(value) - if err != nil { - return rest.ForkChoice{}, fmt.Errorf("failed to marshal extra_data %q: %w", key, err) - } + if len(fc.ExtraData) > 0 { + extra, err := extraDataToRest(fc.ExtraData) + if err != nil { + return rest.ForkChoice{}, err + } + + restFC.ExtraData = rest.NewOptForkChoiceExtraData(extra) + } - extra[key] = jx.Raw(raw) - } + return restFC, nil +} + +// forkChoiceNodeToRest maps a fork-choice node onto the spec's +// ForkChoiceNode schema. +func forkChoiceNodeToRest(node *types.ForkChoiceNode) (rest.ForkChoiceNode, error) { + restNode := rest.ForkChoiceNode{ + Slot: rest.SlotString(strconv.FormatUint(uint64(node.Slot), 10)), + BlockRoot: rest.Root(node.BlockRoot.String()), + ParentRoot: rest.Root(node.ParentRoot.String()), + Weight: strconv.FormatUint(node.Weight, 10), + Validity: rest.ForkChoiceNodeValidity(node.Validity.String()), + ExecutionBlockHash: rest.Root(node.ExecutionBlockHash.String()), + PayloadAttesterCount: optUint64String(node.PayloadAttesterCount), + PayloadAvailabilityYesCount: optUint64String(node.PayloadAvailabilityYesCount), + PayloadDataAvailabilityYesCount: optUint64String(node.PayloadDataAvailabilityYesCount), + } + + if node.JustifiedEpoch != nil { + restNode.JustifiedEpoch = rest.NewOptEpochString(rest.EpochString(strconv.FormatUint(uint64(*node.JustifiedEpoch), 10))) + } + + if node.FinalizedEpoch != nil { + restNode.FinalizedEpoch = rest.NewOptEpochString(rest.EpochString(strconv.FormatUint(uint64(*node.FinalizedEpoch), 10))) + } + + if node.PayloadStatus != v1.ForkChoicePayloadStatusUnknown { + restNode.PayloadStatus = rest.NewOptPayloadStatus(rest.PayloadStatus(node.PayloadStatus.String())) + } - restNode.ExtraData = rest.NewOptForkChoiceNodeExtraData(extra) + if node.ParentPayloadStatus != nil { + restNode.ParentPayloadStatus = rest.NewOptPayloadStatus(rest.PayloadStatus(node.ParentPayloadStatus.String())) + } + + if len(node.ExtraData) > 0 { + extra, err := extraDataToRest(node.ExtraData) + if err != nil { + return rest.ForkChoiceNode{}, err } - nodes = append(nodes, restNode) + restNode.ExtraData = rest.NewOptForkChoiceNodeExtraData(rest.ForkChoiceNodeExtraData(extra)) } - return rest.ForkChoice{ - JustifiedCheckpoint: checkpointToRest(fc.JustifiedCheckpoint), - FinalizedCheckpoint: checkpointToRest(fc.FinalizedCheckpoint), - ForkChoiceNodes: nodes, - }, nil + return restNode, nil +} + +// extraDataToRest maps client-specific extra data onto raw JSON values. +func extraDataToRest(extraData map[string]any) (map[string]jx.Raw, error) { + extra := make(map[string]jx.Raw, len(extraData)) + + for key, value := range extraData { + raw, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("failed to marshal extra_data %q: %w", key, err) + } + + extra[key] = jx.Raw(raw) + } + + return extra, nil +} + +// optUint64String maps an optional value onto the spec's decimal string +// serialization. +func optUint64String(value *uint64) rest.OptString { + if value == nil { + return rest.OptString{} + } + + return rest.NewOptString(strconv.FormatUint(*value, 10)) } // checkpointToRest maps a beacon checkpoint onto the spec's Checkpoint diff --git a/internal/db/frame_metadata.go b/internal/db/frame_metadata.go index 1a89189..098c4cf 100644 --- a/internal/db/frame_metadata.go +++ b/internal/db/frame_metadata.go @@ -3,7 +3,7 @@ package db import ( "time" - "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "gorm.io/gorm" "github.com/ethpandaops/forky/internal/types" diff --git a/internal/db/indexer.go b/internal/db/indexer.go index d5a8a79..a894c69 100644 --- a/internal/db/indexer.go +++ b/internal/db/indexer.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/glebarez/sqlite" "github.com/sirupsen/logrus" "gorm.io/driver/postgres" diff --git a/internal/db/indexer_test.go b/internal/db/indexer_test.go index c542a81..577004b 100644 --- a/internal/db/indexer_test.go +++ b/internal/db/indexer_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/google/uuid" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/internal/service/service.go b/internal/service/service.go index 3d9b3b4..1e1c180 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/sirupsen/logrus" "github.com/ethpandaops/forky/internal/db" diff --git a/internal/source/beacon_node.go b/internal/source/beacon_node.go index 91b34f4..abab8a4 100644 --- a/internal/source/beacon_node.go +++ b/internal/source/beacon_node.go @@ -4,15 +4,17 @@ import ( "context" "errors" "fmt" + nethttp "net/http" + "sync/atomic" "time" - eth2client "github.com/attestantio/go-eth2-client" - api "github.com/attestantio/go-eth2-client/api" - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/http" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/cenkalti/backoff/v4" "github.com/ethpandaops/ethwallclock" + eth2client "github.com/ethpandaops/go-eth2-client" + api "github.com/ethpandaops/go-eth2-client/api" + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/http" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/go-co-op/gocron" "github.com/google/uuid" "github.com/rs/zerolog" @@ -45,8 +47,16 @@ type BeaconNode struct { secondsPerSlot time.Duration slotsPerEpoch uint64 wallclock *ethwallclock.EthereumBeaconChain + + // forkChoiceV2RetryAt is when to next try the v2 fork choice endpoint, in + // unix nanoseconds, after the node reported it as unsupported. + forkChoiceV2RetryAt atomic.Int64 } +// forkChoiceV2RetryInterval is how long to use the v1 fork choice endpoint +// before checking whether a node has started supporting v2. +const forkChoiceV2RetryInterval = time.Hour + type BeaconNodeConfig struct { Address string `yaml:"address"` PollingInterval string `yaml:"polling_interval"` @@ -272,38 +282,95 @@ func (b *BeaconNode) fetchFrame(ctx context.Context) error { fetchedAt := time.Now() - if provider, isProvider := b.client.(eth2client.ForkChoiceProvider); isProvider { - rsp, err := provider.ForkChoice(ctx, &api.ForkChoiceOpts{}) - if err != nil { - return fmt.Errorf("failed to get fork choice dump: %w", err) + forkChoice, err := b.fetchForkChoice(ctx) + if err != nil { + return err + } + + b.metrics.ObserveItemFetched(string(DataFrame)) + + frame := &types.Frame{ + Metadata: types.FrameMetadata{ + Node: b.Name(), + FetchedAt: fetchedAt, + WallClockSlot: phase0.Slot(slot.Number()), + WallClockEpoch: phase0.Epoch(epoch.Number()), + ID: uuid.New().String(), + Labels: b.config.Labels, + EventSource: types.BeaconNodeEventSource.String(), + ConsensusClient: string(ethereum.ClientFromString(nodeVersion)), + }, + Data: forkChoice, + } + + b.publishFrame(ctx, frame) + + b.log.WithFields(logrus.Fields{ + "wallclock_slot": slot.Number(), + "wallclock_epoch": epoch.Number(), + "fetchedAt": frame.Metadata.FetchedAt, + }).Debug("Fetched frame") + + return nil +} + +// fetchForkChoice fetches the node's fork choice dump, preferring the +// Gloas-aware v2 endpoint and falling back to v1. +// +// A node without the endpoint, or whose v2 response does not follow the spec +// (ethereum/beacon-APIs#615), cannot serve v2 until it is upgraded, so v1 is +// used for forkChoiceV2RetryInterval before v2 is tried again. Any other v2 +// failure falls back to v1 for this fetch only. +func (b *BeaconNode) fetchForkChoice(ctx context.Context) (*types.ForkChoice, error) { + if provider, isProvider := b.client.(eth2client.ForkChoiceV2Provider); isProvider && + time.Now().UnixNano() >= b.forkChoiceV2RetryAt.Load() { + rsp, err := provider.ForkChoiceV2(ctx, &api.ForkChoiceOpts{}) + if err == nil { + return types.ForkChoiceFromV2(rsp.Data), nil } - dump := rsp.Data - - b.metrics.ObserveItemFetched(string(DataFrame)) - - frame := &types.Frame{ - Metadata: types.FrameMetadata{ - Node: b.Name(), - FetchedAt: fetchedAt, - WallClockSlot: phase0.Slot(slot.Number()), - WallClockEpoch: phase0.Epoch(epoch.Number()), - ID: uuid.New().String(), - Labels: b.config.Labels, - EventSource: types.BeaconNodeEventSource.String(), - ConsensusClient: string(ethereum.ClientFromString(nodeVersion)), - }, - Data: dump, + if ctxErr := ctx.Err(); ctxErr != nil { + return nil, ctxErr } - b.publishFrame(ctx, frame) + switch { + case isUnsupportedEndpoint(err): + b.forkChoiceV2RetryAt.Store(time.Now().Add(forkChoiceV2RetryInterval).UnixNano()) + b.log.WithError(err).Debug("Beacon node does not support the v2 fork choice endpoint, using v1") + case errors.Is(err, eth2client.ErrInvalidResponse): + b.forkChoiceV2RetryAt.Store(time.Now().Add(forkChoiceV2RetryInterval).UnixNano()) + b.log.WithError(err).WithField("retry_in", forkChoiceV2RetryInterval). + Warn("Beacon node's v2 fork choice does not follow the spec, using v1") + default: + b.log.WithError(err).Warn("Failed to get v2 fork choice dump, falling back to v1 for this fetch") + } + } - b.log.WithFields(logrus.Fields{ - "wallclock_slot": slot.Number(), - "wallclock_epoch": epoch.Number(), - "fetchedAt": frame.Metadata.FetchedAt, - }).Debug("Fetched frame") + provider, isProvider := b.client.(eth2client.ForkChoiceProvider) + if !isProvider { + return nil, errors.New("client does not support fork choice provider") } - return nil + rsp, err := provider.ForkChoice(ctx, &api.ForkChoiceOpts{}) + if err != nil { + return nil, fmt.Errorf("failed to get fork choice dump: %w", err) + } + + return types.ForkChoiceFromV1(rsp.Data), nil +} + +// isUnsupportedEndpoint reports whether err is a beacon node rejecting an +// endpoint it does not implement. +func isUnsupportedEndpoint(err error) bool { + var apiErr *api.Error + if !errors.As(err, &apiErr) { + return false + } + + switch apiErr.StatusCode { + case nethttp.StatusBadRequest, nethttp.StatusNotFound, nethttp.StatusMethodNotAllowed, nethttp.StatusNotImplemented: + return true + default: + return false + } } diff --git a/internal/source/xatu_forkchoice.go b/internal/source/xatu_forkchoice.go new file mode 100644 index 0000000..697a276 --- /dev/null +++ b/internal/source/xatu_forkchoice.go @@ -0,0 +1,228 @@ +package source + +import ( + "encoding/json" + "errors" + "fmt" + + eth2v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" + xatuethv1 "github.com/ethpandaops/xatu/pkg/proto/eth/v1" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/ethpandaops/forky/internal/types" +) + +// forkChoiceFromXatu normalizes a fork choice dump received from Xatu. +// +// Sentries fetching the Gloas-aware v2 endpoint set a payload status on every +// node, with one node per block root and payload status; those dumps are +// normalized as v2 dumps, keeping the empty and full nodes. Anything else came +// from the v1 endpoint and is normalized as a v1 dump. +func forkChoiceFromXatu(fc *xatuethv1.ForkChoiceV2) (*types.ForkChoice, error) { + if !hasPayloadStatuses(fc) { + data, err := fc.AsGoEth2ClientV1ForkChoice() + if err != nil { + return nil, err + } + + return types.ForkChoiceFromV1(data), nil + } + + data, err := forkChoiceV2FromXatu(fc) + if err != nil { + return nil, err + } + + return types.NormalizeV2(data), nil +} + +// hasPayloadStatuses returns true if every node of the fork choice has a +// payload status. +func hasPayloadStatuses(fc *xatuethv1.ForkChoiceV2) bool { + if len(fc.GetForkChoiceNodes()) == 0 { + return false + } + + for _, node := range fc.GetForkChoiceNodes() { + if node.GetPayloadStatus() == nil { + return false + } + } + + return true +} + +// forkChoiceV2FromXatu converts a Xatu fork choice with payload statuses into +// forky's fork choice. Values a node does not carry stay unset, for +// NormalizeV2 to fill in where it can. +func forkChoiceV2FromXatu(fc *xatuethv1.ForkChoiceV2) (*types.ForkChoice, error) { + justifiedCheckpoint, err := checkpointFromXatu(fc.GetJustifiedCheckpoint()) + if err != nil { + return nil, fmt.Errorf("invalid justified checkpoint: %w", err) + } + + finalizedCheckpoint, err := checkpointFromXatu(fc.GetFinalizedCheckpoint()) + if err != nil { + return nil, fmt.Errorf("invalid finalized checkpoint: %w", err) + } + + extraData, err := extraDataFromXatu(fc.GetExtraData()) + if err != nil { + return nil, fmt.Errorf("invalid extra data: %w", err) + } + + nodes := make([]*types.ForkChoiceNode, 0, len(fc.GetForkChoiceNodes())) + + for i, node := range fc.GetForkChoiceNodes() { + converted, err := forkChoiceNodeFromXatu(node) + if err != nil { + return nil, fmt.Errorf("invalid fork choice node %d: %w", i, err) + } + + nodes = append(nodes, converted) + } + + return &types.ForkChoice{ + JustifiedCheckpoint: justifiedCheckpoint, + FinalizedCheckpoint: finalizedCheckpoint, + ForkChoiceNodes: nodes, + ExtraData: extraData, + }, nil +} + +func forkChoiceNodeFromXatu(node *xatuethv1.ForkChoiceNodeV2) (*types.ForkChoiceNode, error) { + blockRoot, err := xatuethv1.StringToRoot(node.GetBlockRoot()) + if err != nil { + return nil, fmt.Errorf("invalid block root: %w", err) + } + + // The parent root is empty for the oldest retained node on some clients. + var parentRoot phase0.Root + if node.GetParentRoot() != "" { + if parentRoot, err = xatuethv1.StringToRoot(node.GetParentRoot()); err != nil { + return nil, fmt.Errorf("invalid parent root: %w", err) + } + } + + executionBlockHash, err := xatuethv1.StringToRoot(node.GetExecutionBlockHash()) + if err != nil { + return nil, fmt.Errorf("invalid execution block hash: %w", err) + } + + payloadStatus, err := payloadStatusFromXatu(node.GetPayloadStatus().GetValue()) + if err != nil { + return nil, err + } + + extraData, err := extraDataFromXatu(node.GetExtraData()) + if err != nil { + return nil, fmt.Errorf("invalid extra data: %w", err) + } + + validity, err := eth2v1.ForkChoiceNodeValidityFromString(node.GetValidity()) + if err != nil { + return nil, fmt.Errorf("invalid validity: %w", err) + } + + converted := &types.ForkChoiceNode{ + Slot: phase0.Slot(node.GetSlot().GetValue()), + BlockRoot: blockRoot, + ParentRoot: parentRoot, + JustifiedEpoch: epochFromXatu(node.GetJustifiedCheckpoint(), node.GetJustifiedEpoch()), + FinalizedEpoch: epochFromXatu(node.GetFinalizedCheckpoint(), node.GetFinalizedEpoch()), + Weight: node.GetWeight().GetValue(), + Validity: validity, + ExecutionBlockHash: executionBlockHash, + PayloadStatus: payloadStatus, + PayloadAttesterCount: uint64FromXatu(node.GetPayloadAttesterCount()), + PayloadAvailabilityYesCount: uint64FromXatu(node.GetPayloadAvailabilityYesCount()), + PayloadDataAvailabilityYesCount: uint64FromXatu(node.GetPayloadDataAvailabilityYesCount()), + ExtraData: extraData, + } + + if node.GetParentPayloadStatus() != nil { + parentPayloadStatus, err := payloadStatusFromXatu(node.GetParentPayloadStatus().GetValue()) + if err != nil { + return nil, fmt.Errorf("invalid parent payload status: %w", err) + } + + converted.ParentPayloadStatus = &parentPayloadStatus + } + + return converted, nil +} + +// epochFromXatu returns a node's checkpoint epoch: from its checkpoint, which +// sentries since ethpandaops/xatu#900 send, else from the epoch field. +func epochFromXatu(checkpoint *xatuethv1.CheckpointV2, epoch *wrapperspb.UInt64Value) *phase0.Epoch { + switch { + case checkpoint.GetEpoch() != nil: + value := phase0.Epoch(checkpoint.GetEpoch().GetValue()) + + return &value + case epoch != nil: + value := phase0.Epoch(epoch.GetValue()) + + return &value + default: + return nil + } +} + +// uint64FromXatu returns a wrapped value, nil if unset. +func uint64FromXatu(value *wrapperspb.UInt64Value) *uint64 { + if value == nil { + return nil + } + + v := value.GetValue() + + return &v +} + +func checkpointFromXatu(checkpoint *xatuethv1.CheckpointV2) (phase0.Checkpoint, error) { + if checkpoint == nil { + return phase0.Checkpoint{}, errors.New("missing") + } + + root, err := xatuethv1.StringToRoot(checkpoint.GetRoot()) + if err != nil { + return phase0.Checkpoint{}, err + } + + return phase0.Checkpoint{ + Epoch: phase0.Epoch(checkpoint.GetEpoch().GetValue()), + Root: root, + }, nil +} + +// payloadStatusFromXatu maps Xatu's payload status, the spec's PayloadStatus +// enum, onto go-eth2-client's. +func payloadStatusFromXatu(status uint32) (eth2v1.ForkChoicePayloadStatus, error) { + switch status { + case 0: + return eth2v1.ForkChoicePayloadStatusEmpty, nil + case 1: + return eth2v1.ForkChoicePayloadStatusFull, nil + case 2: + return eth2v1.ForkChoicePayloadStatusPending, nil + default: + return eth2v1.ForkChoicePayloadStatusUnknown, fmt.Errorf("unrecognised payload status %d", status) + } +} + +// extraDataFromXatu decodes extra data that Xatu carries as a JSON string, +// which is empty or null when the client provides none. +func extraDataFromXatu(input string) (map[string]any, error) { + if input == "" { + return nil, nil + } + + var extraData map[string]any + if err := json.Unmarshal([]byte(input), &extraData); err != nil { + return nil, err + } + + return extraData, nil +} diff --git a/internal/source/xatu_forkchoice_test.go b/internal/source/xatu_forkchoice_test.go new file mode 100644 index 0000000..cb0b0ea --- /dev/null +++ b/internal/source/xatu_forkchoice_test.go @@ -0,0 +1,143 @@ +package source + +import ( + "encoding/json" + "os" + "testing" + + eth2v1 "github.com/ethpandaops/go-eth2-client/api/v1" + xatuethv1 "github.com/ethpandaops/xatu/pkg/proto/eth/v1" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/ethpandaops/forky/internal/types" +) + +func loadFixture(t *testing.T, path string, wrapped bool, dst any) { + t.Helper() + + raw, err := os.ReadFile(path) + require.NoError(t, err) + + if wrapped { + var envelope struct { + Data json.RawMessage `json:"data"` + } + + require.NoError(t, json.Unmarshal(raw, &envelope)) + + if envelope.Data != nil { + raw = envelope.Data + } + } + + require.NoError(t, json.Unmarshal(raw, dst)) +} + +// throughPipeline sends a fork choice through the Xatu pipeline as forky +// receives it: encoded as JSON by the Xatu server, decoded by forky. +func throughPipeline(t *testing.T, fc *xatuethv1.ForkChoiceV2) *xatuethv1.ForkChoiceV2 { + t.Helper() + + encoded, err := protojson.Marshal(fc) + require.NoError(t, err) + + var decoded xatuethv1.ForkChoiceV2 + + require.NoError(t, protojson.Unmarshal(encoded, &decoded)) + + return &decoded +} + +// TestForkChoiceFromXatuV2 ensures a v2 fork choice received from Xatu +// normalizes exactly as when fetched from the beacon node directly, keeping +// the empty and full nodes. +func TestForkChoiceFromXatuV2(t *testing.T) { + var original eth2v1.ForkChoiceV2 + + loadFixture(t, "../types/testdata/forkchoice_v2_spec.json", true, &original) + + fc, err := xatuethv1.NewForkChoiceV2FromGoEth2ClientV2(&original) + require.NoError(t, err) + + got, err := forkChoiceFromXatu(throughPipeline(t, fc)) + require.NoError(t, err) + require.Equal(t, types.ForkChoiceFromV2(&original), got) + + statuses := make(map[eth2v1.ForkChoicePayloadStatus]int) + for _, node := range got.ForkChoiceNodes { + statuses[node.PayloadStatus]++ + } + + require.Equal(t, map[eth2v1.ForkChoicePayloadStatus]int{ + eth2v1.ForkChoicePayloadStatusPending: 4, + eth2v1.ForkChoicePayloadStatusEmpty: 4, + eth2v1.ForkChoicePayloadStatusFull: 4, + }, statuses) +} + +// TestForkChoiceFromXatuV2Partial ensures a v2 fork choice from a sentry that +// sent nodes without checkpoints or PTC counts keeps them unset, for +// NormalizeV2 to fill in, rather than zero. +func TestForkChoiceFromXatuV2Partial(t *testing.T) { + var original eth2v1.ForkChoiceV2 + + loadFixture(t, "../types/testdata/forkchoice_v2_spec.json", true, &original) + + fc, err := xatuethv1.NewForkChoiceV2FromGoEth2ClientV2(&original) + require.NoError(t, err) + + for _, node := range fc.GetForkChoiceNodes() { + node.JustifiedCheckpoint, node.JustifiedEpoch = nil, nil + node.FinalizedCheckpoint, node.FinalizedEpoch = nil, nil + node.PayloadAttesterCount = nil + } + + got, err := forkChoiceFromXatu(throughPipeline(t, fc)) + require.NoError(t, err) + + for _, node := range got.ForkChoiceNodes { + require.Nil(t, node.JustifiedEpoch) + require.Nil(t, node.FinalizedEpoch) + require.Nil(t, node.PayloadAttesterCount) + require.NotNil(t, node.PayloadAvailabilityYesCount) + } + + // A sentry before ethpandaops/xatu#900 sent only the epochs. + fc.GetForkChoiceNodes()[0].JustifiedEpoch = &wrapperspb.UInt64Value{Value: 7} + + got, err = forkChoiceFromXatu(throughPipeline(t, fc)) + require.NoError(t, err) + require.NotNil(t, got.ForkChoiceNodes[0].JustifiedEpoch) + require.Equal(t, uint64(7), uint64(*got.ForkChoiceNodes[0].JustifiedEpoch)) +} + +// TestForkChoiceFromXatuV1 ensures a v1 fork choice received from Xatu, from +// a sentry whose beacon node lacks the v2 endpoint, normalizes as a v1 dump. +func TestForkChoiceFromXatuV1(t *testing.T) { + var original eth2v1.ForkChoice + + loadFixture(t, "../types/testdata/forkchoice_v1_nimbus.json", false, &original) + + fc, err := xatuethv1.NewForkChoiceV2FromGoEth2ClientV1(&original) + require.NoError(t, err) + + got, err := forkChoiceFromXatu(throughPipeline(t, fc)) + require.NoError(t, err) + require.Equal(t, types.ForkChoiceFromV1(&original), got) +} + +func TestForkChoiceFromXatuInvalidPayloadStatus(t *testing.T) { + var original eth2v1.ForkChoiceV2 + + loadFixture(t, "../types/testdata/forkchoice_v2_spec.json", true, &original) + + fc, err := xatuethv1.NewForkChoiceV2FromGoEth2ClientV2(&original) + require.NoError(t, err) + + fc.ForkChoiceNodes[1].PayloadStatus.Value = 7 + + _, err = forkChoiceFromXatu(fc) + require.EqualError(t, err, "invalid fork choice node 1: unrecognised payload status 7") +} diff --git a/internal/source/xatu_http.go b/internal/source/xatu_http.go index e6a1bc2..421e884 100644 --- a/internal/source/xatu_http.go +++ b/internal/source/xatu_http.go @@ -10,8 +10,7 @@ import ( "strings" "time" - eth2v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/ethpandaops/xatu/pkg/proto/xatu" "github.com/google/uuid" "github.com/sirupsen/logrus" @@ -172,6 +171,11 @@ func (x *XatuHTTP) registerHandler(ctx context.Context, mux *http.ServeMux) { }) } +// xatuEventUnmarshalOptions decode Xatu events ignoring fields this build does +// not know, so that a Xatu server on a newer version than forky's does not get +// every event rejected. +var xatuEventUnmarshalOptions = protojson.UnmarshalOptions{DiscardUnknown: true} + func (x *XatuHTTP) handleNDJSONRequest(ctx context.Context, body []byte) error { s := string(body) @@ -185,7 +189,7 @@ func (x *XatuHTTP) handleNDJSONRequest(ctx context.Context, body []byte) error { } var v xatu.DecoratedEvent - if err := protojson.Unmarshal([]byte(line), &v); err != nil { + if err := xatuEventUnmarshalOptions.Unmarshal([]byte(line), &v); err != nil { return err } @@ -209,7 +213,7 @@ func (x *XatuHTTP) handleJSONRequest(ctx context.Context, body []byte) error { event := xatu.DecoratedEvent{} - err := protojson.Unmarshal(body, &event) + err := xatuEventUnmarshalOptions.Unmarshal(body, &event) if err != nil { return err } @@ -290,7 +294,7 @@ func (x *XatuHTTP) handleForkChoiceEvent(ctx context.Context, event *xatu.Decora return errors.New("event is not a fork choice event") } - data, err := fc.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fc) if err != nil { return fmt.Errorf("failed to convert event to fork choice: %w", err) } @@ -310,7 +314,7 @@ func (x *XatuHTTP) handleForkChoiceV2Event(ctx context.Context, event *xatu.Deco return errors.New("event is not a fork choice event") } - data, err := fc.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fc) if err != nil { return fmt.Errorf("failed to convert event to fork choice: %w", err) } @@ -346,7 +350,7 @@ func (x *XatuHTTP) handleForkChoiceReorgEvent(ctx context.Context, event *xatu.D } if fcr.After != nil && additionalData.After != nil { - data, err := fcr.After.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fcr.After) if err != nil { x.log.WithError(err).Error("failed to convert fork_choice_reorg.after to fork choice") } else { @@ -358,7 +362,7 @@ func (x *XatuHTTP) handleForkChoiceReorgEvent(ctx context.Context, event *xatu.D } if fcr.Before != nil && additionalData.Before != nil { - data, err := fcr.Before.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fcr.Before) if err != nil { x.log.WithError(err).Error("failed to convert fork_choice_reorg.before to fork choice") } else { @@ -395,7 +399,7 @@ func (x *XatuHTTP) handleForkChoiceReorgV2Event(ctx context.Context, event *xatu } if fcr.After != nil && additionalData.After != nil { - data, err := fcr.After.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fcr.After) if err != nil { x.log.WithError(err).Error("failed to convert fork_choice_reorg.after to fork choice") } else { @@ -407,7 +411,7 @@ func (x *XatuHTTP) handleForkChoiceReorgV2Event(ctx context.Context, event *xatu } if fcr.Before != nil && additionalData.Before != nil { - data, err := fcr.Before.AsGoEth2ClientV1ForkChoice() + data, err := forkChoiceFromXatu(fcr.Before) if err != nil { x.log.WithError(err).Error("failed to convert fork_choice_reorg.before to fork choice") } else { @@ -423,7 +427,7 @@ func (x *XatuHTTP) handleForkChoiceReorgV2Event(ctx context.Context, event *xatu func (x *XatuHTTP) createFrameFromSnapshotAndData(ctx context.Context, event *xatu.DecoratedEvent, - data *eth2v1.ForkChoice, + forkChoice *types.ForkChoice, snapshot *xatu.ClientMeta_ForkChoiceSnapshot, timing string, ) error { @@ -443,7 +447,7 @@ func (x *XatuHTTP) createFrameFromSnapshotAndData(ctx context.Context, Labels: []string{}, }, - Data: data, + Data: forkChoice, } if event.GetEvent().GetName() == xatu.Event_BEACON_API_ETH_V1_DEBUG_FORK_CHOICE_REORG { @@ -475,7 +479,7 @@ func (x *XatuHTTP) createFrameFromSnapshotAndData(ctx context.Context, func (x *XatuHTTP) createFrameFromSnapshotV2AndData(ctx context.Context, event *xatu.DecoratedEvent, - data *eth2v1.ForkChoice, + forkChoice *types.ForkChoice, snapshot *xatu.ClientMeta_ForkChoiceSnapshotV2, timing string, ) error { @@ -495,7 +499,7 @@ func (x *XatuHTTP) createFrameFromSnapshotV2AndData(ctx context.Context, Labels: []string{}, }, - Data: data, + Data: forkChoice, } if event.GetEvent().GetName() == xatu.Event_BEACON_API_ETH_V1_DEBUG_FORK_CHOICE_REORG_V2 { diff --git a/internal/types/forkchoice.go b/internal/types/forkchoice.go new file mode 100644 index 0000000..5b5056d --- /dev/null +++ b/internal/types/forkchoice.go @@ -0,0 +1,261 @@ +package types + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" +) + +// ForkChoice is a beacon node's fork choice dump, normalized across the v1 and +// v2 debug fork choice endpoints and the differences between client +// implementations of them. +// +// Frames stored before Gloas support hold the v1 shape, which this type reads +// unchanged: the Gloas fields are all optional. +type ForkChoice struct { + // JustifiedCheckpoint is the store's justified checkpoint. + JustifiedCheckpoint phase0.Checkpoint + // FinalizedCheckpoint is the store's finalized checkpoint. + FinalizedCheckpoint phase0.Checkpoint + // ForkChoiceNodes are the fork choice nodes. Pre-Gloas, and for clients + // without the v2 endpoint, there is one node per block. Post-Gloas a block + // can have a pending, an empty and a full node. + ForkChoiceNodes []*ForkChoiceNode + // ExtraData is the client-specific extra data of the fork choice store, + // such as the unrealized justified checkpoint. + ExtraData map[string]any +} + +// ForkChoiceNode is a node in the fork choice tree. +type ForkChoiceNode struct { + Slot phase0.Slot + BlockRoot phase0.Root + // ParentRoot is the root of the parent block. Unlike the v2 endpoint, it is + // the parent block's root for empty and full nodes too. + ParentRoot phase0.Root + JustifiedEpoch *phase0.Epoch + FinalizedEpoch *phase0.Epoch + Weight uint64 + Validity v1.ForkChoiceNodeValidity + ExecutionBlockHash phase0.Root + // PayloadStatus is the Gloas payload status of the node; unknown (the zero + // value) for nodes without one. + PayloadStatus v1.ForkChoicePayloadStatus + // ParentPayloadStatus is the payload status of the parent fork choice node: + // for a pending node, whether its block was built on the parent block's + // empty or full payload; for empty and full nodes, pending. Nil if unknown. + ParentPayloadStatus *v1.ForkChoicePayloadStatus + PayloadAttesterCount *uint64 + PayloadAvailabilityYesCount *uint64 + PayloadDataAvailabilityYesCount *uint64 + ExtraData map[string]any +} + +type forkChoiceJSON struct { + JustifiedCheckpoint *phase0.Checkpoint `json:"justified_checkpoint"` + FinalizedCheckpoint *phase0.Checkpoint `json:"finalized_checkpoint"` + ForkChoiceNodes []*ForkChoiceNode `json:"fork_choice_nodes"` + ExtraData map[string]any `json:"extra_data,omitempty"` +} + +// MarshalJSON implements json.Marshaler. +func (f *ForkChoice) MarshalJSON() ([]byte, error) { + return json.Marshal(&forkChoiceJSON{ + JustifiedCheckpoint: &f.JustifiedCheckpoint, + FinalizedCheckpoint: &f.FinalizedCheckpoint, + ForkChoiceNodes: f.ForkChoiceNodes, + ExtraData: f.ExtraData, + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (f *ForkChoice) UnmarshalJSON(input []byte) error { + var data forkChoiceJSON + if err := json.Unmarshal(input, &data); err != nil { + return fmt.Errorf("invalid JSON: %w", err) + } + + if data.JustifiedCheckpoint == nil { + return errors.New("justified checkpoint missing") + } + + if data.FinalizedCheckpoint == nil { + return errors.New("finalized checkpoint missing") + } + + if data.ForkChoiceNodes == nil { + return errors.New("fork choice nodes missing") + } + + for i, node := range data.ForkChoiceNodes { + if node == nil { + return fmt.Errorf("fork choice node entry %d missing", i) + } + } + + f.JustifiedCheckpoint = *data.JustifiedCheckpoint + f.FinalizedCheckpoint = *data.FinalizedCheckpoint + f.ForkChoiceNodes = data.ForkChoiceNodes + f.ExtraData = data.ExtraData + + return nil +} + +type forkChoiceNodeJSON struct { + Slot string `json:"slot"` + BlockRoot string `json:"block_root"` + ParentRoot string `json:"parent_root"` + JustifiedEpoch string `json:"justified_epoch,omitempty"` + FinalizedEpoch string `json:"finalized_epoch,omitempty"` + Weight string `json:"weight"` + Validity string `json:"validity"` + ExecutionBlockHash string `json:"execution_block_hash"` + PayloadStatus string `json:"payload_status,omitempty"` + ParentPayloadStatus string `json:"parent_payload_status,omitempty"` + PayloadAttesterCount string `json:"payload_attester_count,omitempty"` + PayloadAvailabilityYesCount string `json:"payload_availability_yes_count,omitempty"` + PayloadDataAvailabilityYesCount string `json:"payload_data_availability_yes_count,omitempty"` + ExtraData map[string]any `json:"extra_data,omitempty"` +} + +// MarshalJSON implements json.Marshaler. +func (n *ForkChoiceNode) MarshalJSON() ([]byte, error) { + data := &forkChoiceNodeJSON{ + Slot: strconv.FormatUint(uint64(n.Slot), 10), + BlockRoot: n.BlockRoot.String(), + ParentRoot: n.ParentRoot.String(), + JustifiedEpoch: formatOptionalUint64((*uint64)(n.JustifiedEpoch)), + FinalizedEpoch: formatOptionalUint64((*uint64)(n.FinalizedEpoch)), + Weight: strconv.FormatUint(n.Weight, 10), + Validity: n.Validity.String(), + ExecutionBlockHash: n.ExecutionBlockHash.String(), + PayloadStatus: "", + PayloadAttesterCount: formatOptionalUint64(n.PayloadAttesterCount), + PayloadAvailabilityYesCount: formatOptionalUint64(n.PayloadAvailabilityYesCount), + PayloadDataAvailabilityYesCount: formatOptionalUint64(n.PayloadDataAvailabilityYesCount), + ExtraData: n.ExtraData, + } + + if n.PayloadStatus != v1.ForkChoicePayloadStatusUnknown { + data.PayloadStatus = n.PayloadStatus.String() + } + + if n.ParentPayloadStatus != nil { + data.ParentPayloadStatus = n.ParentPayloadStatus.String() + } + + return json.Marshal(data) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (n *ForkChoiceNode) UnmarshalJSON(input []byte) error { + var data forkChoiceNodeJSON + if err := json.Unmarshal(input, &data); err != nil { + return fmt.Errorf("invalid JSON: %w", err) + } + + slot, err := strconv.ParseUint(data.Slot, 10, 64) + if err != nil { + return fmt.Errorf("invalid value for slot: %w", err) + } + + n.Slot = phase0.Slot(slot) + + if err = decodeRoot(&n.BlockRoot, data.BlockRoot); err != nil { + return fmt.Errorf("invalid value for block root: %w", err) + } + + // The parent root is null for the oldest retained node on some clients. + if data.ParentRoot != "" { + if err = decodeRoot(&n.ParentRoot, data.ParentRoot); err != nil { + return fmt.Errorf("invalid value for parent root: %w", err) + } + } + + justifiedEpoch, err := parseOptionalUint64(data.JustifiedEpoch) + if err != nil { + return fmt.Errorf("invalid value for justified epoch: %w", err) + } + + n.JustifiedEpoch = (*phase0.Epoch)(justifiedEpoch) + + finalizedEpoch, err := parseOptionalUint64(data.FinalizedEpoch) + if err != nil { + return fmt.Errorf("invalid value for finalized epoch: %w", err) + } + + n.FinalizedEpoch = (*phase0.Epoch)(finalizedEpoch) + + if n.Weight, err = strconv.ParseUint(data.Weight, 10, 64); err != nil { + return fmt.Errorf("invalid value for weight: %w", err) + } + + if n.Validity, err = v1.ForkChoiceNodeValidityFromString(data.Validity); err != nil { + return fmt.Errorf("invalid value for validity: %w", err) + } + + if err = decodeRoot(&n.ExecutionBlockHash, data.ExecutionBlockHash); err != nil { + return fmt.Errorf("invalid value for execution block hash: %w", err) + } + + if data.PayloadStatus != "" { + if n.PayloadStatus, err = v1.ForkChoicePayloadStatusFromString(data.PayloadStatus); err != nil { + return err + } + } + + if data.ParentPayloadStatus != "" { + var status v1.ForkChoicePayloadStatus + if status, err = v1.ForkChoicePayloadStatusFromString(data.ParentPayloadStatus); err != nil { + return fmt.Errorf("invalid value for parent payload status: %w", err) + } + + n.ParentPayloadStatus = &status + } + + if n.PayloadAttesterCount, err = parseOptionalUint64(data.PayloadAttesterCount); err != nil { + return fmt.Errorf("invalid value for payload attester count: %w", err) + } + + if n.PayloadAvailabilityYesCount, err = parseOptionalUint64(data.PayloadAvailabilityYesCount); err != nil { + return fmt.Errorf("invalid value for payload availability yes count: %w", err) + } + + if n.PayloadDataAvailabilityYesCount, err = parseOptionalUint64(data.PayloadDataAvailabilityYesCount); err != nil { + return fmt.Errorf("invalid value for payload data availability yes count: %w", err) + } + + n.ExtraData = data.ExtraData + + return nil +} + +func decodeRoot(dst *phase0.Root, input string) error { + return dst.UnmarshalJSON([]byte(strconv.Quote(input))) +} + +// parseOptionalUint64 parses a decimal string, returning nil for an empty one. +func parseOptionalUint64(input string) (*uint64, error) { + if input == "" { + return nil, nil + } + + value, err := strconv.ParseUint(input, 10, 64) + if err != nil { + return nil, err + } + + return &value, nil +} + +func formatOptionalUint64(value *uint64) string { + if value == nil { + return "" + } + + return strconv.FormatUint(*value, 10) +} diff --git a/internal/types/forkchoice_normalize.go b/internal/types/forkchoice_normalize.go new file mode 100644 index 0000000..43e3404 --- /dev/null +++ b/internal/types/forkchoice_normalize.go @@ -0,0 +1,285 @@ +package types + +import ( + "encoding/json" + "strconv" + + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" +) + +// ForkChoiceFromV1 normalizes a GET /eth/v1/debug/fork_choice response. +// +// Since Gloas some clients return one v1 node per fork choice node, i.e. up to +// three nodes per block with no payload status to tell them apart. Those are +// collapsed to the heaviest node per block, which is the block's pending node. +func ForkChoiceFromV1(fc *v1.ForkChoice) *ForkChoice { + nodes := make([]*ForkChoiceNode, 0, len(fc.ForkChoiceNodes)) + byRoot := make(map[phase0.Root]int, len(fc.ForkChoiceNodes)) + + for _, node := range fc.ForkChoiceNodes { + justifiedEpoch := node.JustifiedEpoch + finalizedEpoch := node.FinalizedEpoch + + normalized := &ForkChoiceNode{ + Slot: node.Slot, + BlockRoot: node.BlockRoot, + ParentRoot: node.ParentRoot, + JustifiedEpoch: &justifiedEpoch, + FinalizedEpoch: &finalizedEpoch, + Weight: node.Weight, + Validity: v1Validity(node), + ExecutionBlockHash: node.ExecutionBlockHash, + ExtraData: node.ExtraData, + } + + if i, exists := byRoot[node.BlockRoot]; exists { + if normalized.Weight > nodes[i].Weight { + nodes[i] = normalized + } + + continue + } + + byRoot[node.BlockRoot] = len(nodes) + nodes = append(nodes, normalized) + } + + return &ForkChoice{ + JustifiedCheckpoint: fc.JustifiedCheckpoint, + FinalizedCheckpoint: fc.FinalizedCheckpoint, + ForkChoiceNodes: nodes, + ExtraData: fc.ExtraData, + } +} + +// v1Validity is a v1 node's validity. Lighthouse reports a Gloas block whose +// payload has not been revealed yet as not_yet_revealed (and mirrors it in +// extra_data["execution_status"]); its execution is simply not verified yet, +// i.e. optimistic. go-eth2-client decodes it as +// ForkChoiceNodeValidityNotYetRevealed; older versions decoded it as unknown, +// keeping the original under extra_data["validity"], as fork choices already +// stored or received from older sentries may still have it. +func v1Validity(node *v1.ForkChoiceNode) v1.ForkChoiceNodeValidity { + switch { + case node.Validity == v1.ForkChoiceNodeValidityNotYetRevealed: + return v1.ForkChoiceNodeValidityOptimistic + case node.Validity == v1.ForkChoiceNodeValidityUnknown && + (node.ExtraData["validity"] == "not_yet_revealed" || node.ExtraData["execution_status"] == "not_yet_revealed"): + return v1.ForkChoiceNodeValidityOptimistic + default: + return node.Validity + } +} + +// ForkChoiceFromV2 normalizes a GET /eth/v2/debug/fork_choice response, which +// go-eth2-client has checked against the spec (ethereum/beacon-APIs#615), so +// every node carries all of its values. +func ForkChoiceFromV2(fc *v1.ForkChoiceV2) *ForkChoice { + nodes := make([]*ForkChoiceNode, 0, len(fc.ForkChoiceNodes)) + + for _, node := range fc.ForkChoiceNodes { + var ( + justifiedEpoch = node.JustifiedCheckpoint.Epoch + finalizedEpoch = node.FinalizedCheckpoint.Epoch + payloadAttesterCount = node.PayloadAttesterCount + payloadAvailabilityYesCount = node.PayloadAvailabilityYesCount + payloadDataAvailabilityYesCount = node.PayloadDataAvailabilityYesCount + ) + + normalized := &ForkChoiceNode{ + Slot: node.Slot, + BlockRoot: node.BlockRoot, + ParentRoot: node.ParentRoot, + JustifiedEpoch: &justifiedEpoch, + FinalizedEpoch: &finalizedEpoch, + Weight: uint64(node.Weight), + Validity: node.Validity, + ExecutionBlockHash: phase0.Root(node.ExecutionBlockHash), + PayloadStatus: node.PayloadStatus, + PayloadAttesterCount: &payloadAttesterCount, + PayloadAvailabilityYesCount: &payloadAvailabilityYesCount, + PayloadDataAvailabilityYesCount: &payloadDataAvailabilityYesCount, + ExtraData: node.ExtraData, + } + + if node.ParentPayloadStatus != nil { + parentPayloadStatus := *node.ParentPayloadStatus + normalized.ParentPayloadStatus = &parentPayloadStatus + } + + nodes = append(nodes, normalized) + } + + return NormalizeV2(&ForkChoice{ + JustifiedCheckpoint: fc.JustifiedCheckpoint, + FinalizedCheckpoint: fc.FinalizedCheckpoint, + ForkChoiceNodes: nodes, + ExtraData: fc.ExtraData, + }) +} + +// nilIfEmpty returns nil for an empty extra data map. The spec requires +// extra_data, so clients send an empty object when they have none; forky +// represents (and encodes) no extra data as nil, however it was received. +func nilIfEmpty(extraData map[string]any) map[string]any { + if len(extraData) == 0 { + return nil + } + + return extraData +} + +// NormalizeV2 normalizes, in place, a fork choice with one node per (block +// root, payload status) pair. +// +// Empty and full nodes, which the spec points at their own block's pending +// node, keep the parent block's root as their parent root. Values a node +// lacks, as nodes received through Xatu from older sentries or from a client +// predating the spec may, are filled in from its extra_data or its block's +// other nodes, and missing parent payload statuses are inferred where possible. +// Empty extra data is set to nil. +func NormalizeV2(fc *ForkChoice) *ForkChoice { + blocks := make(map[phase0.Root]map[v1.ForkChoicePayloadStatus]*ForkChoiceNode, len(fc.ForkChoiceNodes)) + + fc.ExtraData = nilIfEmpty(fc.ExtraData) + + for _, node := range fc.ForkChoiceNodes { + node.ExtraData = nilIfEmpty(node.ExtraData) + fillFromExtraData(node) + + if blocks[node.BlockRoot] == nil { + blocks[node.BlockRoot] = make(map[v1.ForkChoicePayloadStatus]*ForkChoiceNode, 3) + } + + blocks[node.BlockRoot][node.PayloadStatus] = node + } + + for _, node := range fc.ForkChoiceNodes { + block := blocks[node.BlockRoot] + + // The spec points empty and full nodes at their own block's pending + // node; keep parent_root as the parent block's root. + if pending := block[v1.ForkChoicePayloadStatusPending]; pending != nil && node.ParentRoot == node.BlockRoot { + node.ParentRoot = pending.ParentRoot + } + + for _, sibling := range block { + if sibling != node { + fillFromSibling(node, sibling) + } + } + + if node.ParentPayloadStatus == nil { + node.ParentPayloadStatus = inferParentPayloadStatus(node, blocks[node.ParentRoot]) + } + } + + return fc +} + +// fillFromExtraData fills values that some clients only provide in extra_data. +func fillFromExtraData(node *ForkChoiceNode) { + if node.JustifiedEpoch == nil { + node.JustifiedEpoch = (*phase0.Epoch)(extraDataUint64(node.ExtraData, "justified_epoch")) + } + + if node.FinalizedEpoch == nil { + node.FinalizedEpoch = (*phase0.Epoch)(extraDataUint64(node.ExtraData, "finalized_epoch")) + } + + if node.PayloadAttesterCount == nil { + node.PayloadAttesterCount = extraDataUint64(node.ExtraData, "payload_attester_count") + } + + if node.PayloadAvailabilityYesCount == nil { + node.PayloadAvailabilityYesCount = extraDataUint64(node.ExtraData, "payload_availability_yes_count") + } + + if node.PayloadDataAvailabilityYesCount == nil { + node.PayloadDataAvailabilityYesCount = extraDataUint64(node.ExtraData, "payload_data_availability_yes_count") + } +} + +// fillFromSibling fills block-level values, which are the same for all nodes +// of a block, that some clients only provide on one of a block's nodes: +// Prysm on the pending node, Lodestar's PTC counts on the full node. +func fillFromSibling(node, sibling *ForkChoiceNode) { + if node.JustifiedEpoch == nil { + node.JustifiedEpoch = sibling.JustifiedEpoch + } + + if node.FinalizedEpoch == nil { + node.FinalizedEpoch = sibling.FinalizedEpoch + } + + if node.PayloadAttesterCount == nil { + node.PayloadAttesterCount = sibling.PayloadAttesterCount + } + + if node.PayloadAvailabilityYesCount == nil { + node.PayloadAvailabilityYesCount = sibling.PayloadAvailabilityYesCount + } + + if node.PayloadDataAvailabilityYesCount == nil { + node.PayloadDataAvailabilityYesCount = sibling.PayloadDataAvailabilityYesCount + } +} + +// inferParentPayloadStatus infers the payload status of a node's parent fork +// choice node. Empty and full nodes hang off their own block's pending node. +// A pending node's execution block hash is the bid's parent block hash, which +// is the parent block's payload hash if the block was built on the full +// parent, or the parent's own parent block hash if it was built on the empty +// parent. Returns nil when it cannot be determined, e.g. for clients that put +// the bid's own block hash on pending nodes. +func inferParentPayloadStatus( + node *ForkChoiceNode, + parent map[v1.ForkChoicePayloadStatus]*ForkChoiceNode, +) *v1.ForkChoicePayloadStatus { + switch node.PayloadStatus { + case v1.ForkChoicePayloadStatusEmpty, v1.ForkChoicePayloadStatusFull: + return new(v1.ForkChoicePayloadStatusPending) + case v1.ForkChoicePayloadStatusPending: + if full := parent[v1.ForkChoicePayloadStatusFull]; full != nil && full.ExecutionBlockHash == node.ExecutionBlockHash { + return new(v1.ForkChoicePayloadStatusFull) + } + + if pending := parent[v1.ForkChoicePayloadStatusPending]; pending != nil && pending.ExecutionBlockHash == node.ExecutionBlockHash { + return new(v1.ForkChoicePayloadStatusEmpty) + } + case v1.ForkChoicePayloadStatusUnknown: + } + + return nil +} + +// extraDataUint64 reads a uint64 from extra_data, which clients encode either +// as a decimal string or as a JSON number. +func extraDataUint64(extraData map[string]any, key string) *uint64 { + var ( + value uint64 + err error + ) + + switch raw := extraData[key].(type) { + case string: + value, err = strconv.ParseUint(raw, 10, 64) + case float64: + if raw < 0 || raw != float64(uint64(raw)) { + return nil + } + + value = uint64(raw) + case json.Number: + value, err = strconv.ParseUint(raw.String(), 10, 64) + default: + return nil + } + + if err != nil { + return nil + } + + return &value +} diff --git a/internal/types/forkchoice_test.go b/internal/types/forkchoice_test.go new file mode 100644 index 0000000..18c5b2f --- /dev/null +++ b/internal/types/forkchoice_test.go @@ -0,0 +1,381 @@ +package types + +import ( + "encoding/hex" + "encoding/json" + "os" + "strconv" + "strings" + "testing" + + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// loadV2 decodes a GET /eth/v2/debug/fork_choice response, as go-eth2-client +// does: strictly to the spec. +func loadV2(t *testing.T, path string) *v1.ForkChoiceV2 { + t.Helper() + + raw, err := os.ReadFile(path) + require.NoError(t, err) + + var wrapped struct { + Data *v1.ForkChoiceV2 `json:"data"` + } + + require.NoError(t, json.Unmarshal(raw, &wrapped)) + require.NotNil(t, wrapped.Data) + + return wrapped.Data +} + +// loadPartialV2 decodes a v2 response captured from a client predating the +// spec into forky's fork choice, leaving values the client does not provide +// unset, as a fork choice received through Xatu from an older sentry may be. +func loadPartialV2(t *testing.T, path string) *ForkChoice { + t.Helper() + + raw, err := os.ReadFile(path) + require.NoError(t, err) + + var envelope map[string]json.RawMessage + + require.NoError(t, json.Unmarshal(raw, &envelope)) + + if data, wrapped := envelope["data"]; wrapped { + raw = data + } + + var response struct { + JustifiedCheckpoint phase0.Checkpoint `json:"justified_checkpoint"` + FinalizedCheckpoint phase0.Checkpoint `json:"finalized_checkpoint"` + ForkChoiceNodes []struct { + Slot string `json:"slot"` + BlockRoot string `json:"block_root"` + ParentRoot string `json:"parent_root"` + PayloadStatus string `json:"payload_status"` + Weight string `json:"weight"` + Validity string `json:"validity"` + ExecutionBlockHash string `json:"execution_block_hash"` + JustifiedEpoch string `json:"justified_epoch"` + FinalizedEpoch string `json:"finalized_epoch"` + PayloadAttesters string `json:"payload_attester_count"` + PayloadAvailable string `json:"payload_availability_yes_count"` + PayloadDataAvailabe string `json:"payload_data_availability_yes_count"` + ExtraData map[string]any `json:"extra_data"` + } `json:"fork_choice_nodes"` + ExtraData map[string]any `json:"extra_data"` + } + + require.NoError(t, json.Unmarshal(raw, &response)) + + optional := func(value string) *uint64 { + if value == "" { + return nil + } + + parsed, err := strconv.ParseUint(value, 10, 64) + require.NoError(t, err) + + return &parsed + } + + root := func(value string) phase0.Root { + var r phase0.Root + if value == "" { + return r + } + + decoded, err := hex.DecodeString(strings.TrimPrefix(value, "0x")) + require.NoError(t, err) + copy(r[:], decoded) + + return r + } + + fc := &ForkChoice{ + JustifiedCheckpoint: response.JustifiedCheckpoint, + FinalizedCheckpoint: response.FinalizedCheckpoint, + ExtraData: response.ExtraData, + } + + for _, node := range response.ForkChoiceNodes { + payloadStatus, err := v1.ForkChoicePayloadStatusFromString(node.PayloadStatus) + require.NoError(t, err) + + validity, err := v1.ForkChoiceNodeValidityFromString(node.Validity) + require.NoError(t, err) + + fc.ForkChoiceNodes = append(fc.ForkChoiceNodes, &ForkChoiceNode{ + Slot: phase0.Slot(*optional(node.Slot)), + BlockRoot: root(node.BlockRoot), + ParentRoot: root(node.ParentRoot), + JustifiedEpoch: (*phase0.Epoch)(optional(node.JustifiedEpoch)), + FinalizedEpoch: (*phase0.Epoch)(optional(node.FinalizedEpoch)), + Weight: *optional(node.Weight), + Validity: validity, + ExecutionBlockHash: root(node.ExecutionBlockHash), + PayloadStatus: payloadStatus, + PayloadAttesterCount: optional(node.PayloadAttesters), + PayloadAvailabilityYesCount: optional(node.PayloadAvailable), + PayloadDataAvailabilityYesCount: optional(node.PayloadDataAvailabe), + ExtraData: node.ExtraData, + }) + } + + return fc +} + +func status(s v1.ForkChoicePayloadStatus) *v1.ForkChoicePayloadStatus { + return new(s) +} + +// The fixtures are the four newest blocks of each client's v2 response on +// Sepolia, a chain where every block was built on its parent's full payload, +// captured before the clients implemented the merged spec. The oldest block's +// parent is not in the fixture. NormalizeV2 fills in what they lack. +func TestNormalizeV2PartialClients(t *testing.T) { + const oldestSlot = phase0.Slot(11299989) + + tests := []struct { + name string + fixture string + // epochs is whether the client provides checkpoint epochs. + epochs bool + // inferable is whether pending nodes carry the bid's parent block + // hash, from which the parent payload status can be inferred. + inferable bool + }{ + {name: "Teku", fixture: "testdata/forkchoice_v2_teku.json", epochs: false, inferable: true}, + {name: "Prysm", fixture: "testdata/forkchoice_v2_prysm.json", epochs: true, inferable: false}, + {name: "Lodestar", fixture: "testdata/forkchoice_v2_lodestar.json", epochs: true, inferable: true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fc := NormalizeV2(loadPartialV2(t, test.fixture)) + require.Len(t, fc.ForkChoiceNodes, 12) + + for _, node := range fc.ForkChoiceNodes { + assert.NotEmpty(t, node.PayloadStatus) + assert.NotNil(t, node.PayloadAttesterCount, "slot %d %s", node.Slot, node.PayloadStatus) + assert.NotNil(t, node.PayloadAvailabilityYesCount) + assert.NotNil(t, node.PayloadDataAvailabilityYesCount) + assert.Equal(t, test.epochs, node.JustifiedEpoch != nil, "slot %d %s", node.Slot, node.PayloadStatus) + assert.Equal(t, test.epochs, node.FinalizedEpoch != nil) + assert.NotEqual(t, node.BlockRoot, node.ParentRoot) + + switch { + case node.PayloadStatus != v1.ForkChoicePayloadStatusPending: + assert.Equal(t, status(v1.ForkChoicePayloadStatusPending), node.ParentPayloadStatus) + case test.inferable && node.Slot != oldestSlot: + assert.Equal(t, status(v1.ForkChoicePayloadStatusFull), node.ParentPayloadStatus, "slot %d", node.Slot) + default: + assert.Nil(t, node.ParentPayloadStatus, "slot %d", node.Slot) + } + } + }) + } +} + +func TestForkChoiceFromV2SpecParentLinks(t *testing.T) { + var ( + rootA = phase0.Root{0xa} + rootB = phase0.Root{0xb} + rootC = phase0.Root{0xc} + hashX = phase0.Hash32{0x1} + hashA = phase0.Hash32{0x2} + hashB = phase0.Hash32{0x3} + ) + + node := func(slot phase0.Slot, root, parent phase0.Root, s v1.ForkChoicePayloadStatus, hash phase0.Hash32) *v1.ForkChoiceNodeV2 { + return &v1.ForkChoiceNodeV2{ + Slot: slot, + BlockRoot: root, + PayloadStatus: s, + ParentRoot: parent, + ExecutionBlockHash: hash, + } + } + + // Block A has pending and empty nodes with the chain's previous payload + // hash X, and a full node with its own payload hash A. Block B follows the + // spec, pointing its empty and full nodes at its own pending node, and was + // built on A's full payload. Block C was built on B's empty payload. + fc := ForkChoiceFromV2(&v1.ForkChoiceV2{ + ForkChoiceNodes: []*v1.ForkChoiceNodeV2{ + node(1, rootA, phase0.Root{}, v1.ForkChoicePayloadStatusPending, hashX), + node(1, rootA, phase0.Root{}, v1.ForkChoicePayloadStatusEmpty, hashX), + node(1, rootA, phase0.Root{}, v1.ForkChoicePayloadStatusFull, hashA), + node(2, rootB, rootA, v1.ForkChoicePayloadStatusPending, hashA), + node(2, rootB, rootB, v1.ForkChoicePayloadStatusEmpty, hashA), + node(2, rootB, rootB, v1.ForkChoicePayloadStatusFull, hashB), + node(3, rootC, rootB, v1.ForkChoicePayloadStatusPending, hashA), + }, + }) + + nodes := fc.ForkChoiceNodes + require.Len(t, nodes, 7) + + assert.Nil(t, nodes[0].ParentPayloadStatus, "parent not in the tree") + assert.Equal(t, status(v1.ForkChoicePayloadStatusFull), nodes[3].ParentPayloadStatus) + assert.Equal(t, status(v1.ForkChoicePayloadStatusEmpty), nodes[6].ParentPayloadStatus) + + for _, n := range nodes[4:6] { + assert.Equal(t, rootA, n.ParentRoot, "empty and full nodes keep the parent block's root") + assert.Equal(t, status(v1.ForkChoicePayloadStatusPending), n.ParentPayloadStatus) + } +} + +func TestForkChoiceFromV2KeepsProvidedParentPayloadStatus(t *testing.T) { + fc := ForkChoiceFromV2(&v1.ForkChoiceV2{ + ForkChoiceNodes: []*v1.ForkChoiceNodeV2{ + { + Slot: 1, + BlockRoot: phase0.Root{0xb}, + ParentRoot: phase0.Root{0xa}, + PayloadStatus: v1.ForkChoicePayloadStatusPending, + ParentPayloadStatus: status(v1.ForkChoicePayloadStatusEmpty), + }, + }, + }) + + assert.Equal(t, status(v1.ForkChoicePayloadStatusEmpty), fc.ForkChoiceNodes[0].ParentPayloadStatus) +} + +// Nimbus returns two v1 nodes per Gloas block, with no payload status. +func TestForkChoiceFromV1CollapsesDuplicateBlocks(t *testing.T) { + raw, err := os.ReadFile("testdata/forkchoice_v1_nimbus.json") + require.NoError(t, err) + + var data v1.ForkChoice + + require.NoError(t, json.Unmarshal(raw, &data)) + require.Len(t, data.ForkChoiceNodes, 8) + require.NotEmpty(t, data.ExtraData) + + fc := ForkChoiceFromV1(&data) + require.Len(t, fc.ForkChoiceNodes, 4) + assert.Equal(t, data.ExtraData, fc.ExtraData) + + heaviest := make(map[phase0.Root]uint64) + for _, node := range data.ForkChoiceNodes { + heaviest[node.BlockRoot] = max(heaviest[node.BlockRoot], node.Weight) + } + + for _, node := range fc.ForkChoiceNodes { + assert.Equal(t, heaviest[node.BlockRoot], node.Weight) + assert.Empty(t, node.PayloadStatus) + assert.NotNil(t, node.JustifiedEpoch) + } +} + +// Frames stored before Gloas support must decode and re-encode unchanged. +func TestFrameLegacyJSON(t *testing.T) { + raw, err := os.ReadFile("testdata/frame_v1_legacy.json") + require.NoError(t, err) + + var frame Frame + + require.NoError(t, json.Unmarshal(raw, &frame)) + require.NoError(t, frame.Validate()) + require.Len(t, frame.Data.ForkChoiceNodes, 3) + + encoded, err := json.Marshal(&frame) + require.NoError(t, err) + assert.JSONEq(t, string(raw), string(encoded)) +} + +func TestFrameGzipRoundTrip(t *testing.T) { + data := ForkChoiceFromV2(loadV2(t, "testdata/forkchoice_v2_spec.json")) + // Exercise the store extra data too; an empty one is omitted when encoded. + data.ExtraData = map[string]any{"proposer_boost_root": "0xb0"} + + frame := &Frame{ + Data: data, + Metadata: FrameMetadata{ + ID: "id", + Node: "node", + }, + } + + gz, err := frame.AsGzipJSON() + require.NoError(t, err) + + var decoded Frame + + require.NoError(t, decoded.FromGzipJSON(gz)) + assert.Equal(t, frame.Data, decoded.Data) +} + +// Lighthouse reports a Gloas block whose payload has not been revealed as +// not_yet_revealed, which go-eth2-client decodes as unknown. +func TestForkChoiceFromV1NotYetRevealedIsOptimistic(t *testing.T) { + var data v1.ForkChoice + + require.NoError(t, json.Unmarshal([]byte(`{ + "justified_checkpoint": {"epoch": "1", "root": "0x0000000000000000000000000000000000000000000000000000000000000000"}, + "finalized_checkpoint": {"epoch": "0", "root": "0x0000000000000000000000000000000000000000000000000000000000000000"}, + "fork_choice_nodes": [{ + "slot": "11300470", + "block_root": "0x0100000000000000000000000000000000000000000000000000000000000000", + "parent_root": "0x0200000000000000000000000000000000000000000000000000000000000000", + "justified_epoch": "1", + "finalized_epoch": "0", + "weight": "0", + "validity": "not_yet_revealed", + "execution_block_hash": "0x0300000000000000000000000000000000000000000000000000000000000000", + "extra_data": {} + }] + }`), &data)) + + assert.Equal(t, v1.ForkChoiceNodeValidityNotYetRevealed, data.ForkChoiceNodes[0].Validity) + + fc := ForkChoiceFromV1(&data) + require.Len(t, fc.ForkChoiceNodes, 1) + assert.Equal(t, v1.ForkChoiceNodeValidityOptimistic, fc.ForkChoiceNodes[0].Validity) + + // Older go-eth2-client versions decoded it as unknown, keeping the original in extra_data. + data.ForkChoiceNodes[0].Validity = v1.ForkChoiceNodeValidityUnknown + data.ForkChoiceNodes[0].ExtraData = map[string]any{"validity": "not_yet_revealed"} + + fc = ForkChoiceFromV1(&data) + assert.Equal(t, v1.ForkChoiceNodeValidityOptimistic, fc.ForkChoiceNodes[0].Validity) +} + +// TestForkChoiceFromV2Spec normalizes a response in the shape the merged spec +// defines, derived from the Teku fixture: every node carries its checkpoints, +// PTC counts and parent payload status. +func TestForkChoiceFromV2Spec(t *testing.T) { + original := loadV2(t, "testdata/forkchoice_v2_spec.json") + fc := ForkChoiceFromV2(original) + require.Len(t, fc.ForkChoiceNodes, 12) + + pendingRoots := make(map[phase0.Root]phase0.Root) + + for _, node := range fc.ForkChoiceNodes { + if node.PayloadStatus == v1.ForkChoicePayloadStatusPending { + pendingRoots[node.BlockRoot] = node.ParentRoot + } + } + + for i, node := range fc.ForkChoiceNodes { + require.NotNil(t, node.JustifiedEpoch) + assert.Equal(t, original.ForkChoiceNodes[i].JustifiedCheckpoint.Epoch, *node.JustifiedEpoch) + require.NotNil(t, node.FinalizedEpoch) + require.NotNil(t, node.PayloadAttesterCount) + assert.Equal(t, original.ForkChoiceNodes[i].PayloadAttesterCount, *node.PayloadAttesterCount) + assert.Equal(t, uint64(original.ForkChoiceNodes[i].Weight), node.Weight) + + if node.PayloadStatus != v1.ForkChoicePayloadStatusPending { + assert.Equal(t, pendingRoots[node.BlockRoot], node.ParentRoot, "empty and full nodes keep the parent block's root") + assert.Equal(t, status(v1.ForkChoicePayloadStatusPending), node.ParentPayloadStatus) + } + } + + // The oldest block's parent is not retained; the others were built on their parent's full payload. + assert.Nil(t, fc.ForkChoiceNodes[0].ParentPayloadStatus) +} diff --git a/internal/types/frame.go b/internal/types/frame.go index 4bf4fdf..bdfc72c 100644 --- a/internal/types/frame.go +++ b/internal/types/frame.go @@ -9,8 +9,8 @@ import ( "math/rand" "time" - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" + v1 "github.com/ethpandaops/go-eth2-client/api/v1" + "github.com/ethpandaops/go-eth2-client/spec/phase0" "github.com/google/uuid" ) @@ -54,7 +54,7 @@ func (f *FrameMetadata) Validate() error { // Frame holds a fork choice dump with a timestamp. type Frame struct { // Data is the fork choice dump. - Data *v1.ForkChoice `json:"data"` + Data *ForkChoice `json:"data"` // Metadata is the metadata of the frame. Metadata FrameMetadata `json:"metadata"` } @@ -140,7 +140,7 @@ func GenerateFakeFrame() *Frame { } } -func GenerateFakeForkChoice() *v1.ForkChoice { +func GenerateFakeForkChoice() *ForkChoice { justifiedCheckpoint := phase0.Checkpoint{ //nolint:gosec // This is a test function. Epoch: phase0.Epoch(rand.Uint64()), @@ -179,9 +179,9 @@ func GenerateFakeForkChoice() *v1.ForkChoice { } } - return &v1.ForkChoice{ + return ForkChoiceFromV1(&v1.ForkChoice{ JustifiedCheckpoint: justifiedCheckpoint, FinalizedCheckpoint: finalizedCheckpoint, ForkChoiceNodes: nodes, - } + }) } diff --git a/internal/types/testdata/forkchoice_v1_nimbus.json b/internal/types/testdata/forkchoice_v1_nimbus.json new file mode 100644 index 0000000..0dadda7 --- /dev/null +++ b/internal/types/testdata/forkchoice_v1_nimbus.json @@ -0,0 +1,177 @@ +{ + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "fork_choice_nodes": [ + { + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "1888000000000", + "validity": "VALID", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "best_child": "0x0000000000000000000000000000000000000000000000000000000000000000", + "best_descendant": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "4508512500000", + "validity": "VALID", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "best_child": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "best_descendant": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } + }, + { + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "1856000000000", + "validity": "VALID", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "u_justified_checkpoint": { + "epoch": "353124", + "root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc" + }, + "u_finalized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "best_child": "0x0000000000000000000000000000000000000000000000000000000000000000", + "best_descendant": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "2652512500000", + "validity": "VALID", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "best_child": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "best_descendant": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } + }, + { + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "1896000000000", + "validity": "VALID", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "u_justified_checkpoint": { + "epoch": "353124", + "root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc" + }, + "u_finalized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "best_child": "0x0000000000000000000000000000000000000000000000000000000000000000", + "best_descendant": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "756512500000", + "validity": "VALID", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "best_child": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "best_descendant": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } + }, + { + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "756512500000", + "validity": "VALID", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "u_justified_checkpoint": { + "epoch": "353124", + "root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc" + }, + "u_finalized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "best_child": "0x0000000000000000000000000000000000000000000000000000000000000000", + "best_descendant": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "weight": "0", + "validity": "VALID", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "best_child": "0x0000000000000000000000000000000000000000000000000000000000000000", + "best_descendant": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + ], + "extra_data": { + "confirmed_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "current_epoch_observed_justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "previous_epoch_greatest_unrealized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "previous_slot_head": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "current_slot_head": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } +} diff --git a/internal/types/testdata/forkchoice_v2_lodestar.json b/internal/types/testdata/forkchoice_v2_lodestar.json new file mode 100644 index 0000000..008eaaf --- /dev/null +++ b/internal/types/testdata/forkchoice_v2_lodestar.json @@ -0,0 +1,289 @@ +{ + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "fork_choice_nodes": [ + { + "payload_status": "pending", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "6396512500000", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333468", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353123", + "unrealized_finalized_epoch": "353122", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "empty", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333468", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353123", + "unrealized_finalized_epoch": "353122", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "full", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333468", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353123", + "unrealized_finalized_epoch": "353122", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "gas_limit": "191139849" + } + }, + { + "payload_status": "pending", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333480", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "empty", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333480", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "full", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333480", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "gas_limit": "190953190" + } + }, + { + "payload_status": "pending", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333492", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "empty", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333492", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "full", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333492", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "gas_limit": "191139666" + } + }, + { + "payload_status": "pending", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333504", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "empty", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333504", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": null, + "payload_availability_yes_count": null, + "payload_data_availability_yes_count": null, + "gas_limit": null + } + }, + { + "payload_status": "full", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "execution_optimistic": false, + "timestamp": "1791333504", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "332", + "payload_availability_yes_count": "332", + "payload_data_availability_yes_count": "332", + "gas_limit": "191326324" + } + } + ], + "extra_data": { + "unrealized_justified_checkpoint": { + "epoch": "353124", + "root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc" + }, + "unrealized_finalized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "proposer_boost_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "previous_proposer_boost_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "head_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } +} diff --git a/internal/types/testdata/forkchoice_v2_prysm.json b/internal/types/testdata/forkchoice_v2_prysm.json new file mode 100644 index 0000000..96db8fe --- /dev/null +++ b/internal/types/testdata/forkchoice_v2_prysm.json @@ -0,0 +1,229 @@ +{ + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "fork_choice_nodes": [ + { + "payload_status": "pending", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "6396512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "balance": "1888000000000", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:37:48.459262005 +0000 UTC m=+84154.037352850", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353123", + "unrealized_finalized_epoch": "353122", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512" + } + }, + { + "payload_status": "empty", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:37:48.459262005 +0000 UTC m=+84154.037352850" + } + }, + { + "payload_status": "full", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:37:48.55071432 +0000 UTC m=+84154.128805145", + "gas_limit": "191139849" + } + }, + { + "payload_status": "pending", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "balance": "1856000000000", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:00.320782724 +0000 UTC m=+84165.898873569", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512" + } + }, + { + "payload_status": "empty", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:00.320782724 +0000 UTC m=+84165.898873569" + } + }, + { + "payload_status": "full", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:00.407294624 +0000 UTC m=+84165.985385469", + "gas_limit": "190953190" + } + }, + { + "payload_status": "pending", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "balance": "1896000000000", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:12.447481008 +0000 UTC m=+84178.025571853", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512" + } + }, + { + "payload_status": "empty", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:12.447481008 +0000 UTC m=+84178.025571853" + } + }, + { + "payload_status": "full", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:12.554044319 +0000 UTC m=+84178.132135144", + "gas_limit": "191139666" + } + }, + { + "payload_status": "pending", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "balance": "756512500000", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:24.147622033 +0000 UTC m=+84189.725712878", + "target": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "justified_epoch": "353123", + "finalized_epoch": "353122", + "unrealized_justified_epoch": "353124", + "unrealized_finalized_epoch": "353123", + "payload_attester_count": "369", + "payload_availability_yes_count": "369", + "payload_data_availability_yes_count": "369" + } + }, + { + "payload_status": "empty", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:24.147622033 +0000 UTC m=+84189.725712878" + } + }, + { + "payload_status": "full", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "extra_data": { + "balance": "0", + "execution_optimistic": false, + "timestamp": "2026-10-07 00:38:24.376577942 +0000 UTC m=+84189.954668777", + "gas_limit": "191326324" + } + } + ], + "extra_data": { + "unrealized_justified_checkpoint": { + "epoch": "353124", + "root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc" + }, + "unrealized_finalized_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "proposer_boost_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "previous_proposer_boost_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "head_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5" + } +} diff --git a/internal/types/testdata/forkchoice_v2_spec.json b/internal/types/testdata/forkchoice_v2_spec.json new file mode 100644 index 0000000..492073c --- /dev/null +++ b/internal/types/testdata/forkchoice_v2_spec.json @@ -0,0 +1,279 @@ +{ + "data": { + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "fork_choice_nodes": [ + { + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "payload_status": "pending", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "parent_payload_status": null, + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "6396512500000", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "payload_status": "empty", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "payload_status": "full", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "payload_status": "pending", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_payload_status": "full", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "payload_status": "empty", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "payload_status": "full", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "payload_status": "pending", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_payload_status": "full", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "payload_status": "empty", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "0", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "payload_status": "full", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "extra_data": {} + }, + { + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "payload_status": "pending", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_payload_status": "full", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "extra_data": {} + }, + { + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "payload_status": "empty", + "parent_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "extra_data": {} + }, + { + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "payload_status": "full", + "parent_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_payload_status": "pending", + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "extra_data": {} + } + ], + "extra_data": {} + } +} diff --git a/internal/types/testdata/forkchoice_v2_teku.json b/internal/types/testdata/forkchoice_v2_teku.json new file mode 100644 index 0000000..f313c60 --- /dev/null +++ b/internal/types/testdata/forkchoice_v2_teku.json @@ -0,0 +1,243 @@ +{ + "data": { + "justified_checkpoint": { + "epoch": "353123", + "root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f" + }, + "finalized_checkpoint": { + "epoch": "353122", + "root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e" + }, + "fork_choice_nodes": [ + { + "payload_status": "pending", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "6396512500000", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0xe5e7289ef9efcabf00e88ad25b983c1928c511aa3ef80f7ce4fa9cde2c7c7b59", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353123", + "unrealized_justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_finalized_epoch": "353122", + "unrealized_finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "extra_data": {} + }, + { + "payload_status": "empty", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x1e9424df27053e5a0bba5311d090aa681b335339a8dcf2c2b4bf382e783f5a35", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0xe5e7289ef9efcabf00e88ad25b983c1928c511aa3ef80f7ce4fa9cde2c7c7b59", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353123", + "unrealized_justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_finalized_epoch": "353122", + "unrealized_finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "extra_data": {} + }, + { + "payload_status": "full", + "slot": "11299989", + "block_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "parent_root": "0x7fb0613617ff6e9296fbd6d99b23318db552184979ea8bdfeb7242a727e092ee", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0xe5e7289ef9efcabf00e88ad25b983c1928c511aa3ef80f7ce4fa9cde2c7c7b59", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353123", + "unrealized_justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_finalized_epoch": "353122", + "unrealized_finalized_root": "0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e", + "extra_data": {} + }, + { + "payload_status": "pending", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "4508512500000", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x2975a308e487f5769d0d4acc08f4ad22cb792e8134bb7bb27d03092a7ed38fd3", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "empty", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x564f3094885709e9a7a0dd72371415444bad18fdda9fcb0b4de72097cafbde5b", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x2975a308e487f5769d0d4acc08f4ad22cb792e8134bb7bb27d03092a7ed38fd3", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "full", + "slot": "11299990", + "block_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "parent_root": "0x40c801c610dddc20e122cce6b4464c1a52c45055c6a937f559db7256f645be40", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x2975a308e487f5769d0d4acc08f4ad22cb792e8134bb7bb27d03092a7ed38fd3", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "pending", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "2652512500000", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x9d7409d02b954c1e06440420dc5e5016b37dd919a58c2a2f3ac5996f725da4c1", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "empty", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0xa9139b2e3d6d8485c9113ca79f7814ffaa1d90ad6ca0c738c068c943b44d75b1", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x9d7409d02b954c1e06440420dc5e5016b37dd919a58c2a2f3ac5996f725da4c1", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "full", + "slot": "11299991", + "block_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "parent_root": "0x6fd29650081a93c7b426fa828fb79e3c7b095347a0312c469a83ff2e6950d317", + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "512", + "payload_availability_yes_count": "512", + "payload_data_availability_yes_count": "512", + "state_root": "0x9d7409d02b954c1e06440420dc5e5016b37dd919a58c2a2f3ac5996f725da4c1", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "pending", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "756512500000", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "state_root": "0x4f449d30580e5f13e9e18b622913ac0fdb9201ff11aeca3b6067f063756b02df", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "empty", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x01e630868fb7260bcaf47703c658b078c2114c18945bff5e7c001ca66df474a4", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "state_root": "0x4f449d30580e5f13e9e18b622913ac0fdb9201ff11aeca3b6067f063756b02df", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + }, + { + "payload_status": "full", + "slot": "11299992", + "block_root": "0x6f3c564f799b9135a1563469615817dc33da0af0309a0fb0af876fed695f16d5", + "parent_root": "0x1336b6be88ebbdd156addf8a74138a0149559f8bf832ac3d3c7959d1dbca7f78", + "weight": "0", + "validity": "valid", + "execution_block_hash": "0x78adb32a2f47003432eb27d75c9c991b8d6ea90d9f114e901c480457aed52deb", + "payload_attester_count": "422", + "payload_availability_yes_count": "422", + "payload_data_availability_yes_count": "422", + "state_root": "0x4f449d30580e5f13e9e18b622913ac0fdb9201ff11aeca3b6067f063756b02df", + "justified_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "unrealised_justified_epoch": "353124", + "unrealized_justified_root": "0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc", + "unrealised_finalized_epoch": "353123", + "unrealized_finalized_root": "0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f", + "extra_data": {} + } + ], + "extra_data": {} + } +} diff --git a/internal/types/testdata/frame_v1_legacy.json b/internal/types/testdata/frame_v1_legacy.json new file mode 100644 index 0000000..af2f1f4 --- /dev/null +++ b/internal/types/testdata/frame_v1_legacy.json @@ -0,0 +1 @@ +{"data":{"justified_checkpoint":{"epoch":"353123","root":"0x23fa50345581dbaea096882d62f14d0f7a47100e66a17184d31243e9aa6b409f"},"finalized_checkpoint":{"epoch":"353122","root":"0xd2e6f75402afa7c6ff1ab1e327ed44a81df38a9f7d7a4dba274bbe6122d6077e"},"fork_choice_nodes":[{"slot":"11299978","block_root":"0xb016fca7b5b8b7457b4ff276cc6daa7c6e72752c43d773bcb3b74f68259abc02","parent_root":"0x46855b6a455eb348b6fc921dcf2203797e734b802938e3d8a3c3b003698c1967","justified_epoch":"353123","finalized_epoch":"353122","weight":"4504512500000","validity":"valid","execution_block_hash":"0x09c0532187317a849f9ceba3baf60db926d54cf0e665d3579b222997e58b306d","extra_data":{"balance":"1856000000000","execution_optimistic":false,"target":"0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc","timestamp":"2026-10-07 00:35:36.329747313 +0000 UTC m=+84021.907838148","unrealized_finalized_epoch":"353122","unrealized_justified_epoch":"353123"}},{"slot":"11299979","block_root":"0xc88284ded55cf0884239c5485629f17a277828d04b512d65b4d5d2955f38cced","parent_root":"0xb016fca7b5b8b7457b4ff276cc6daa7c6e72752c43d773bcb3b74f68259abc02","justified_epoch":"353123","finalized_epoch":"353122","weight":"2648512500000","validity":"valid","execution_block_hash":"0x6db0da19575ca7be5b5defbb1ef9c82cbe00528bdcb45a57f9c1e33e70a2d1ef","extra_data":{"balance":"1892000000000","execution_optimistic":false,"target":"0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc","timestamp":"2026-10-07 00:35:48.333024848 +0000 UTC m=+84033.911115673","unrealized_finalized_epoch":"353122","unrealized_justified_epoch":"353123"}},{"slot":"11299980","block_root":"0x60e808951e4928c2e47a352773a9c4ed3a7ca2654232429b8edd874dc4b1b780","parent_root":"0xc88284ded55cf0884239c5485629f17a277828d04b512d65b4d5d2955f38cced","justified_epoch":"353123","finalized_epoch":"353122","weight":"756512500000","validity":"valid","execution_block_hash":"0x739f60c4a3e63367235c98af81f9e39728fe7eeae8b3f7bee43d3452f6fa748d","extra_data":{"balance":"756512500000","execution_optimistic":false,"target":"0x4f15c072239dc09f797029206509e59f907c9a3a3767bec18c3074855f0ab0cc","timestamp":"2026-10-07 00:36:00.295919018 +0000 UTC m=+84045.874009853","unrealized_finalized_epoch":"353122","unrealized_justified_epoch":"353123"}}]},"metadata":{"id":"09a1be3a-24ea-4703-84f6-b254d2da6e91","node":"prysm-reth-001","fetched_at":"2026-10-07T10:36:00.829779723+10:00","wall_clock_slot":"11299980","wall_clock_epoch":"353124","labels":null,"consensus_client":"prysm","event_source":"beacon_node"}} diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 96fbcb0..58fc8af 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -69,6 +69,7 @@ export type { MetadataQuery2, PaginationCursor, PaginationResponse, + PayloadStatus, Root, Slot, SlotString, diff --git a/web/src/api/types.gen.ts b/web/src/api/types.gen.ts index 2456691..dc07e3b 100644 --- a/web/src/api/types.gen.ts +++ b/web/src/api/types.gen.ts @@ -111,12 +111,27 @@ export type Checkpoint = { root: Root; }; +/** + * The Gloas payload status of a fork-choice node. A Gloas block has a + * `pending` node, which is the parent of its `empty` node (the block + * without its execution payload) and its `full` node (the block with it). + * + */ +export type PayloadStatus = 'pending' | 'empty' | 'full'; + +/** + * A fork-choice node. Pre-Gloas, and for beacon nodes without the v2 + * fork-choice endpoint, there is one node per block. Post-Gloas a block can + * have a `pending`, an `empty` and a `full` node, which share `slot`, + * `block_root` and `parent_root`. + * + */ export type ForkChoiceNode = { slot: SlotString; block_root: Root; parent_root: Root; - justified_epoch: EpochString; - finalized_epoch: EpochString; + justified_epoch?: EpochString; + finalized_epoch?: EpochString; /** * The node's fork-choice weight, serialized as a string. */ @@ -126,6 +141,27 @@ export type ForkChoiceNode = { */ validity: 'valid' | 'invalid' | 'optimistic' | 'unknown'; execution_block_hash: Root; + payload_status?: PayloadStatus; + /** + * The payload status of the parent fork-choice node: for a `pending` + * node, whether the block was built on its parent block's `empty` or + * `full` payload; for `empty` and `full` nodes, `pending`. Absent if + * unknown. + * + */ + parent_payload_status?: PayloadStatus; + /** + * Number of Payload Timeliness Committee positions with a recorded vote. + */ + payload_attester_count?: string; + /** + * Number of PTC positions voting that the payload was received on time. + */ + payload_availability_yes_count?: string; + /** + * Number of PTC positions voting that the blob data is available. + */ + payload_data_availability_yes_count?: string; extra_data?: { [key: string]: unknown; }; @@ -138,6 +174,14 @@ export type ForkChoice = { justified_checkpoint: Checkpoint; finalized_checkpoint: Checkpoint; fork_choice_nodes: Array; + /** + * Client-specific values of the fork-choice store, such as the + * unrealized justified checkpoint. + * + */ + extra_data?: { + [key: string]: unknown; + }; }; export type Frame = { diff --git a/web/src/api/zod.gen.ts b/web/src/api/zod.gen.ts index 62eb0c8..497e6a8 100644 --- a/web/src/api/zod.gen.ts +++ b/web/src/api/zod.gen.ts @@ -100,15 +100,35 @@ export const zCheckpoint = z.object({ root: zRoot, }); +/** + * The Gloas payload status of a fork-choice node. A Gloas block has a + * `pending` node, which is the parent of its `empty` node (the block + * without its execution payload) and its `full` node (the block with it). + * + */ +export const zPayloadStatus = z.enum(['pending', 'empty', 'full']); + +/** + * A fork-choice node. Pre-Gloas, and for beacon nodes without the v2 + * fork-choice endpoint, there is one node per block. Post-Gloas a block can + * have a `pending`, an `empty` and a `full` node, which share `slot`, + * `block_root` and `parent_root`. + * + */ export const zForkChoiceNode = z.object({ slot: zSlotString, block_root: zRoot, parent_root: zRoot, - justified_epoch: zEpochString, - finalized_epoch: zEpochString, + justified_epoch: z.optional(zEpochString), + finalized_epoch: z.optional(zEpochString), weight: z.string(), validity: z.enum(['valid', 'invalid', 'optimistic', 'unknown']), execution_block_hash: zRoot, + payload_status: z.optional(zPayloadStatus), + parent_payload_status: z.optional(zPayloadStatus), + payload_attester_count: z.optional(z.string()), + payload_availability_yes_count: z.optional(z.string()), + payload_data_availability_yes_count: z.optional(z.string()), extra_data: z.optional(z.record(z.string(), z.unknown())), }); @@ -119,6 +139,7 @@ export const zForkChoice = z.object({ justified_checkpoint: zCheckpoint, finalized_checkpoint: zCheckpoint, fork_choice_nodes: z.array(zForkChoiceNode), + extra_data: z.optional(z.record(z.string(), z.unknown())), }); export const zFrame = z.object({ diff --git a/web/src/components/AggregatedNode.tsx b/web/src/components/AggregatedNode.tsx index bd073fa..6fc71c2 100644 --- a/web/src/components/AggregatedNode.tsx +++ b/web/src/components/AggregatedNode.tsx @@ -5,8 +5,10 @@ import classNames from 'clsx'; import HoverCard from '@components/HoverCard'; import { AggregatedNodeCard } from '@components/NodeCards'; +import PayloadTide, { PayloadChip } from '@components/PayloadTide'; import ProgressCircle from '@components/ProgressCircle'; import { NODE_STYLES, NodeStyleKey } from '@utils/nodeStyles'; +import { PayloadStage, summarizePayloads } from '@utils/payload'; function AggregatedNode({ id, @@ -20,6 +22,8 @@ function AggregatedNode({ valid, optimistic, total, + payloads = [], + lead = false, x, y, radius, @@ -39,6 +43,10 @@ function AggregatedNode({ valid: number; optimistic: number; total: number; + // payload stage and progress per source, for sources reporting payloads. + payloads?: { stage: PayloadStage; progress: number }[]; + // a lead node (the newest slots) sloshes its Tide as the canvas is panned. + lead?: boolean; x: number; y: number; radius: number; @@ -67,6 +75,10 @@ function AggregatedNode({ const handleActivate = () => onClick?.(hash); + const payload = summarizePayloads(payloads); + const payloadStages: Partial> = {}; + for (const { stage } of payloads) payloadStages[stage] = (payloadStages[stage] ?? 0) + 1; + return ( } referenceProps={{ @@ -116,6 +129,7 @@ function AggregatedNode({ height: `${radius * 2}px`, }} > + {payload && }

{title}

-

+

{hash.substring(0, 6)}…{hash.substring(hash.length - 4)}

-

+

{finalizedCheckpoints > 0 || justifiedCheckpoints > 0 ? ( <> @@ -151,6 +171,9 @@ function AggregatedNode({ {seen}/{total}

+ {payload && ( + + )} )}
diff --git a/web/src/components/FrameBlockSummary.tsx b/web/src/components/FrameBlockSummary.tsx index 95acf18..b9b70a8 100644 --- a/web/src/components/FrameBlockSummary.tsx +++ b/web/src/components/FrameBlockSummary.tsx @@ -12,6 +12,9 @@ import { import useEthereum from '@contexts/ethereum'; import useSelection, { FrameBlock } from '@contexts/selection'; import { useFrameQuery } from '@hooks/useQuery'; +import { formatExtraDataValue } from '@utils/strings'; + +const PAYLOAD_ORDER = ['pending', 'empty', 'full']; export default function FrameBlockSummary({ frameId, blockRoot }: FrameBlock) { const { clearAll } = useSelection(); @@ -25,7 +28,15 @@ export default function FrameBlockSummary({ frameId, blockRoot }: FrameBlock) { return ; } - const node = data.frame?.data?.fork_choice_nodes?.find(n => n.block_root === blockRoot); + // Since Gloas a block can have a pending, an empty and a full node. + const variants = (data.frame?.data?.fork_choice_nodes ?? []) + .filter(n => n.block_root === blockRoot) + .sort( + (a, b) => + PAYLOAD_ORDER.indexOf(a.payload_status ?? '') - + PAYLOAD_ORDER.indexOf(b.payload_status ?? ''), + ); + const node = variants.find(n => n.payload_status === 'pending') ?? variants[0]; if (!node) { return ; @@ -69,12 +80,33 @@ export default function FrameBlockSummary({ frameId, blockRoot }: FrameBlock) { {node.justified_epoch} {node.finalized_epoch} + {node.payload_status && ( + <> + + + {node.parent_payload_status ?? 'unknown'} + + {node.payload_attester_count !== undefined && ( + + {node.payload_availability_yes_count} timely ·{' '} + {node.payload_data_availability_yes_count} data available ·{' '} + {node.payload_attester_count} voted + + )} + {variants.map(variant => ( + + {variant.weight} + {variant.execution_block_hash} + + ))} + + )} {node.extra_data && ( <> {Object.entries(node.extra_data).map(([key, value]) => ( - {`${value}`} + {formatExtraDataValue(value)} ))} @@ -82,7 +114,7 @@ export default function FrameBlockSummary({ frameId, blockRoot }: FrameBlock) {
1 ? variants : node, null, 2)} filename={`block-${node.block_root}.json`} size="md" text="Block" diff --git a/web/src/components/FrameSummary.tsx b/web/src/components/FrameSummary.tsx index c63763d..dbba6f0 100644 --- a/web/src/components/FrameSummary.tsx +++ b/web/src/components/FrameSummary.tsx @@ -5,6 +5,7 @@ import Loading from '@components/Loading'; import { SummaryCard, SummaryLink, SummaryRow, SummarySection } from '@components/Summary'; import useSelection from '@contexts/selection'; import { useFrameQuery } from '@hooks/useQuery'; +import { formatExtraDataValue } from '@utils/strings'; function parseLabel(label: string): [string, string | undefined] { const [key, value] = label.split('=', 2); @@ -43,6 +44,18 @@ export default function FrameSummary({ id }: { id: string }) { {data.frame.metadata.wall_clock_epoch} {data.frame.metadata.wall_clock_slot} + + + {formatExtraDataValue(data.frame.data.justified_checkpoint)} + + + {formatExtraDataValue(data.frame.data.finalized_checkpoint)} + + {Object.entries(data.frame.data.extra_data ?? {}).map(([key, value]) => ( + + {formatExtraDataValue(value)} + + ))} {data.frame.metadata.labels && data.frame.metadata.labels.length > 0 && ( <> diff --git a/web/src/components/GraphPrimitives.stories.tsx b/web/src/components/GraphPrimitives.stories.tsx index dac6aa8..0b151b3 100644 --- a/web/src/components/GraphPrimitives.stories.tsx +++ b/web/src/components/GraphPrimitives.stories.tsx @@ -11,6 +11,7 @@ import SlotBoundary from '@components/SlotBoundary'; import TruncationMarker from '@components/TruncationMarker'; import WeightedNode from '@components/WeightedNode'; import { storyBlockRoots } from '@app/stories/fixtures'; +import { BlockPayload, PayloadStage } from '@app/types/graph'; import { withForkyProviders } from '@app/stories/storybook'; /* Always render graph nodes at the radius the real graph uses (see RADIUS in @@ -112,6 +113,194 @@ export const WeightedNodeStates: Story = { ), }; +/* Tide across every state a block can be in: one row per node status, one + * column per Gloas payload stage (plus a block without payload data, as from + * a pre-Gloas frame or a client without the v2 fork choice endpoint), then + * aggregated nodes summarising several sources. Drawn at half size. */ +const PAYLOAD_STAGE_EXAMPLES: { label: string; payload?: BlockPayload }[] = [ + { label: 'no payload data' }, + { label: 'awaiting', payload: { emptyWeight: 0n } }, + { label: 'voting', payload: { emptyWeight: 0n, fullWeight: 0n, attesterCount: 357 } }, + { + label: 'timely', + payload: { emptyWeight: 0n, fullWeight: 0n, attesterCount: 512, availabilityYesCount: 498 }, + }, + { + label: 'late', + payload: { emptyWeight: 0n, fullWeight: 0n, attesterCount: 512, availabilityYesCount: 120 }, + }, + { label: 'undecided', payload: { emptyWeight: 0n, fullWeight: 0n } }, + { + label: 'full', + payload: { + status: 'full', + emptyWeight: 0n, + fullWeight: 756n, + parentPayloadStatus: 'full', + attesterCount: 512, + availabilityYesCount: 512, + dataAvailabilityYesCount: 512, + }, + }, + { + label: 'empty', + payload: { status: 'empty', emptyWeight: 64n, fullWeight: 0n, parentPayloadStatus: 'full' }, + }, +]; + +const NODE_STATUS_EXAMPLES: { + label: string; + type: 'canonical' | 'fork' | 'finalized' | 'justified' | 'detached'; + validity: string; +}[] = [ + { label: 'canonical', type: 'canonical', validity: 'valid' }, + { label: 'fork', type: 'fork', validity: 'valid' }, + { label: 'finalized', type: 'finalized', validity: 'valid' }, + { label: 'justified', type: 'justified', validity: 'valid' }, + { label: 'optimistic', type: 'canonical', validity: 'optimistic' }, + { label: 'invalid', type: 'canonical', validity: 'invalid' }, + { label: 'detached', type: 'detached', validity: 'valid' }, +]; + +const AGGREGATED_PAYLOAD_EXAMPLES: { + label: string; + payloads: { stage: PayloadStage; progress: number }[]; +}[] = [ + { label: 'no payload data', payloads: [] }, + { + label: 'awaiting 3/3', + payloads: [ + { stage: 'awaiting', progress: 0 }, + { stage: 'awaiting', progress: 0 }, + { stage: 'awaiting', progress: 0 }, + ], + }, + { + label: 'voting 3/3', + payloads: [ + { stage: 'voting', progress: 58 }, + { stage: 'voting', progress: 70 }, + { stage: 'voting', progress: 64 }, + ], + }, + { + label: 'timely 2/3', + payloads: [ + { stage: 'timely', progress: 97 }, + { stage: 'timely', progress: 96 }, + { stage: 'voting', progress: 90 }, + ], + }, + { + label: 'late 3/3', + payloads: [ + { stage: 'late', progress: 22 }, + { stage: 'late', progress: 25 }, + { stage: 'late', progress: 20 }, + ], + }, + { + label: 'full 2/3', + payloads: [ + { stage: 'full', progress: 100 }, + { stage: 'full', progress: 100 }, + { stage: 'timely', progress: 97 }, + ], + }, + { + label: 'full 3/3', + payloads: [ + { stage: 'full', progress: 100 }, + { stage: 'full', progress: 100 }, + { stage: 'full', progress: 100 }, + ], + }, + { + label: 'empty 3/3', + payloads: [ + { stage: 'empty', progress: 100 }, + { stage: 'empty', progress: 100 }, + { stage: 'empty', progress: 100 }, + ], + }, +]; + +const MATRIX_COLUMN = 360; +const MATRIX_ROW = 400; + +export const PayloadStages: Story = { + render: () => ( +
+
+ {PAYLOAD_STAGE_EXAMPLES.map(({ label }, column) => ( +

+ {label} +

+ ))} + {NODE_STATUS_EXAMPLES.map(({ label, type, validity }, row) => ( +
+

+ {label} +

+ {PAYLOAD_STAGE_EXAMPLES.map(({ label: stage, payload }, column) => ( + + ))} +
+ ))} +

+ aggregated +

+ {AGGREGATED_PAYLOAD_EXAMPLES.map(({ label, payloads }, column) => ( + + ))} +
+
+ ), +}; + export const AggregatedNodeStates: Story = { render: () => (
diff --git a/web/src/components/Legend.tsx b/web/src/components/Legend.tsx index 5e3f222..1cb3a30 100644 --- a/web/src/components/Legend.tsx +++ b/web/src/components/Legend.tsx @@ -5,6 +5,7 @@ import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/solid'; import classNames from 'clsx'; import { NODE_STYLES, NodeStyleKey } from '@utils/nodeStyles'; +import { PAYLOAD_STAGE_ORDER, PAYLOAD_STAGES } from '@utils/payload'; /* Stage legend for the fork-choice graph's visual vocabulary. Collapsed to * a compact pill by default (mobile stays clean); expands into a glass @@ -178,6 +179,39 @@ function Legend({ + + Payload (Gloas) + +
    + {PAYLOAD_STAGE_ORDER.map(stage => { + const style = PAYLOAD_STAGES[stage]; + return ( +
  • + {/* the stage's typical payload level */} + + + + {style.name} + {style.description} +
  • + ); + })} +
  • + + Edge + Built on the parent's empty payload +
  • +

{RING_NOTE[mode]}

diff --git a/web/src/components/NodeCards.tsx b/web/src/components/NodeCards.tsx index db3caaf..6d35b1a 100644 --- a/web/src/components/NodeCards.tsx +++ b/web/src/components/NodeCards.tsx @@ -3,8 +3,16 @@ import { ReactNode, useState } from 'react'; import { CheckIcon, DocumentDuplicateIcon, EyeIcon, FlagIcon } from '@heroicons/react/20/solid'; import classNames from 'clsx'; +import { BlockPayload } from '@app/types/graph'; import { truncateHash } from '@app/utils/strings'; import { NODE_STYLES, NodeStyleKey } from '@utils/nodeStyles'; +import { + PAYLOAD_STAGE_ORDER, + PAYLOAD_STAGES, + PTC_SIZE, + PayloadStage, + payloadStage, +} from '@utils/payload'; /* Read-only content for the graph hover cards (see HoverCard). One variant * per graph primitive; all of them stay presentational so Storybook can @@ -243,6 +251,7 @@ export function WeightedNodeCard({ validity, weight, weightPercentage, + payload, }: { styleKey: NodeStyleKey; status: string; @@ -253,6 +262,7 @@ export function WeightedNodeCard({ validity: string; weight: string; weightPercentage: number; + payload?: BlockPayload; }) { const formattedWeight = weight && weight !== '0' ? formatWeight(weight) : undefined; @@ -275,11 +285,76 @@ export function WeightedNodeCard({ {parentRoot && {truncateHash(parentRoot)}} + {payload && } ); } +function payloadWeight(weight?: bigint): string { + if (weight === undefined) return '—'; + return weight === 0n ? '0' : formatWeight(weight.toString()).display; +} + +function PayloadStageBadge({ stage }: { stage: PayloadStage }) { + const style = PAYLOAD_STAGES[stage]; + return ( + + {style.name} + + ); +} + +function PayloadRows({ payload }: { payload: BlockPayload }) { + const stage = payloadStage(payload); + return ( + <> + + + + + {payloadWeight(payload.emptyWeight)} / {payloadWeight(payload.fullWeight)} + + {payload.attesterCount !== undefined && ( + + {payload.availabilityYesCount ?? '—'} / {payload.dataAvailabilityYesCount ?? '—'} /{' '} + {payload.attesterCount} of {PTC_SIZE} + + )} + + {payload.parentPayloadStatus ?? 'unknown'} + + + ); +} + +// Payload stages reported by the sources of an aggregated block, in lifecycle +// order. +export function PayloadStageCounts({ + stages, + total, +}: { + stages: Partial>; + total: number; +}) { + return ( + + {PAYLOAD_STAGE_ORDER.filter(stage => stages[stage]).map(stage => ( + + + {stages[stage]}/{total} + + ))} + + ); +} + export function AggregatedNodeCard({ styleKey, status, @@ -294,12 +369,14 @@ export function AggregatedNodeCard({ valid, optimistic, total, + payloadStages = {}, }: { styleKey: NodeStyleKey; status: string; blockRoot: string; slot?: number; epoch?: number; + payloadStages?: Partial>; seen: number; canonical: number; finalizedCheckpoints: number; @@ -334,6 +411,16 @@ export function AggregatedNodeCard({ {seen}/{total}
+ {Object.keys(payloadStages).length > 0 && ( +
+ + sum + n, 0)} + /> + +
+ )} {(finalizedCheckpoints > 0 || justifiedCheckpoints > 0 || notValid > 0 || diff --git a/web/src/components/PayloadTide.tsx b/web/src/components/PayloadTide.tsx new file mode 100644 index 0000000..0aa3212 --- /dev/null +++ b/web/src/components/PayloadTide.tsx @@ -0,0 +1,116 @@ +import { CSSProperties, memo } from 'react'; + +import { + CheckIcon, + ClockIcon, + CubeIcon, + CubeTransparentIcon, + ExclamationTriangleIcon, + HandRaisedIcon, + ScaleIcon, +} from '@heroicons/react/24/outline'; +import classNames from 'clsx'; + +import useTideSlosh from '@hooks/useTideSlosh'; +import { PAYLOAD_STAGES, PayloadStage } from '@utils/payload'; + +const STAGE_ICONS: Record = { + awaiting: ClockIcon, + voting: HandRaisedIcon, + timely: CheckIcon, + late: ExclamationTriangleIcon, + undecided: ScaleIcon, + full: CubeIcon, + empty: CubeTransparentIcon, +}; + +/* Tide: a Gloas block's execution payload as liquid inside its graph node. + * The level rises with the PTC vote and settles near the top once the + * payload is full; an empty payload drains, leaving a dashed rim, and an + * awaited one shows a blinking waterline. Sits behind the node's ring and + * text, so the node's status colors stay in charge. With `slosh` (the lead + * nodes) the liquid rocks as the graph canvas is panned, and crashes with a + * little spray off its crest when it sloshes hard. */ +function PayloadTide({ + stage, + level, + slosh = false, +}: { + stage: PayloadStage; + level: number; + slosh?: boolean; +}) { + const style = PAYLOAD_STAGES[stage]; + const still = style.motion === 'still'; + const { liquidRef, crestRef, spray, settleSpray } = useTideSlosh(slosh && level > 0, level); + + // The waves stay mounted at every level (below the node when drained) so a + // change of level always eases rather than popping in or out. The back wave + // rides just above the crest: turning the other way, slower and rounder, it + // shows as a soft shaded lip over the crest line that swells and thins. + return ( +
+
+
0 ? 100 - level - 2.5 : 104}%` }} + /> +
0 ? 100 - level - 1 : 104}%` }} + /> +
+ {spray.map(drop => ( + settleSpray(drop.id)} + /> + ))} + {stage === 'awaiting' && ( +
+ )} + {stage === 'empty' && ( +
+ )} +
+ ); +} + +/* The payload's stage, inside the node under the block's hash. `value` is + * the PTC count while voting, or the agreeing sources in aggregated views. */ +export function PayloadChip({ stage, value }: { stage: PayloadStage; value?: string }) { + const style = PAYLOAD_STAGES[stage]; + const Icon = STAGE_ICONS[stage]; + + return ( + + + + {style.short} + + {value && {value}} + + ); +} + +export default memo(PayloadTide); diff --git a/web/src/components/WeightedNode.tsx b/web/src/components/WeightedNode.tsx index 2405e8c..98cb20a 100644 --- a/web/src/components/WeightedNode.tsx +++ b/web/src/components/WeightedNode.tsx @@ -4,8 +4,11 @@ import classNames from 'clsx'; import HoverCard from '@components/HoverCard'; import { WeightedNodeCard } from '@components/NodeCards'; +import PayloadTide, { PayloadChip } from '@components/PayloadTide'; import ProgressCircle from '@components/ProgressCircle'; +import { BlockPayload } from '@app/types/graph'; import { NODE_STYLES, NodeStyleKey } from '@utils/nodeStyles'; +import { payloadProgress, payloadStage, PTC_SIZE, tideLevel } from '@utils/payload'; function WeightedNode({ id, @@ -20,6 +23,8 @@ function WeightedNode({ slot, epoch, parentRoot, + payload, + lead = false, className, onClick, }: { @@ -35,6 +40,9 @@ function WeightedNode({ slot?: number; epoch?: number; parentRoot?: string; + payload?: BlockPayload; + // a lead node (the newest slots) sloshes its Tide as the canvas is panned. + lead?: boolean; className?: string; onClick?: (hash: string) => void; }) { @@ -52,6 +60,7 @@ function WeightedNode({ : validity.toUpperCase(); const handleActivate = () => onClick?.(hash); + const stage = payload ? payloadStage(payload) : undefined; return ( } referenceProps={{ @@ -98,6 +108,13 @@ function WeightedNode({ height: `${radius * 2}px`, }} > + {stage && payload && ( + + )}

{label}

-

+

{hash.substring(0, 6)}…{hash.substring(hash.length - 4)}

-

+

{weight && weight !== '0' ? weight : ' '}

+ {stage && payload && ( + + )}
)} diff --git a/web/src/hooks/useGraph.ts b/web/src/hooks/useGraph.ts index 9a9eea4..1593a4e 100644 --- a/web/src/hooks/useGraph.ts +++ b/web/src/hooks/useGraph.ts @@ -27,6 +27,7 @@ interface EdgeRelationship { interface EdgeData { id: string; canonical: boolean; + builtOnEmpty: boolean; source: EdgeRelationship; target: EdgeRelationship; } @@ -80,6 +81,7 @@ function generateEdgeData({ return { id: edge, canonical: graph.getSourceAttribute(edge, 'canonical'), + builtOnEmpty: graph.getEdgeAttribute(edge, 'builtOnEmpty') ?? false, source: { id: graph.getSourceAttribute(edge, 'blockRoot'), x: (graph.getSourceAttribute(edge, 'slot') - slotStart) * spacingX + spacingX, diff --git a/web/src/hooks/useTideSlosh.ts b/web/src/hooks/useTideSlosh.ts new file mode 100644 index 0000000..a32cd15 --- /dev/null +++ b/web/src/hooks/useTideSlosh.ts @@ -0,0 +1,156 @@ +import { useEffect, useRef, useState } from 'react'; + +import { subscribeSlosh } from '@utils/slosh'; + +// Spring constants, per 60 Hz frame: a natural period of about a second and +// light damping, so the liquid rocks a few times before settling. +const STIFFNESS = 0.012; +const DAMPING = 0.055; +// Tilt kick (degrees per frame) per px/ms change in the canvas velocity. +const KICK = 1.4; +const MAX_TILT = 14; +const MAX_SPIN = 3.5; +const REST = 0.03; + +// A slosh peaking past this tilt crashes: spray leaps off the high side's +// crest and falls back towards the middle. At most this often, this many. +const SPLASH_TILT = 5; +const SPLASH_INTERVAL_MS = 320; +const MAX_SPRAY = 24; +// Above this level the surface is out of view, so there is nothing to crash. +const SPLASH_MAX_LEVEL = 92; + +export interface TideSpray { + id: number; + // start position, percent of the node: on the crest at the high side. + x: number; + y: number; + // flight, percent of the node: sideways travel, peak lift (negative is + // up) and where it lands relative to its start. + dx: number; + rise: number; + fall: number; + size: number; + duration: number; + delay: number; +} + +const clamp = (value: number, limit: number) => Math.min(Math.max(value, -limit), limit); +const between = (min: number, max: number) => min + Math.random() * (max - min); + +let nextSprayId = 0; + +// Spray for a slosh peaking at `tilt` degrees over liquid at `level`. A +// clockwise (positive) tilt raises the left side, so the wave crashes from +// the left towards the middle, and vice versa. +function crash(tilt: number, level: number): TideSpray[] { + const fromLeft = tilt > 0; + const slope = Math.tan((tilt * Math.PI) / 180); + const count = Math.min(4 + Math.round(Math.abs(tilt) / 3), 9); + + return Array.from({ length: count }, () => { + const x = fromLeft ? between(14, 32) : between(68, 86); + return { + id: nextSprayId++, + x, + // the tilted surface: rotating the liquid about the node's center + // moves the surface by (x - 50) * tan(tilt). + y: 100 - level + (x - 50) * slope, + dx: (fromLeft ? 1 : -1) * between(6, 16), + rise: -between(4, 11), + fall: between(5, 11), + size: between(1.2, 2.6), + duration: between(0.65, 1.05), + delay: between(0, 0.12), + }; + }); +} + +/* Sloshes a Tide when the graph canvas is panned: the canvas's acceleration + * kicks a damped spring whose angle tilts the liquid (it lags behind the + * motion, piling up on the trailing side) and rocks back to level. When a + * slosh peaks hard the wave crashes: spray leaps off the crest on the high + * side and falls back into the liquid, and the crest flashes like foam. + * Runs an animation frame loop only while the liquid moves, and not at all + * for reduced motion. */ +export default function useTideSlosh(enabled: boolean, level: number) { + const liquidRef = useRef(null); + const crestRef = useRef(null); + const levelRef = useRef(level); + const [spray, setSpray] = useState([]); + + useEffect(() => { + levelRef.current = level; + }, [level]); + + useEffect(() => { + if (!enabled) return; + if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return; + + let tilt = 0; + let spin = 0; + let lastVelocity = 0; + let lastFrame = 0; + let lastSplash = 0; + let frame = 0; + + const render = () => { + const el = liquidRef.current; + if (el) el.style.transform = tilt === 0 ? '' : `rotate(${tilt.toFixed(2)}deg)`; + }; + + const splash = (now: number) => { + const liquidLevel = levelRef.current; + if (Math.abs(tilt) < SPLASH_TILT || liquidLevel > SPLASH_MAX_LEVEL) return; + if (now - lastSplash < SPLASH_INTERVAL_MS) return; + lastSplash = now; + const spawned = crash(tilt, liquidLevel); + setSpray(current => [...current, ...spawned].slice(-MAX_SPRAY)); + crestRef.current?.animate?.([{ filter: 'brightness(1.7)' }, { filter: 'brightness(1)' }], { + duration: 520, + easing: 'ease-out', + }); + }; + + const step = (now: number) => { + const dt = Math.min((now - lastFrame) / (1000 / 60), 3); + lastFrame = now; + const previousSpin = spin; + spin = clamp(spin + (-STIFFNESS * tilt - DAMPING * spin) * dt, MAX_SPIN); + tilt = clamp(tilt + spin * dt, MAX_TILT); + // the slosh peaks where it stops rising and turns back + if (Math.sign(spin) !== Math.sign(previousSpin)) splash(now); + if (Math.abs(tilt) < REST && Math.abs(spin) < REST) { + tilt = 0; + spin = 0; + frame = 0; + render(); + return; + } + render(); + frame = requestAnimationFrame(step); + }; + + const unsubscribe = subscribeSlosh(velocity => { + // Accelerating the canvas one way piles the liquid up on the trailing + // side: a clockwise tilt raises the left when moving right. + spin = clamp(spin + (velocity - lastVelocity) * KICK, MAX_SPIN); + lastVelocity = velocity; + if (!frame) { + lastFrame = performance.now(); + frame = requestAnimationFrame(step); + } + }); + + return () => { + unsubscribe(); + if (frame) cancelAnimationFrame(frame); + tilt = 0; + render(); + }; + }, [enabled]); + + const settleSpray = (id: number) => setSpray(current => current.filter(s => s.id !== id)); + + return { liquidRef, crestRef, spray, settleSpray }; +} diff --git a/web/src/index.css b/web/src/index.css index 936c944..be1e5b8 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -126,6 +126,13 @@ --color-optimistic-edge: var(--optimistic-edge-value); --color-optimistic-ring: var(--optimistic-ring-value); + /* Gloas execution payload: the Tide liquid inside graph nodes. A hue of + * its own (blue, between the accent cyan and justified indigo) so a + * block's payload never reads as a block status. `-surface` = the wave + * crest drawn over the liquid body. */ + --color-payload: var(--payload-value); + --color-payload-surface: var(--payload-surface-value); + /* Timeline markers */ --color-marker: var(--marker-value); --color-marker-active: var(--marker-active-value); @@ -178,6 +185,94 @@ html:not(.dark) .hero-gradient-text { background-size: 28px 28px; } +/* Tide: a Gloas block's execution payload drawn as liquid inside its graph + * node. Each wave is a rounded square far larger than the node, turning + * slowly, so its top edge reads as a moving surface; the inline `top` sets + * the level and eases to each new snapshot's level, rising or draining as + * frames arrive or the timeline is scrubbed. Decided payloads pause the + * turn (rather than dropping it, which would snap the wave's angle). */ +.tide-wave { + position: absolute; + left: -60%; + width: 220%; + aspect-ratio: 1; + border-radius: 43%; + animation: tide-spin 7s linear infinite; + transition: + top 1.1s cubic-bezier(0.22, 1, 0.36, 1), + background-color 0.6s ease, + border-color 0.6s ease; +} +.tide-wave-back { + border-radius: 40%; + animation: tide-spin 11s linear infinite reverse; +} +.tide-still { + animation-play-state: paused; +} +.tide-blink { + animation: tide-blink 1.6s ease-in-out infinite; +} +@keyframes tide-spin { + to { + transform: rotate(360deg); + } +} +@keyframes tide-blink { + 0%, + 100% { + opacity: 0.25; + } + 50% { + opacity: 1; + } +} +/* Spray thrown off a Tide's crest when its slosh peaks (see useTideSlosh): + * each drop leaps up and over (easing out), then falls back into the liquid + * (easing in, like gravity) and fades. Margins in % track the node's width, + * which is also its height. */ +.tide-spray { + position: absolute; + border-radius: 9999px; + opacity: 0; + animation: tide-spray var(--tide-spray-duration, 0.9s) linear var(--tide-spray-delay, 0s) + forwards; +} +@keyframes tide-spray { + 0% { + margin-left: 0; + margin-top: 0; + opacity: 0; + transform: scale(0.6); + animation-timing-function: cubic-bezier(0.2, 0.7, 0.4, 1); + } + 12% { + opacity: 0.95; + } + 45% { + margin-left: calc(var(--tide-spray-dx) * 0.55); + margin-top: var(--tide-spray-rise); + transform: scale(1); + animation-timing-function: cubic-bezier(0.5, 0, 0.85, 0.45); + } + 100% { + margin-left: var(--tide-spray-dx); + margin-top: var(--tide-spray-fall); + opacity: 0; + transform: scale(0.45); + } +} +@media (prefers-reduced-motion: reduce) { + .tide-wave, + .tide-blink { + animation: none; + transition: none; + } + .tide-spray { + display: none; + } +} + /* Glass chrome: the floating console dock + blurred header. Glass is for * chrome only — content panels stay solid for readability. */ .glass-chrome { @@ -217,6 +312,12 @@ html:not(.dark) .hero-gradient-text { --accent-deep: oklch(55% 0.12 205); --accent-deeper: oklch(48% 0.11 207); + /* Execution payload family (Gloas), hue ~240. */ + --payload-bright: oklch(72% 0.14 240); + --payload-brighter: oklch(84% 0.1 238); + --payload-strong: oklch(52% 0.14 245); + --payload-deep: oklch(42% 0.13 248); + /* Status families. Hues: canonical 163, fork 60, finalized 322, * justified 277, invalid 25, optimistic 100. Bands (shared L/C per * role): bright = dark-mode text/arc, dim = dark node fill, shade = @@ -293,6 +394,8 @@ html:not(.dark) .hero-gradient-text { --faint-value: var(--ink-400); --accent-value: var(--accent-deep); --accent-hover-value: var(--accent-deeper); + --payload-value: var(--payload-strong); + --payload-surface-value: var(--payload-deep); --track-value: oklch(0% 0 0 / 5%); --active-value: var(--accent-deep); --active-hover-value: var(--accent-deeper); @@ -376,6 +479,8 @@ html:not(.dark) .hero-gradient-text { --faint-value: var(--ink-500); --accent-value: var(--accent-bright); --accent-hover-value: var(--accent-brighter); + --payload-value: var(--payload-bright); + --payload-surface-value: var(--payload-brighter); --track-value: oklch(100% 0 0 / 6%); --active-value: var(--accent-bright); --active-hover-value: var(--accent-brighter); diff --git a/web/src/parts/Graph.tsx b/web/src/parts/Graph.tsx index 8297a3e..d68cb6b 100644 --- a/web/src/parts/Graph.tsx +++ b/web/src/parts/Graph.tsx @@ -16,6 +16,7 @@ import { useAppNavigate, usePathname } from '@hooks/useAppNavigation'; import SlotBoundary from '@app/components/SlotBoundary'; import { AggregatedNodeAttributes, ProcessedData, WeightedNodeAttributes } from '@app/types/graph'; import { getFrameSlotsBehind } from '@app/utils/graph'; +import { endCanvasPan, reportCanvasPan } from '@app/utils/slosh'; import { truncateHash } from '@app/utils/strings'; import AggregatedNode from '@components/AggregatedNode'; import Edge from '@components/Edge'; @@ -30,6 +31,9 @@ import useGraph from '@hooks/useGraph'; import useWindowSize from '@hooks/useWindowSize'; const RADIUS = 150; +// Nodes in the newest this-many slots (the graph's leading edge) slosh their +// Tide when the canvas is panned. +const LEAD_SLOTS = 4; const SPACING_X = 3 * RADIUS; const SPACING_Y = 4 * RADIUS; @@ -179,7 +183,7 @@ function Graph({ y1={edge.source.y + RADIUS} x2={edge.target.x + RADIUS} y2={edge.target.y + RADIUS} - className="bg-edge" + className={edge.builtOnEmpty ? 'bg-warning' : 'bg-edge'} thickness={8} /> )); @@ -195,6 +199,7 @@ function Graph({ orphaned, validities, slot, + payloads, } = node.attributes as AggregatedNodeAttributes; const type: 'canonical' | 'fork' = canonical ? 'canonical' : 'fork'; @@ -215,6 +220,7 @@ function Graph({ slotEnd - LEAD_SLOTS} x={node.x} y={node.y} seen={seenByNodes.length} @@ -225,6 +231,7 @@ function Graph({ valid={validities.filter(v => ['valid', 'optimistic'].includes(v.validity)).length} optimistic={validities.filter(v => v.validity === 'optimistic').length} total={data.length} + payloads={payloads} type={type} hash={blockRoot} slot={slot} @@ -251,6 +258,7 @@ function Graph({ blockRoot, parentRoot, slot, + payload, } = node.attributes as WeightedNodeAttributes; let type: 'canonical' | 'fork' | 'finalized' | 'justified' | 'detached' | 'invalid' = @@ -261,6 +269,7 @@ function Graph({ slotEnd - LEAD_SLOTS} x={node.x} y={node.y} weight={`${weight}`} @@ -269,6 +278,7 @@ function Graph({ validity={validity} hash={blockRoot} parentRoot={parentRoot} + payload={payload} slot={slot} epoch={Math.floor(slot / slotsPerEpoch)} radius={RADIUS} @@ -405,9 +415,11 @@ function Graph({ setScale(ref.state.scale); }} maxScale={10} - onPanning={() => { + onPanning={ref => { if (focused) setFocused(false); + reportCanvasPan(ref.state.positionX); }} + onPanningStop={() => endCanvasPan()} > {() => { return ( diff --git a/web/src/types/graph.ts b/web/src/types/graph.ts index 40a63ad..5405d22 100644 --- a/web/src/types/graph.ts +++ b/web/src/types/graph.ts @@ -36,6 +36,8 @@ export interface NodeAttributes { export interface EdgeAttributes { distance: number; + // the child block was built on its parent block's empty payload (Gloas). + builtOnEmpty?: boolean; } export interface GraphAttributes { @@ -64,6 +66,32 @@ export interface AggregatedGraphAttributes extends GraphAttributes { }[]; } +// Where a Gloas block's execution payload stands; see payloadStage in +// @utils/payload. +export type PayloadStage = + | 'awaiting' + | 'voting' + | 'timely' + | 'late' + | 'undecided' + | 'full' + | 'empty'; + +// The Gloas payload of a block, from its pending, empty and full fork-choice +// nodes. +export interface BlockPayload { + // the heavier of the block's empty and full nodes; undefined while tied. + status?: 'empty' | 'full'; + emptyWeight?: bigint; + fullWeight?: bigint; + // which payload of its parent block the block was built on, if known. + parentPayloadStatus?: 'empty' | 'full'; + // Payload Timeliness Committee votes. + attesterCount?: number; + availabilityYesCount?: number; + dataAvailabilityYesCount?: number; +} + export interface WeightedNodeAttributes extends NodeAttributes { checkpoint?: 'finalized' | 'justified'; validity: 'valid' | string; @@ -71,6 +99,7 @@ export interface WeightedNodeAttributes extends NodeAttributes { parentRoot?: string; weight: bigint; weightPercentageComparedToHeaviestNeighbor: number; + payload?: BlockPayload; } export interface AggregatedNodeAttributes extends NodeAttributes { @@ -80,6 +109,8 @@ export interface AggregatedNodeAttributes extends NodeAttributes { highestWeight: bigint; seenByNodes: string[]; canonicalForNodes: string[]; + // payload stage per source, for sources that report payload statuses. + payloads: { node: string; stage: PayloadStage; progress: number }[]; } export type ProcessedData = { diff --git a/web/src/utils/__tests__/graph.test.ts b/web/src/utils/__tests__/graph.test.ts index a969d86..29f6409 100644 --- a/web/src/utils/__tests__/graph.test.ts +++ b/web/src/utils/__tests__/graph.test.ts @@ -14,6 +14,7 @@ import { applyNodeAttributeToAllChildren, applyNodeOffsetToAllChildren, processForkChoiceData, + aggregateProcessedData, GraphError, generateNodeId, } from '@utils/graph'; @@ -959,4 +960,168 @@ describe('graph', () => { expect(graph.edges().length).toBe(data.fork_choice_nodes.length - 2); }); }); + + describe('Gloas payload nodes', () => { + const rootA = '0xaa'; + const rootB = '0xbb'; + const rootC = '0xcc'; + + function gloasNode( + slot: number, + blockRoot: string, + parentRoot: string, + payloadStatus: 'pending' | 'empty' | 'full', + weight: string, + parentPayloadStatus?: 'pending' | 'empty' | 'full', + ): Required['data']['fork_choice_nodes'][number] { + return { + slot: `${slot}`, + block_root: blockRoot, + parent_root: parentRoot, + weight, + validity: 'valid', + execution_block_hash: '0x00', + payload_status: payloadStatus, + parent_payload_status: parentPayloadStatus, + payload_attester_count: payloadStatus === 'full' ? '512' : undefined, + payload_availability_yes_count: payloadStatus === 'full' ? '500' : undefined, + payload_data_availability_yes_count: payloadStatus === 'full' ? '490' : undefined, + }; + } + + function frame(nodes: Required['data']['fork_choice_nodes']): Required { + return { + data: { + finalized_checkpoint: { epoch: '0', root: rootA }, + justified_checkpoint: { epoch: '0', root: rootA }, + fork_choice_nodes: nodes, + }, + metadata: { + id: '1', + node: 'node1', + fetched_at: new Date().toISOString(), + wall_clock_slot: 0, + wall_clock_epoch: 0, + labels: [], + consensus_client: 'lodestar', + event_source: 'beacon_node', + }, + }; + } + + // B was built on A's full payload and C on B's empty payload. + const nodes = [ + gloasNode(1, rootA, '0x00', 'pending', '300'), + gloasNode(1, rootA, '0x00', 'empty', '0', 'pending'), + gloasNode(1, rootA, '0x00', 'full', '200', 'pending'), + gloasNode(2, rootB, rootA, 'pending', '200', 'full'), + gloasNode(2, rootB, rootA, 'empty', '100', 'pending'), + gloasNode(2, rootB, rootA, 'full', '0', 'pending'), + gloasNode(3, rootC, rootB, 'pending', '100', 'empty'), + gloasNode(3, rootC, rootB, 'empty', '0', 'pending'), + gloasNode(3, rootC, rootB, 'full', '0', 'pending'), + ]; + + it('should draw one node per block with its payload', () => { + const { graph } = processForkChoiceData(frame(nodes)); + expect(graph.order).toBe(3); + + const idB = generateNodeId({ slot: 2, blockRoot: rootB, parentRoot: rootA }); + expect(graph.getNodeAttribute(idB, 'weight')).toBe(200n); + expect(graph.getNodeAttribute(idB, 'payload')).toEqual({ + status: 'empty', + emptyWeight: 100n, + fullWeight: 0n, + parentPayloadStatus: 'full', + attesterCount: 512, + availabilityYesCount: 500, + dataAvailabilityYesCount: 490, + }); + + const idC = generateNodeId({ slot: 3, blockRoot: rootC, parentRoot: rootB }); + expect(graph.getNodeAttribute(idC, 'payload')?.status).toBeUndefined(); + }); + + it('should mark edges to blocks built on an empty parent payload', () => { + const { graph } = processForkChoiceData(frame(nodes)); + const idA = generateNodeId({ slot: 1, blockRoot: rootA, parentRoot: '0x00' }); + const idB = generateNodeId({ slot: 2, blockRoot: rootB, parentRoot: rootA }); + const idC = generateNodeId({ slot: 3, blockRoot: rootC, parentRoot: rootB }); + + expect(graph.getEdgeAttribute(graph.edge(idA, idB), 'builtOnEmpty')).toBe(false); + expect(graph.getEdgeAttribute(graph.edge(idB, idC), 'builtOnEmpty')).toBe(true); + }); + + it('should aggregate payload stages and empty-parent edges across sources', () => { + const graph = aggregateProcessedData([ + processForkChoiceData(frame(nodes)), + processForkChoiceData({ + ...frame(nodes), + metadata: { ...frame(nodes).metadata, id: '2', node: 'node2' }, + }), + ]); + const idA = generateNodeId({ slot: 1, blockRoot: rootA, parentRoot: '0x00' }); + const idB = generateNodeId({ slot: 2, blockRoot: rootB, parentRoot: rootA }); + const idC = generateNodeId({ slot: 3, blockRoot: rootC, parentRoot: rootB }); + + expect(graph.getNodeAttribute(idB, 'payloads')).toEqual([ + { node: 'node1', stage: 'empty', progress: 100 }, + { node: 'node2', stage: 'empty', progress: 100 }, + ]); + // C's weights are tied but the PTC voted its payload timely. + expect(graph.getNodeAttribute(idC, 'payloads').map(p => p.stage)).toEqual([ + 'timely', + 'timely', + ]); + expect(graph.getEdgeAttribute(graph.edge(idA, idB), 'builtOnEmpty')).toBe(false); + expect(graph.getEdgeAttribute(graph.edge(idB, idC), 'builtOnEmpty')).toBe(true); + }); + + it('should await the payload until the block has a full node', () => { + const { graph } = processForkChoiceData( + frame([ + gloasNode(1, rootA, '0x00', 'pending', '300'), + gloasNode(1, rootA, '0x00', 'empty', '0', 'pending'), + ]), + ); + const idA = generateNodeId({ slot: 1, blockRoot: rootA, parentRoot: '0x00' }); + const payload = graph.getNodeAttribute(idA, 'payload'); + expect(payload?.status).toBeUndefined(); + expect(payload?.fullWeight).toBeUndefined(); + }); + + it('should treat a payload that never arrived but drew votes as empty', () => { + const { graph } = processForkChoiceData( + frame([ + gloasNode(1, rootA, '0x00', 'pending', '300'), + gloasNode(1, rootA, '0x00', 'empty', '64', 'pending'), + ]), + ); + const idA = generateNodeId({ slot: 1, blockRoot: rootA, parentRoot: '0x00' }); + expect(graph.getNodeAttribute(idA, 'payload')?.status).toBe('empty'); + }); + + it.each<['pending' | 'full']>([['full'], ['pending']])( + 'should not give a pre-Gloas block reported as a single %s node a payload', + payloadStatus => { + const { graph } = processForkChoiceData( + frame([gloasNode(1, rootA, '0x00', payloadStatus, '300')]), + ); + const idA = generateNodeId({ slot: 1, blockRoot: rootA, parentRoot: '0x00' }); + expect(graph.getNodeAttribute(idA, 'payload')).toBeUndefined(); + }, + ); + + // Frames stored before payload statuses were recorded hold up to three + // indistinguishable nodes per block. + it('should keep the heaviest node of blocks without payload statuses', () => { + const legacy = nodes.map(({ payload_status, parent_payload_status, ...node }) => node); + const { graph } = processForkChoiceData(frame(legacy)); + expect(graph.order).toBe(3); + + const idB = generateNodeId({ slot: 2, blockRoot: rootB, parentRoot: rootA }); + expect(graph.getNodeAttribute(idB, 'weight')).toBe(200n); + expect(graph.getNodeAttribute(idB, 'payload')).toBeUndefined(); + }); + }); }); diff --git a/web/src/utils/__tests__/payload.test.ts b/web/src/utils/__tests__/payload.test.ts new file mode 100644 index 0000000..8d1eae2 --- /dev/null +++ b/web/src/utils/__tests__/payload.test.ts @@ -0,0 +1,100 @@ +import { BlockPayload } from '@app/types/graph'; +import { + PTC_SIZE, + TIDE_FULL_LEVEL, + TIDE_RISE_LEVEL, + payloadProgress, + payloadStage, + summarizePayloads, + tideLevel, +} from '@utils/payload'; + +describe('payload', () => { + const received: BlockPayload = { emptyWeight: 0n, fullWeight: 0n }; + + describe('payloadStage', () => { + it.each<[string, BlockPayload, ReturnType]>([ + ['awaiting without a full node', { emptyWeight: 0n }, 'awaiting'], + ['undecided without PTC counts', received, 'undecided'], + ['voting while the PTC votes', { ...received, attesterCount: 357 }, 'voting'], + [ + 'timely once the PTC voted the payload timely', + { ...received, attesterCount: PTC_SIZE, availabilityYesCount: 400 }, + 'timely', + ], + [ + 'late once the PTC did not', + { ...received, attesterCount: PTC_SIZE, availabilityYesCount: 100 }, + 'late', + ], + ['full once the full node is heavier', { ...received, status: 'full' }, 'full'], + ['empty once the empty node is heavier', { ...received, status: 'empty' }, 'empty'], + ])('should be %s', (_, payload, expected) => { + expect(payloadStage(payload)).toBe(expected); + }); + }); + + describe('payloadProgress', () => { + it('should track PTC votes while voting', () => { + expect(payloadProgress({ ...received, attesterCount: 256 }, 'voting')).toBe(50); + }); + + it('should track timely votes once voted', () => { + const payload = { ...received, attesterCount: PTC_SIZE, availabilityYesCount: 384 }; + expect(payloadProgress(payload, 'timely')).toBe(75); + }); + }); + + describe('tideLevel', () => { + it('should rise with progress up to the rise level', () => { + expect(tideLevel('voting', 50)).toBe(TIDE_RISE_LEVEL / 2); + expect(tideLevel('timely', 100)).toBe(TIDE_RISE_LEVEL); + }); + + it('should fill full, drain empty and wait while awaiting', () => { + expect(tideLevel('full', 0)).toBe(100); + expect(tideLevel('empty', 100)).toBe(0); + expect(tideLevel('awaiting', 0)).toBe(0); + }); + }); + + describe('summarizePayloads', () => { + it('should be undefined without reporting sources', () => { + expect(summarizePayloads([])).toBeUndefined(); + }); + + it('should only fill the node when every source sees the payload full', () => { + expect( + summarizePayloads([ + { stage: 'full', progress: 100 }, + { stage: 'full', progress: 100 }, + { stage: 'voting', progress: 70 }, + ]), + ).toEqual({ stage: 'full', count: 2, reporting: 3, level: TIDE_RISE_LEVEL }); + expect( + summarizePayloads([ + { stage: 'full', progress: 100 }, + { stage: 'full', progress: 100 }, + ])?.level, + ).toBe(TIDE_FULL_LEVEL); + }); + + it('should average the progress of agreeing sources', () => { + expect( + summarizePayloads([ + { stage: 'voting', progress: 40 }, + { stage: 'voting', progress: 60 }, + ])?.level, + ).toBe(TIDE_RISE_LEVEL / 2); + }); + + it('should prefer the later lifecycle stage on a tie', () => { + expect( + summarizePayloads([ + { stage: 'voting', progress: 40 }, + { stage: 'full', progress: 100 }, + ])?.stage, + ).toBe('full'); + }); + }); +}); diff --git a/web/src/utils/__tests__/slosh.test.ts b/web/src/utils/__tests__/slosh.test.ts new file mode 100644 index 0000000..be33cea --- /dev/null +++ b/web/src/utils/__tests__/slosh.test.ts @@ -0,0 +1,52 @@ +import { endCanvasPan, reportCanvasPan, subscribeSlosh } from '@utils/slosh'; + +describe('slosh', () => { + afterEach(() => endCanvasPan()); + + it('should publish the horizontal pan velocity in px/ms', () => { + const velocities: number[] = []; + const unsubscribe = subscribeSlosh(v => velocities.push(v)); + + reportCanvasPan(100, 1000); + reportCanvasPan(130, 1010); + reportCanvasPan(120, 1030); + unsubscribe(); + + expect(velocities).toEqual([3, -0.5]); + }); + + it('should not measure across a gap between pans', () => { + const velocities: number[] = []; + const unsubscribe = subscribeSlosh(v => velocities.push(v)); + + reportCanvasPan(0, 1000); + reportCanvasPan(500, 2000); + unsubscribe(); + + expect(velocities).toEqual([]); + }); + + it('should settle listeners and start a new gesture when a pan ends', () => { + const velocities: number[] = []; + const unsubscribe = subscribeSlosh(v => velocities.push(v)); + + reportCanvasPan(0, 1000); + reportCanvasPan(20, 1010); + endCanvasPan(); + reportCanvasPan(900, 1020); + unsubscribe(); + + expect(velocities).toEqual([2, 0]); + }); + + it('should stop publishing to unsubscribed listeners', () => { + const velocities: number[] = []; + const unsubscribe = subscribeSlosh(v => velocities.push(v)); + unsubscribe(); + + reportCanvasPan(0, 1000); + reportCanvasPan(10, 1010); + + expect(velocities).toEqual([]); + }); +}); diff --git a/web/src/utils/__tests__/tideStyles.test.ts b/web/src/utils/__tests__/tideStyles.test.ts new file mode 100644 index 0000000..e982f53 --- /dev/null +++ b/web/src/utils/__tests__/tideStyles.test.ts @@ -0,0 +1,17 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +// The Tide's motion lives in index.css; an animation naming keyframes that +// do not exist silently does nothing, so check every Tide animation has its +// keyframes. +describe('tide styles', () => { + const css = readFileSync(resolve(__dirname, '../../index.css'), 'utf8'); + + it('should define keyframes for every tide animation', () => { + const used = [...css.matchAll(/animation:\s*(tide-[a-z-]+)/g)].map(m => m[1]); + const defined = new Set([...css.matchAll(/@keyframes\s+(tide-[a-z-]+)/g)].map(m => m[1])); + + expect(used.length).toBeGreaterThan(0); + for (const name of used) expect(defined).toContain(name); + }); +}); diff --git a/web/src/utils/graph.ts b/web/src/utils/graph.ts index ec14608..198956d 100644 --- a/web/src/utils/graph.ts +++ b/web/src/utils/graph.ts @@ -3,6 +3,7 @@ import { Attributes } from 'graphology-types'; import { ForkChoiceNode, Frame } from '@api'; import { + BlockPayload, Graph, WeightedGraph, WeightedNodeAttributes, @@ -16,6 +17,7 @@ import { ForkReference, } from '@app/types/graph'; import { getCheckpointType } from '@app/utils/api'; +import { payloadProgress, payloadStage } from '@app/utils/payload'; // A node whose fork choice head is more than this many slots behind its own // metadata wall clock slot is considered "stuck" and is excluded from the @@ -336,6 +338,63 @@ export function generateNodeId({ return `${slot}_${blockRoot}_${parentRoot}`; } +// Reads an optional decimal-string count. +function optionalCount(nodes: ForkChoiceNode[], key: keyof ForkChoiceNode): number | undefined { + const value = nodes.map(node => node[key]).find(v => v !== undefined); + return typeof value === 'string' ? Number.parseInt(value) : undefined; +} + +// Since Gloas a block can have a pending, an empty and a full fork-choice node, +// which share their slot, block root and parent root. The graph draws one node +// per block: the block's pending node, which carries the weight of all votes +// for the block, with the empty and full nodes summarised as its payload. Frames +// from before forky recorded payload statuses can still hold several nodes per +// block, of which the heaviest is kept. +export function groupBlockNodes( + nodes: ForkChoiceNode[], +): { node: ForkChoiceNode; payload?: BlockPayload }[] { + const blocks = new Map(); + for (const node of nodes) { + const variants = blocks.get(node.block_root); + if (variants) variants.push(node); + else blocks.set(node.block_root, [node]); + } + + return [...blocks.values()].map(variants => { + const pending = variants.find(n => n.payload_status === 'pending'); + const node = + pending ?? variants.reduce((a, b) => (BigInt(b.weight) > BigInt(a.weight) ? b : a)); + // Every Gloas block has an empty node; a pre-Gloas block is a single + // node (full per the spec, pending on some clients) with no payload. + const empty = variants.find(n => n.payload_status === 'empty'); + if (!empty) return { node }; + + // The full node only exists once the payload has been received. + const full = variants.find(n => n.payload_status === 'full'); + const emptyWeight = BigInt(empty.weight); + const fullWeight = full ? BigInt(full.weight) : undefined; + + let status: BlockPayload['status']; + if (fullWeight !== undefined && fullWeight > emptyWeight) status = 'full'; + else if (emptyWeight > (fullWeight ?? 0n)) status = 'empty'; + + const parent = node.parent_payload_status; + + return { + node, + payload: { + status, + emptyWeight, + fullWeight, + parentPayloadStatus: parent === 'empty' || parent === 'full' ? parent : undefined, + attesterCount: optionalCount(variants, 'payload_attester_count'), + availabilityYesCount: optionalCount(variants, 'payload_availability_yes_count'), + dataAvailabilityYesCount: optionalCount(variants, 'payload_data_availability_yes_count'), + }, + }; + }); +} + export function processForkChoiceData(frame: Required): ProcessedData { const { data, metadata } = frame; const graph = new Graphology(); @@ -360,8 +419,8 @@ export function processForkChoiceData(frame: Required): ProcessedData { // reverse sort data by highest slot first to later iterate over it // and stop when hitting the finalized checkpoint - const sortedData = data.fork_choice_nodes.sort((a, b) => { - return Number.parseInt(b.slot) - Number.parseInt(a.slot); + const sortedData = groupBlockNodes(data.fork_choice_nodes).sort((a, b) => { + return Number.parseInt(b.node.slot) - Number.parseInt(a.node.slot); }); // map block roots to fork choice nodes @@ -371,7 +430,7 @@ export function processForkChoiceData(frame: Required): ProcessedData { const blockRootNodeIds: Record = {}; // iterate over nodes and add them to the graph - for (const forkChoiceNode of sortedData) { + for (const { node: forkChoiceNode, payload } of sortedData) { const slot = Number.parseInt(forkChoiceNode.slot); if (isNaN(slot) || slot < 0) { throw new GraphError('Invalid slot', forkChoiceNode); @@ -408,6 +467,7 @@ export function processForkChoiceData(frame: Required): ProcessedData { offset: 0, weight: BigInt(forkChoiceNode.weight), weightPercentageComparedToHeaviestNeighbor: 100, + payload, }); // don't bother with nodes that are earlier than the finalized slot @@ -443,6 +503,7 @@ export function processForkChoiceData(frame: Required): ProcessedData { graph.addEdge(parentNodeId, nodeId, { distance: Number.parseInt(forkChoiceNode.slot) - Number.parseInt(parentForkChoiceNode.slot), + builtOnEmpty: forkChoiceNode.parent_payload_status === 'empty', }); } catch (e: unknown) { // handle orphaned nodes @@ -568,6 +629,11 @@ export function aggregateProcessedData(data: ProcessedData[]): AggregatedGraph { const nodeId = generateNodeId(node); nodeMap[node.blockRoot] = { id: nodeId, slot: node.slot }; + const stage = node.payload ? payloadStage(node.payload) : undefined; + const payloads = + node.payload && stage + ? [{ node: metadata.node, stage, progress: payloadProgress(node.payload, stage) }] + : []; // handle duplicate nodes if (graph.hasNode(nodeId)) { @@ -585,6 +651,7 @@ export function aggregateProcessedData(data: ProcessedData[]): AggregatedGraph { ? [...attributes.canonicalForNodes, metadata.node] : attributes.canonicalForNodes, seenByNodes: [...attributes.seenByNodes, metadata.node], + payloads: [...attributes.payloads, ...payloads], })); } else { graph.setAttribute('slotEnd', Math.max(graph.getAttribute('slotEnd'), node.slot)); @@ -607,14 +674,23 @@ export function aggregateProcessedData(data: ProcessedData[]): AggregatedGraph { highestWeight: node.weight, canonicalForNodes: node.canonical ? [metadata.node] : [], seenByNodes: [metadata.node], + payloads, }); } // check if parent exists to add edge if (node.parentRoot && nodeMap[node.parentRoot]) { - if (!graph.hasEdge(nodeMap[node.parentRoot].id, nodeId)) { - graph.addEdge(nodeMap[node.parentRoot].id, nodeId, { + const parentId = nodeMap[node.parentRoot].id; + // node ids are derived from the block, so they match across graphs. + const builtOnEmpty = + weightedGraph.hasEdge(parentId, nodeId) && + weightedGraph.getEdgeAttribute(parentId, nodeId, 'builtOnEmpty') === true; + if (!graph.hasEdge(parentId, nodeId)) { + graph.addEdge(parentId, nodeId, { distance: node.slot - nodeMap[node.parentRoot].slot, + builtOnEmpty, }); + } else if (builtOnEmpty) { + graph.setEdgeAttribute(parentId, nodeId, 'builtOnEmpty', true); } } else { orphanedNodes.push({ diff --git a/web/src/utils/payload.ts b/web/src/utils/payload.ts new file mode 100644 index 0000000..a2e0172 --- /dev/null +++ b/web/src/utils/payload.ts @@ -0,0 +1,225 @@ +import { BlockPayload, PayloadStage } from '@app/types/graph'; + +export type { PayloadStage }; + +// Payload Timeliness Committee size and the number of "payload present" votes +// a payload needs to be considered timely (mainnet preset, used by Sepolia, +// Hoodi and the devnets). +export const PTC_SIZE = 512; +export const PAYLOAD_TIMELY_THRESHOLD = PTC_SIZE / 2; + +/* Where a Gloas block's execution payload stands, from block arrival to the + * fork choice deciding between the block's empty and full nodes: + * awaiting - the node has not received the payload yet (no full node) + * voting - payload received, the PTC is still voting on its timeliness + * timely - the PTC voted the payload timely; attesters have not split + * the block's weight between empty and full yet + * late - the PTC did not vote the payload timely; ditto + * undecided - payload received, empty and full tied, no PTC counts + * full - the full node outweighs the empty node + * empty - the empty node outweighs the full node */ +export function payloadStage(payload: BlockPayload): PayloadStage { + if (payload.status) return payload.status; + if (payload.fullWeight === undefined) return 'awaiting'; + if (payload.attesterCount === undefined) return 'undecided'; + if (payload.attesterCount < PTC_SIZE) return 'voting'; + return (payload.availabilityYesCount ?? 0) > PAYLOAD_TIMELY_THRESHOLD ? 'timely' : 'late'; +} + +// Progress (0-100) of a block's payload: PTC votes while the committee is +// voting, the share voting the payload timely once it has. +export function payloadProgress(payload: BlockPayload, stage: PayloadStage): number { + switch (stage) { + case 'voting': + return ((payload.attesterCount ?? 0) / PTC_SIZE) * 100; + case 'timely': + case 'late': + return ((payload.availabilityYesCount ?? 0) / PTC_SIZE) * 100; + case 'full': + case 'empty': + return 100; + default: + return 0; + } +} + +// The Tide's levels (percent of the node's height). While a payload is in +// between, the tide rises with its progress up to TIDE_RISE_LEVEL, just below +// the block hash, so the moving surface never crosses the node's label or +// hash. Once full it fills the node to the brim, its surface out of view. +export const TIDE_RISE_LEVEL = 52; +export const TIDE_FULL_LEVEL = 100; + +// The Tide's level for a stage and its progress. +export function tideLevel(stage: PayloadStage, progress: number): number { + switch (stage) { + case 'full': + return TIDE_FULL_LEVEL; + case 'empty': + case 'awaiting': + return 0; + case 'undecided': + return TIDE_RISE_LEVEL / 2; + default: + return (Math.min(Math.max(progress, 0), 100) * TIDE_RISE_LEVEL) / 100; + } +} + +export interface PayloadStageStyle { + name: string; + // the label inside the node, where space is tight. + short: string; + description: string; + // decided stages hold the Tide still; in-between ones keep it moving. + decided: boolean; + motion: 'still' | 'moving'; + // the liquid's back wave (body) and front wave (crest, whose border draws + // the surface line), the in-node chip and the legend swatch. + body: string; + crest: string; + chip: string; + swatch: string; + // a representative level for the legend. + sampleLevel: number; +} + +// Single source of truth for the payload stages, shared by the graph nodes, +// hover cards and legend. +export const PAYLOAD_STAGES: Record = { + awaiting: { + short: 'Awaiting', + name: 'Awaiting', + description: 'Payload not received yet', + decided: false, + motion: 'still', + body: '', + crest: '', + chip: 'bg-background/70 text-muted', + swatch: '', + sampleLevel: 0, + }, + voting: { + short: 'PTC', + name: 'PTC voting', + description: 'PTC votes coming in', + decided: false, + motion: 'moving', + body: 'bg-payload/15', + crest: 'bg-payload/20 border-4 border-payload-surface/80', + chip: 'bg-background/70 text-payload', + swatch: 'bg-payload/60', + sampleLevel: 50, + }, + timely: { + short: 'Timely', + name: 'Timely', + description: 'PTC: timely; awaiting attesters', + decided: false, + motion: 'moving', + body: 'bg-payload/15', + crest: 'bg-payload/20 border-4 border-payload-surface/80', + chip: 'bg-background/70 text-payload', + swatch: 'bg-payload/60', + sampleLevel: 90, + }, + late: { + short: 'Late', + name: 'Late', + description: 'PTC: not timely', + decided: false, + motion: 'moving', + body: 'bg-payload/10', + crest: 'bg-payload/15 border-4 border-payload-surface/50', + chip: 'bg-background/70 text-payload', + swatch: 'bg-payload/40', + sampleLevel: 25, + }, + undecided: { + short: 'Tied', + name: 'Undecided', + description: 'Empty and full tied', + decided: false, + motion: 'still', + body: 'bg-muted/10', + crest: 'bg-muted/15 border-4 border-muted/60', + chip: 'bg-background/70 text-muted', + swatch: 'bg-muted/50', + sampleLevel: 50, + }, + full: { + short: 'Full', + name: 'Full', + description: 'Backed with its payload', + decided: true, + motion: 'still', + body: 'bg-payload/10', + crest: 'bg-payload/10 border-4 border-payload-surface', + chip: 'bg-payload text-foreground-inverted', + swatch: 'bg-payload', + sampleLevel: 100, + }, + empty: { + short: 'Empty', + name: 'Empty', + description: 'Backed without its payload', + decided: true, + motion: 'still', + body: '', + crest: '', + chip: 'bg-background/70 text-payload ring-2 ring-payload ring-inset', + swatch: '', + sampleLevel: 0, + }, +}; + +// Display order, following a block's lifecycle. +export const PAYLOAD_STAGE_ORDER: PayloadStage[] = [ + 'awaiting', + 'voting', + 'timely', + 'late', + 'undecided', + 'full', + 'empty', +]; + +export interface PayloadSummary { + stage: PayloadStage; + // sources reporting the stage, out of the sources reporting payloads. + count: number; + reporting: number; + level: number; +} + +// Summarises the payload stages several sources report for one block: the +// stage most sources report (the later lifecycle stage on a tie), with the +// Tide at the mean level of those sources, scaled by how many agree. Only a +// payload every reporting source sees as full fills the node; otherwise the +// tide stays below the hash. +export function summarizePayloads( + payloads: { stage: PayloadStage; progress: number }[], +): PayloadSummary | undefined { + if (payloads.length === 0) return undefined; + + const counts = new Map(); + for (const { stage } of payloads) counts.set(stage, (counts.get(stage) ?? 0) + 1); + + let stage = PAYLOAD_STAGE_ORDER[0]; + let count = 0; + for (const candidate of PAYLOAD_STAGE_ORDER) { + const n = counts.get(candidate) ?? 0; + if (n > 0 && n >= count) { + stage = candidate; + count = n; + } + } + + const agreeing = payloads.filter(p => p.stage === stage); + const mean = + agreeing.reduce((sum, p) => sum + tideLevel(p.stage, p.progress), 0) / agreeing.length; + + const level = + count === payloads.length ? mean : Math.min((mean * count) / payloads.length, TIDE_RISE_LEVEL); + + return { stage, count, reporting: payloads.length, level }; +} diff --git a/web/src/utils/slosh.ts b/web/src/utils/slosh.ts new file mode 100644 index 0000000..d58837d --- /dev/null +++ b/web/src/utils/slosh.ts @@ -0,0 +1,36 @@ +// Carries the graph canvas's pan velocity to the Tide in the lead nodes, so +// their liquid can slosh as the canvas is dragged. A plain pub/sub rather +// than React state: pans fire every frame and only the subscribed tides' +// animation loops need to hear about them. + +export type SloshListener = (velocityX: number) => void; + +const listeners = new Set(); +let last: { x: number; t: number } | undefined; + +// Pan samples further apart than this start a new gesture rather than +// producing a velocity. +const MAX_SAMPLE_GAP_MS = 120; + +export function subscribeSlosh(listener: SloshListener): () => void { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; +} + +// Reports the canvas position during a pan; publishes its horizontal +// velocity in screen pixels per millisecond. +export function reportCanvasPan(positionX: number, now = performance.now()): void { + if (last && now > last.t && now - last.t < MAX_SAMPLE_GAP_MS) { + const velocityX = (positionX - last.x) / (now - last.t); + listeners.forEach(listener => listener(velocityX)); + } + last = { x: positionX, t: now }; +} + +// Ends a pan gesture, so the next one does not measure across the gap. +export function endCanvasPan(): void { + if (last) listeners.forEach(listener => listener(0)); + last = undefined; +} diff --git a/web/src/utils/strings.ts b/web/src/utils/strings.ts index 72b26f0..b61ff7c 100644 --- a/web/src/utils/strings.ts +++ b/web/src/utils/strings.ts @@ -17,3 +17,12 @@ export function convertToHexColor(str: string): string { } return color; } + +// Formats a client-specific extra_data value for display. Checkpoints render +// as "epoch · root"; other objects as JSON. +export function formatExtraDataValue(value: unknown): string { + if (value === null || value === undefined) return '—'; + if (typeof value !== 'object') return `${value}`; + if ('epoch' in value && 'root' in value) return `${value.epoch} · ${value.root}`; + return JSON.stringify(value); +}