From 71fba0d05ea07addd0fdc937ba53f0eca2b82132 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Mon, 20 Jan 2025 11:40:52 +0000 Subject: [PATCH 01/24] initial --- api/handlers/exporter.go | 69 +++ api/server/server.go | 2 + exporter/v2/model.go | 41 ++ exporter/v2/model_encoding.go | 878 ++++++++++++++++++++++++++++++++ exporter/v2/store/store.go | 178 +++++++ exporter/v2/store/store_test.go | 80 +++ 6 files changed, 1248 insertions(+) create mode 100644 exporter/v2/model.go create mode 100644 exporter/v2/model_encoding.go create mode 100644 exporter/v2/store/store.go create mode 100644 exporter/v2/store/store_test.go diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index 5eb1aa01b9..cd5fa863bf 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -11,12 +11,15 @@ import ( qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage" "github.com/ssvlabs/ssv/api" + model "github.com/ssvlabs/ssv/exporter/v2" + trace "github.com/ssvlabs/ssv/exporter/v2/store" ibftstorage "github.com/ssvlabs/ssv/ibft/storage" ) type Exporter struct { NetworkConfig networkconfig.NetworkConfig ParticipantStores *ibftstorage.ParticipantStores + TraceStore trace.DutyTraceStore } type ParticipantResponse struct { @@ -29,6 +32,72 @@ type ParticipantResponse struct { } `json:"message"` } +func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error { + return nil +} + +func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error { + var request struct { + From uint64 `json:"from"` + To uint64 `json:"to"` + Roles api.RoleSlice `json:"roles"` + VIndex uint64 `json:"index"` + } + + if err := api.Bind(r, &request); err != nil { + return api.BadRequestError(err) + } + + if request.From > request.To { + return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) + } + + if len(request.Roles) == 0 { + return api.BadRequestError(fmt.Errorf("at least one role is required")) + } + + if request.VIndex == 0 { + return api.BadRequestError(fmt.Errorf("validator index is required")) + } + vIndex := phase0.ValidatorIndex(request.VIndex) + + var results []*model.ValidatorDutyTrace + + for s := request.From; s <= request.To; s++ { + slot := phase0.Slot(s) + for _, r := range request.Roles { + role := spectypes.BeaconRole(r) + traces, err := e.TraceStore.GetValidatorDuties(role, slot, vIndex) + if err != nil { + return api.Error(fmt.Errorf("error getting traces: %w", err)) + } + results = append(results, traces...) + } + } + + return api.Render(w, r, toValidatorTraceResponse(results)) +} + +type validatorTraceResponse struct { + Data []validatorTrace `json:"data"` +} + +type validatorTrace struct { + Slot phase0.Slot `json:"slot"` +} + +func toValidatorTrace(_ *model.ValidatorDutyTrace) validatorTrace { + return validatorTrace{} +} + +func toValidatorTraceResponse(traces []*model.ValidatorDutyTrace) *validatorTraceResponse { + r := &validatorTraceResponse{} + for _, t := range traces { + r.Data = append(r.Data, toValidatorTrace(t)) + } + return r +} + func (e *Exporter) Decideds(w http.ResponseWriter, r *http.Request) error { var request struct { From uint64 `json:"from"` diff --git a/api/server/server.go b/api/server/server.go index 36418e3e34..f6eb20e41a 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -55,6 +55,8 @@ func (s *Server) Run() error { // We kept both GET and POST methods to ensure compatibility and avoid breaking changes for clients that may rely on either method router.Get("/v1/exporter/decideds", api.Handler(s.exporter.Decideds)) router.Post("/v1/exporter/decideds", api.Handler(s.exporter.Decideds)) + router.Get("/v1/exporter/validator/traces", api.Handler(s.exporter.ValidatorTraces)) + router.Get("/v1/exporter/committee/traces", api.Handler(s.exporter.CommitteeTraces)) s.logger.Info("Serving SSV API", zap.String("addr", s.addr)) diff --git a/exporter/v2/model.go b/exporter/v2/model.go new file mode 100644 index 0000000000..e10df64ae5 --- /dev/null +++ b/exporter/v2/model.go @@ -0,0 +1,41 @@ +package exporter + +import ( + "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" +) + +type ValidatorDutyTrace struct { + DutyTrace + Role spectypes.BeaconRole + Validator phase0.ValidatorIndex +} + +type CommitteeDutyTrace struct { + DutyTrace + CommitteeID spectypes.CommitteeID `ssz-size:"32"` + OperatorIDs []spectypes.OperatorID `ssz-max:"4"` + AttestationDataRoot phase0.Root `ssz-size:"32"` // Computed from BeaconVote: See example in CommitteeObserver + SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` // Computed from BeaconVote: See example in CommitteeObserver +} + +type DutyTrace struct { + Slot phase0.Slot + Pre []*MessageTrace `ssz-max:"4"` + Rounds []*RoundTrace `ssz-max:"4"` + Post []*MessageTrace `ssz-max:"4"` +} + +type RoundTrace struct { + ProposalRoot [32]byte `ssz-size:"32"` + ProposalReceived uint64 // TODO fixme + Prepares []*MessageTrace `ssz-max:"4"` // Only recorded if root matches proposal. + Commits []*MessageTrace `ssz-max:"4"` // Only recorded if root matches proposal. +} + +type MessageTrace struct { + BeaconRoot phase0.Root `ssz-size:"32"` + Signer spectypes.OperatorID + Validator phase0.ValidatorIndex // Only present in CommitteeDutyTrace.Post + Received uint64 // TODO fixme +} diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go new file mode 100644 index 0000000000..65a3721600 --- /dev/null +++ b/exporter/v2/model_encoding.go @@ -0,0 +1,878 @@ +// Code generated by fastssz. DO NOT EDIT. +// Hash: 5827b29a742e60a78d47747ecf052a0ff9466558698dd85659565964ec1e3610 +// Version: 0.1.3 +package exporter + +import ( + "github.com/attestantio/go-eth2-client/spec/phase0" + ssz "github.com/ferranbt/fastssz" + spectypes "github.com/ssvlabs/ssv-spec/types" +) + +// MarshalSSZ ssz marshals the ValidatorDutyTrace object +func (v *ValidatorDutyTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(v) +} + +// MarshalSSZTo ssz marshals the ValidatorDutyTrace object to a target array +func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(36) + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, uint64(v.Slot)) + + // Offset (1) 'Pre' + dst = ssz.WriteOffset(dst, offset) + offset += len(v.Pre) * 56 + + // Offset (2) 'Rounds' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(v.Rounds); ii++ { + offset += 4 + offset += v.Rounds[ii].SizeSSZ() + } + + // Offset (3) 'Post' + dst = ssz.WriteOffset(dst, offset) + offset += len(v.Post) * 56 + + // Field (4) 'Role' + dst = ssz.MarshalUint64(dst, uint64(v.Role)) + + // Field (5) 'Validator' + dst = ssz.MarshalUint64(dst, uint64(v.Validator)) + + // Field (1) 'Pre' + if size := len(v.Pre); size > 4 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Pre", size, 4) + return + } + for ii := 0; ii < len(v.Pre); ii++ { + if dst, err = v.Pre[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (2) 'Rounds' + if size := len(v.Rounds); size > 4 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Rounds", size, 4) + return + } + { + offset = 4 * len(v.Rounds) + for ii := 0; ii < len(v.Rounds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += v.Rounds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(v.Rounds); ii++ { + if dst, err = v.Rounds[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Post' + if size := len(v.Post); size > 4 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Post", size, 4) + return + } + for ii := 0; ii < len(v.Post); ii++ { + if dst, err = v.Post[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ValidatorDutyTrace object +func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 36 { + return ssz.ErrSize + } + + tail := buf + var o1, o2, o3 uint64 + + // Field (0) 'Slot' + v.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) + + // Offset (1) 'Pre' + if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { + return ssz.ErrOffset + } + + if o1 < 36 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (2) 'Rounds' + if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } + + // Offset (3) 'Post' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + return ssz.ErrOffset + } + + // Field (4) 'Role' + v.Role = spectypes.BeaconRole(ssz.UnmarshallUint64(buf[20:28])) + + // Field (5) 'Validator' + v.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[28:36])) + + // Field (1) 'Pre' + { + buf = tail[o1:o2] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + v.Pre = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if v.Pre[ii] == nil { + v.Pre[ii] = new(MessageTrace) + } + if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + + // Field (2) 'Rounds' + { + buf = tail[o2:o3] + num, err := ssz.DecodeDynamicLength(buf, 4) + if err != nil { + return err + } + v.Rounds = make([]*RoundTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if v.Rounds[indx] == nil { + v.Rounds[indx] = new(RoundTrace) + } + if err = v.Rounds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (3) 'Post' + { + buf = tail[o3:] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + v.Post = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if v.Post[ii] == nil { + v.Post[ii] = new(MessageTrace) + } + if err = v.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ValidatorDutyTrace object +func (v *ValidatorDutyTrace) SizeSSZ() (size int) { + size = 36 + + // Field (1) 'Pre' + size += len(v.Pre) * 56 + + // Field (2) 'Rounds' + for ii := 0; ii < len(v.Rounds); ii++ { + size += 4 + size += v.Rounds[ii].SizeSSZ() + } + + // Field (3) 'Post' + size += len(v.Post) * 56 + + return +} + +// HashTreeRoot ssz hashes the ValidatorDutyTrace object +func (v *ValidatorDutyTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(v) +} + +// HashTreeRootWith ssz hashes the ValidatorDutyTrace object with a hasher +func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(uint64(v.Slot)) + + // Field (1) 'Pre' + { + subIndx := hh.Index() + num := uint64(len(v.Pre)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range v.Pre { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (2) 'Rounds' + { + subIndx := hh.Index() + num := uint64(len(v.Rounds)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range v.Rounds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (3) 'Post' + { + subIndx := hh.Index() + num := uint64(len(v.Post)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range v.Post { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (4) 'Role' + hh.PutUint64(uint64(v.Role)) + + // Field (5) 'Validator' + hh.PutUint64(uint64(v.Validator)) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ValidatorDutyTrace object +func (v *ValidatorDutyTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(v) +} + +// MarshalSSZ ssz marshals the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) +} + +// MarshalSSZTo ssz marshals the CommitteeDutyTrace object to a target array +func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(120) + + // Field (0) 'Slot' + dst = ssz.MarshalUint64(dst, uint64(c.Slot)) + + // Offset (1) 'Pre' + dst = ssz.WriteOffset(dst, offset) + offset += len(c.Pre) * 56 + + // Offset (2) 'Rounds' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(c.Rounds); ii++ { + offset += 4 + offset += c.Rounds[ii].SizeSSZ() + } + + // Offset (3) 'Post' + dst = ssz.WriteOffset(dst, offset) + offset += len(c.Post) * 56 + + // Field (4) 'CommitteeID' + dst = append(dst, c.CommitteeID[:]...) + + // Offset (5) 'OperatorIDs' + dst = ssz.WriteOffset(dst, offset) + offset += len(c.OperatorIDs) * 8 + + // Field (6) 'AttestationDataRoot' + dst = append(dst, c.AttestationDataRoot[:]...) + + // Field (7) 'SyncCommitteeMessageRoot' + dst = append(dst, c.SyncCommitteeMessageRoot[:]...) + + // Field (1) 'Pre' + if size := len(c.Pre); size > 4 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Pre", size, 4) + return + } + for ii := 0; ii < len(c.Pre); ii++ { + if dst, err = c.Pre[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (2) 'Rounds' + if size := len(c.Rounds); size > 4 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Rounds", size, 4) + return + } + { + offset = 4 * len(c.Rounds) + for ii := 0; ii < len(c.Rounds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Rounds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(c.Rounds); ii++ { + if dst, err = c.Rounds[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Post' + if size := len(c.Post); size > 4 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 4) + return + } + for ii := 0; ii < len(c.Post); ii++ { + if dst, err = c.Post[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (5) 'OperatorIDs' + if size := len(c.OperatorIDs); size > 4 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 4) + return + } + for ii := 0; ii < len(c.OperatorIDs); ii++ { + dst = ssz.MarshalUint64(dst, uint64(c.OperatorIDs[ii])) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 120 { + return ssz.ErrSize + } + + tail := buf + var o1, o2, o3, o5 uint64 + + // Field (0) 'Slot' + c.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) + + // Offset (1) 'Pre' + if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { + return ssz.ErrOffset + } + + if o1 < 120 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (2) 'Rounds' + if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } + + // Offset (3) 'Post' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + return ssz.ErrOffset + } + + // Field (4) 'CommitteeID' + copy(c.CommitteeID[:], buf[20:52]) + + // Offset (5) 'OperatorIDs' + if o5 = ssz.ReadOffset(buf[52:56]); o5 > size || o3 > o5 { + return ssz.ErrOffset + } + + // Field (6) 'AttestationDataRoot' + copy(c.AttestationDataRoot[:], buf[56:88]) + + // Field (7) 'SyncCommitteeMessageRoot' + copy(c.SyncCommitteeMessageRoot[:], buf[88:120]) + + // Field (1) 'Pre' + { + buf = tail[o1:o2] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + c.Pre = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if c.Pre[ii] == nil { + c.Pre[ii] = new(MessageTrace) + } + if err = c.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + + // Field (2) 'Rounds' + { + buf = tail[o2:o3] + num, err := ssz.DecodeDynamicLength(buf, 4) + if err != nil { + return err + } + c.Rounds = make([]*RoundTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Rounds[indx] == nil { + c.Rounds[indx] = new(RoundTrace) + } + if err = c.Rounds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (3) 'Post' + { + buf = tail[o3:o5] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + c.Post = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if c.Post[ii] == nil { + c.Post[ii] = new(MessageTrace) + } + if err = c.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + + // Field (5) 'OperatorIDs' + { + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 8, 4) + if err != nil { + return err + } + c.OperatorIDs = ssz.ExtendUint64(c.OperatorIDs, num) + for ii := 0; ii < num; ii++ { + c.OperatorIDs[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) SizeSSZ() (size int) { + size = 120 + + // Field (1) 'Pre' + size += len(c.Pre) * 56 + + // Field (2) 'Rounds' + for ii := 0; ii < len(c.Rounds); ii++ { + size += 4 + size += c.Rounds[ii].SizeSSZ() + } + + // Field (3) 'Post' + size += len(c.Post) * 56 + + // Field (5) 'OperatorIDs' + size += len(c.OperatorIDs) * 8 + + return +} + +// HashTreeRoot ssz hashes the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) +} + +// HashTreeRootWith ssz hashes the CommitteeDutyTrace object with a hasher +func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Slot' + hh.PutUint64(uint64(c.Slot)) + + // Field (1) 'Pre' + { + subIndx := hh.Index() + num := uint64(len(c.Pre)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Pre { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (2) 'Rounds' + { + subIndx := hh.Index() + num := uint64(len(c.Rounds)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Rounds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (3) 'Post' + { + subIndx := hh.Index() + num := uint64(len(c.Post)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Post { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (4) 'CommitteeID' + hh.PutBytes(c.CommitteeID[:]) + + // Field (5) 'OperatorIDs' + { + if size := len(c.OperatorIDs); size > 4 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 4) + return + } + subIndx := hh.Index() + for _, i := range c.OperatorIDs { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(c.OperatorIDs)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(4, numItems, 8)) + } + + // Field (6) 'AttestationDataRoot' + hh.PutBytes(c.AttestationDataRoot[:]) + + // Field (7) 'SyncCommitteeMessageRoot' + hh.PutBytes(c.SyncCommitteeMessageRoot[:]) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) +} + +// MarshalSSZ ssz marshals the RoundTrace object +func (r *RoundTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(r) +} + +// MarshalSSZTo ssz marshals the RoundTrace object to a target array +func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(48) + + // Field (0) 'ProposalRoot' + dst = append(dst, r.ProposalRoot[:]...) + + // Field (1) 'ProposalReceived' + dst = ssz.MarshalUint64(dst, r.ProposalReceived) + + // Offset (2) 'Prepares' + dst = ssz.WriteOffset(dst, offset) + offset += len(r.Prepares) * 56 + + // Offset (3) 'Commits' + dst = ssz.WriteOffset(dst, offset) + offset += len(r.Commits) * 56 + + // Field (2) 'Prepares' + if size := len(r.Prepares); size > 4 { + err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 4) + return + } + for ii := 0; ii < len(r.Prepares); ii++ { + if dst, err = r.Prepares[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Commits' + if size := len(r.Commits); size > 4 { + err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 4) + return + } + for ii := 0; ii < len(r.Commits); ii++ { + if dst, err = r.Commits[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the RoundTrace object +func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 48 { + return ssz.ErrSize + } + + tail := buf + var o2, o3 uint64 + + // Field (0) 'ProposalRoot' + copy(r.ProposalRoot[:], buf[0:32]) + + // Field (1) 'ProposalReceived' + r.ProposalReceived = ssz.UnmarshallUint64(buf[32:40]) + + // Offset (2) 'Prepares' + if o2 = ssz.ReadOffset(buf[40:44]); o2 > size { + return ssz.ErrOffset + } + + if o2 < 48 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (3) 'Commits' + if o3 = ssz.ReadOffset(buf[44:48]); o3 > size || o2 > o3 { + return ssz.ErrOffset + } + + // Field (2) 'Prepares' + { + buf = tail[o2:o3] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + r.Prepares = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if r.Prepares[ii] == nil { + r.Prepares[ii] = new(MessageTrace) + } + if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + + // Field (3) 'Commits' + { + buf = tail[o3:] + num, err := ssz.DivideInt2(len(buf), 56, 4) + if err != nil { + return err + } + r.Commits = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if r.Commits[ii] == nil { + r.Commits[ii] = new(MessageTrace) + } + if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the RoundTrace object +func (r *RoundTrace) SizeSSZ() (size int) { + size = 48 + + // Field (2) 'Prepares' + size += len(r.Prepares) * 56 + + // Field (3) 'Commits' + size += len(r.Commits) * 56 + + return +} + +// HashTreeRoot ssz hashes the RoundTrace object +func (r *RoundTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(r) +} + +// HashTreeRootWith ssz hashes the RoundTrace object with a hasher +func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'ProposalRoot' + hh.PutBytes(r.ProposalRoot[:]) + + // Field (1) 'ProposalReceived' + hh.PutUint64(r.ProposalReceived) + + // Field (2) 'Prepares' + { + subIndx := hh.Index() + num := uint64(len(r.Prepares)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.Prepares { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + // Field (3) 'Commits' + { + subIndx := hh.Index() + num := uint64(len(r.Commits)) + if num > 4 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.Commits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 4) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the RoundTrace object +func (r *RoundTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(r) +} + +// MarshalSSZ ssz marshals the MessageTrace object +func (m *MessageTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(m) +} + +// MarshalSSZTo ssz marshals the MessageTrace object to a target array +func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'BeaconRoot' + dst = append(dst, m.BeaconRoot[:]...) + + // Field (1) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(m.Signer)) + + // Field (2) 'Validator' + dst = ssz.MarshalUint64(dst, uint64(m.Validator)) + + // Field (3) 'Received' + dst = ssz.MarshalUint64(dst, m.Received) + + return +} + +// UnmarshalSSZ ssz unmarshals the MessageTrace object +func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 56 { + return ssz.ErrSize + } + + // Field (0) 'BeaconRoot' + copy(m.BeaconRoot[:], buf[0:32]) + + // Field (1) 'Signer' + m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[32:40])) + + // Field (2) 'Validator' + m.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'Received' + m.Received = ssz.UnmarshallUint64(buf[48:56]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object +func (m *MessageTrace) SizeSSZ() (size int) { + size = 56 + return +} + +// HashTreeRoot ssz hashes the MessageTrace object +func (m *MessageTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(m) +} + +// HashTreeRootWith ssz hashes the MessageTrace object with a hasher +func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'BeaconRoot' + hh.PutBytes(m.BeaconRoot[:]) + + // Field (1) 'Signer' + hh.PutUint64(uint64(m.Signer)) + + // Field (2) 'Validator' + hh.PutUint64(uint64(m.Validator)) + + // Field (3) 'Received' + hh.PutUint64(m.Received) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the MessageTrace object +func (m *MessageTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(m) +} diff --git a/exporter/v2/store/store.go b/exporter/v2/store/store.go new file mode 100644 index 0000000000..54672e0f06 --- /dev/null +++ b/exporter/v2/store/store.go @@ -0,0 +1,178 @@ +package store + +import ( + "encoding/binary" + "fmt" + + "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" + model "github.com/ssvlabs/ssv/exporter/v2" + "github.com/ssvlabs/ssv/storage/basedb" +) + +const ( + validatorDutyTraceKey = "vd" + commiteeDutyTraceKey = "cd" + commiteeVIndexKey = "ci" +) + +type DutyTraceStore struct { + db basedb.Database +} + +func New(db basedb.Database) *DutyTraceStore { + return &DutyTraceStore{ + db: db, + } +} + +func (s *DutyTraceStore) GetValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (out []*model.ValidatorDutyTrace, err error) { + prefix := s.makeValidatorSlotPrefix(role, slot, index) + err = s.db.GetAll(prefix, func(_ int, o basedb.Obj) error { + vdt := new(model.ValidatorDutyTrace) + err := vdt.UnmarshalSSZ(o.Value) + if err != nil { + return fmt.Errorf("unmarshall ValidatorDutyTrace: %w", err) + } + out = append(out, vdt) + return nil + }) + if err != nil { + return nil, err + } + + return +} + +func (s *DutyTraceStore) GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) (out []*model.ValidatorDutyTrace, err error) { + panic("fix me") +} + +func (s *DutyTraceStore) GetCommitteeDuties(slot phase0.Slot, role spectypes.BeaconRole) (out []*model.CommitteeDutyTrace, err error) { + panic("fix me") +} + +func (s *DutyTraceStore) GetCommitteeDutiesByValidator(indexes []phase0.ValidatorIndex, slot phase0.Slot) (out []*model.CommitteeDutyTrace, err error) { + prefixes := s.makeCommiteeValidatorIndexPrefixes(indexes, slot) + keys := make([][]byte, 0) + + tx := s.db.BeginRead() + defer tx.Discard() + + for _, prefix := range prefixes { + err = s.db.GetAll(prefix, func(_ int, o basedb.Obj) error { + keys = append(keys, o.Value) + return nil + }) + if err != nil { + return nil, err + } + } + + err = s.db.GetMany(nil, keys, func(o basedb.Obj) error { + vdt := new(model.CommitteeDutyTrace) + if err := vdt.UnmarshalSSZ(o.Value); err != nil { + return fmt.Errorf("unmarshall ValidatorDutyTrace: %w", err) + } + out = append(out, vdt) + return nil + }) + if err != nil { + return nil, err + } + + return +} + +func (s *DutyTraceStore) SaveValidatorDuty(dto *model.ValidatorDutyTrace) error { + prefix := s.makeValidatorPrefix(dto) + + value, err := dto.MarshalSSZ() + if err != nil { + return fmt.Errorf("marshall ValidatorDutyTrace: %w", err) + } + + tx := s.db.Begin() + defer tx.Discard() + + err = s.db.Using(tx).Set(prefix, nil, value) + if err != nil { + return fmt.Errorf("save full ValidatorDutyTrace: %w", err) + } + + return tx.Commit() +} + +func (s *DutyTraceStore) SaveCommiteeDuty(dto *model.CommitteeDutyTrace) error { + prefix := s.makeCommitteePrefix(dto) + + value, err := dto.MarshalSSZ() + if err != nil { + return fmt.Errorf("marshall ValidatorDutyTrace: %w", err) + } + + tx := s.db.Begin() + defer tx.Discard() + + err = s.db.Using(tx).Set(prefix, nil, value) + if err != nil { + return fmt.Errorf("save full ValidatorDutyTrace: %w", err) + } + + return tx.Commit() +} + +func (s *DutyTraceStore) makeValidatorSlotPrefix(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) []byte { + prefix := make([]byte, 0, len(validatorDutyTraceKey)+1+4) + prefix = append(prefix, []byte(validatorDutyTraceKey)...) + prefix = append(prefix, byte(role&0xff)) + prefix = append(prefix, slotToByteSlice(slot)...) + prefix = append(prefix, uInt64ToByteSlice(uint64(index))...) + return prefix +} + +func (s *DutyTraceStore) makeValidatorPrefix(dto *model.ValidatorDutyTrace) []byte { + prefix := make([]byte, 0, len(validatorDutyTraceKey)+4+1+8) + prefix = append(prefix, []byte(validatorDutyTraceKey)...) + prefix = append(prefix, byte(dto.Role&0xff)) + prefix = append(prefix, slotToByteSlice(dto.Slot)...) + prefix = append(prefix, uInt64ToByteSlice(uint64(dto.Validator))...) + return prefix +} + +func (s *DutyTraceStore) makeCommitteePrefix(dto *model.CommitteeDutyTrace) []byte { + prefix := make([]byte, 0, len(commiteeDutyTraceKey)+4+1+8) + prefix = append(prefix, []byte(commiteeDutyTraceKey)...) + prefix = append(prefix, slotToByteSlice(dto.Slot)...) + prefix = append(prefix, dto.CommitteeID[:]...) + return prefix +} + +func (s *DutyTraceStore) makeCommiteeValidatorIndexPrefixes(ii []phase0.ValidatorIndex, slot phase0.Slot) (keys [][]byte) { + for _, index := range ii { + prefix := make([]byte, 0, len(commiteeVIndexKey)+1+8) + prefix = append(prefix, []byte(commiteeVIndexKey)...) + prefix = append(prefix, slotToByteSlice(slot)...) + prefix = append(prefix, uInt64ToByteSlice(uint64(index))...) + keys = append(keys, prefix) + } + + return +} + +// helpers + +func slotToByteSlice(v phase0.Slot) []byte { + b := make([]byte, 4) + // we're casting down but we should be good for now + // #nosec G115 + slot := uint32(uint64(v)) + binary.LittleEndian.PutUint32(b, slot) + return b +} + +func uInt64ToByteSlice(n uint64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, n) + return b +} diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go new file mode 100644 index 0000000000..4c076bd518 --- /dev/null +++ b/exporter/v2/store/store_test.go @@ -0,0 +1,80 @@ +package store_test + +import ( + "testing" + + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ssvlabs/ssv-spec/types" + model "github.com/ssvlabs/ssv/exporter/v2" + store "github.com/ssvlabs/ssv/exporter/v2/store" + "github.com/ssvlabs/ssv/storage/basedb" + "github.com/ssvlabs/ssv/storage/kv" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestSaveCommitteeDutyTrace(t *testing.T) { + logger := zap.NewNop() + db, err := kv.NewInMemory(logger, basedb.Options{}) + require.NoError(t, err) + defer db.Close() + + trace1 := makeCTrace(1) + trace2 := makeCTrace(2) + + _, _ = trace1, trace2 +} + +func TestSaveValidatorDutyTrace(t *testing.T) { + logger := zap.NewNop() + db, err := kv.NewInMemory(logger, basedb.Options{}) + require.NoError(t, err) + defer db.Close() + + trace1 := makeVTrace(1) + trace2 := makeVTrace(2) + + store := store.New(db) + require.NoError(t, store.SaveValidatorDuty(trace1)) + require.NoError(t, store.SaveValidatorDuty(trace2)) + + traces, err := store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(1), phase0.ValidatorIndex(39393)) + require.NoError(t, err) + require.Len(t, traces, 1) + + traces, err = store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(2), phase0.ValidatorIndex(39393)) + require.NoError(t, err) + require.Len(t, traces, 1) + + traces, err = store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(3), phase0.ValidatorIndex(39393)) + require.NoError(t, err) + require.Empty(t, traces) +} + +func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { + return &model.ValidatorDutyTrace{ + DutyTrace: model.DutyTrace{ + Slot: slot, + Pre: nil, + Rounds: nil, + Post: nil, + }, + Role: types.BNRoleAttester, + Validator: phase0.ValidatorIndex(39393), + } +} + +func makeCTrace(slot phase0.Slot) *model.CommitteeDutyTrace { + return &model.CommitteeDutyTrace{ + DutyTrace: model.DutyTrace{ + Slot: slot, + Pre: nil, + Rounds: nil, + Post: nil, + }, + CommitteeID: [32]byte{}, + OperatorIDs: nil, + AttestationDataRoot: [32]byte{}, + SyncCommitteeMessageRoot: [32]byte{}, + } +} From 3dc2fdc24a220a4dcfc8974cdcfd03e8cb7bcf99 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Mon, 20 Jan 2025 18:38:25 +0000 Subject: [PATCH 02/24] fixes --- api/handlers/exporter.go | 224 ++++++++++++++++++++++---------- api/server/server.go | 5 +- exporter/v2/store/store.go | 191 ++++++++++++++++----------- exporter/v2/store/store_test.go | 30 ++++- 4 files changed, 300 insertions(+), 150 deletions(-) diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index cd5fa863bf..8ea92d5a61 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -32,72 +32,6 @@ type ParticipantResponse struct { } `json:"message"` } -func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error { - return nil -} - -func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error { - var request struct { - From uint64 `json:"from"` - To uint64 `json:"to"` - Roles api.RoleSlice `json:"roles"` - VIndex uint64 `json:"index"` - } - - if err := api.Bind(r, &request); err != nil { - return api.BadRequestError(err) - } - - if request.From > request.To { - return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) - } - - if len(request.Roles) == 0 { - return api.BadRequestError(fmt.Errorf("at least one role is required")) - } - - if request.VIndex == 0 { - return api.BadRequestError(fmt.Errorf("validator index is required")) - } - vIndex := phase0.ValidatorIndex(request.VIndex) - - var results []*model.ValidatorDutyTrace - - for s := request.From; s <= request.To; s++ { - slot := phase0.Slot(s) - for _, r := range request.Roles { - role := spectypes.BeaconRole(r) - traces, err := e.TraceStore.GetValidatorDuties(role, slot, vIndex) - if err != nil { - return api.Error(fmt.Errorf("error getting traces: %w", err)) - } - results = append(results, traces...) - } - } - - return api.Render(w, r, toValidatorTraceResponse(results)) -} - -type validatorTraceResponse struct { - Data []validatorTrace `json:"data"` -} - -type validatorTrace struct { - Slot phase0.Slot `json:"slot"` -} - -func toValidatorTrace(_ *model.ValidatorDutyTrace) validatorTrace { - return validatorTrace{} -} - -func toValidatorTraceResponse(traces []*model.ValidatorDutyTrace) *validatorTraceResponse { - r := &validatorTraceResponse{} - for _, t := range traces { - r.Data = append(r.Data, toValidatorTrace(t)) - } - return r -} - func (e *Exporter) Decideds(w http.ResponseWriter, r *http.Request) error { var request struct { From uint64 `json:"from"` @@ -166,3 +100,161 @@ func transformToParticipantResponse(role spectypes.BeaconRole, entry qbftstorage return response } + +type committeeTraceResponse struct { + Data []committeeTrace `json:"data"` +} + +type committeeTrace struct { + Slot phase0.Slot `json:"slot"` +} + +func toCommitteeTrace(*model.CommitteeDutyTrace) committeeTrace { + return committeeTrace{} +} + +func toCommitteeTraceResponse(duties []*model.CommitteeDutyTrace) *committeeTraceResponse { + r := &committeeTraceResponse{} + for _, t := range duties { + r.Data = append(r.Data, toCommitteeTrace(t)) + } + return r +} + +func (e *Exporter) OperatorTraces(w http.ResponseWriter, r *http.Request) error { + var request struct { + From uint64 `json:"from"` + To uint64 `json:"to"` + OperatorID []uint64 `json:"operatorID"` + } + + if err := api.Bind(r, &request); err != nil { + return api.BadRequestError(err) + } + + var indexes []spectypes.OperatorID + for _, id := range request.OperatorID { + indexes = append(indexes, spectypes.OperatorID(id)) + } + + var duties []*model.CommitteeDutyTrace + for s := request.From; s <= request.To; s++ { + slot := phase0.Slot(s) + dd, err := e.TraceStore.GetCommitteeDutiesByOperator(indexes, slot) + if err != nil { + return api.Error(fmt.Errorf("error getting duties: %w", err)) + } + duties = append(duties, dd...) + } + + return api.Render(w, r, toCommitteeTraceResponse(duties)) +} + +func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error { + var request struct { + From uint64 `json:"from"` + To uint64 `json:"to"` + CommitteeID string `json:"committeeID"` + } + + if err := api.Bind(r, &request); err != nil { + return api.BadRequestError(err) + } + + if request.From > request.To { + return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) + } + + if len(request.CommitteeID) != 32 { + return api.BadRequestError(fmt.Errorf("committee ID is required")) + } + + var committeeID spectypes.CommitteeID + copy(committeeID[:], []byte(request.CommitteeID)) + + var duties []*model.CommitteeDutyTrace + for s := request.From; s <= request.To; s++ { + slot := phase0.Slot(s) + duty, err := e.TraceStore.GetCommitteeDuty(slot, committeeID) + if err != nil { + return api.Error(fmt.Errorf("error getting duties: %w", err)) + } + duties = append(duties, duty) + } + + return api.Render(w, r, toCommitteeTraceResponse(duties)) +} + +type validatorTraceResponse struct { + Data []validatorTrace `json:"data"` +} + +type validatorTrace struct { + Slot phase0.Slot `json:"slot"` +} + +func toValidatorTrace(*model.ValidatorDutyTrace) validatorTrace { + return validatorTrace{} +} + +func toValidatorTraceResponse(duties []*model.ValidatorDutyTrace) *validatorTraceResponse { + r := &validatorTraceResponse{} + for _, t := range duties { + r.Data = append(r.Data, toValidatorTrace(t)) + } + return r +} + +func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error { + var request struct { + From uint64 `json:"from"` + To uint64 `json:"to"` + Roles api.RoleSlice `json:"roles"` + VIndex uint64 `json:"index"` + } + + if err := api.Bind(r, &request); err != nil { + return api.BadRequestError(err) + } + + if request.From > request.To { + return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) + } + + if len(request.Roles) == 0 { + return api.BadRequestError(fmt.Errorf("at least one role is required")) + } + + var results []*model.ValidatorDutyTrace + + if request.VIndex == 0 { + for s := request.From; s <= request.To; s++ { + slot := phase0.Slot(s) + for _, r := range request.Roles { + role := spectypes.BeaconRole(r) + duties, err := e.TraceStore.GetAllValidatorDuties(role, slot) + if err != nil { + return api.Error(fmt.Errorf("error getting duties: %w", err)) + } + results = append(results, duties...) + } + } + return api.Render(w, r, toValidatorTraceResponse(results)) + } + + vIndex := phase0.ValidatorIndex(request.VIndex) + + for s := request.From; s <= request.To; s++ { + slot := phase0.Slot(s) + for _, r := range request.Roles { + role := spectypes.BeaconRole(r) + duty, err := e.TraceStore.GetValidatorDuty(role, slot, vIndex) + if err != nil { + return api.Error(fmt.Errorf("error getting duties: %w", err)) + } + results = append(results, duty) + } + } + + return api.Render(w, r, toValidatorTraceResponse(results)) +} diff --git a/api/server/server.go b/api/server/server.go index f6eb20e41a..737ef08a4c 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -55,8 +55,9 @@ func (s *Server) Run() error { // We kept both GET and POST methods to ensure compatibility and avoid breaking changes for clients that may rely on either method router.Get("/v1/exporter/decideds", api.Handler(s.exporter.Decideds)) router.Post("/v1/exporter/decideds", api.Handler(s.exporter.Decideds)) - router.Get("/v1/exporter/validator/traces", api.Handler(s.exporter.ValidatorTraces)) - router.Get("/v1/exporter/committee/traces", api.Handler(s.exporter.CommitteeTraces)) + router.Get("/v1/exporter/traces/validator", api.Handler(s.exporter.ValidatorTraces)) + router.Get("/v1/exporter/traces/committee", api.Handler(s.exporter.CommitteeTraces)) + router.Get("/v1/exporter/traces/operator", api.Handler(s.exporter.OperatorTraces)) s.logger.Info("Serving SSV API", zap.String("addr", s.addr)) diff --git a/exporter/v2/store/store.go b/exporter/v2/store/store.go index 54672e0f06..3596526774 100644 --- a/exporter/v2/store/store.go +++ b/exporter/v2/store/store.go @@ -11,9 +11,9 @@ import ( ) const ( - validatorDutyTraceKey = "vd" - commiteeDutyTraceKey = "cd" - commiteeVIndexKey = "ci" + validatorDutyTraceKey = "vd" + commiteeDutyTraceKey = "cd" + commiteeOperatorIndexKey = "ci" ) type DutyTraceStore struct { @@ -26,55 +26,55 @@ func New(db basedb.Database) *DutyTraceStore { } } -func (s *DutyTraceStore) GetValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (out []*model.ValidatorDutyTrace, err error) { - prefix := s.makeValidatorSlotPrefix(role, slot, index) - err = s.db.GetAll(prefix, func(_ int, o basedb.Obj) error { - vdt := new(model.ValidatorDutyTrace) - err := vdt.UnmarshalSSZ(o.Value) - if err != nil { - return fmt.Errorf("unmarshall ValidatorDutyTrace: %w", err) - } - out = append(out, vdt) - return nil - }) +func (s *DutyTraceStore) SaveValidatorDuty(dto *model.ValidatorDutyTrace) error { + role, slot, index := dto.Role, dto.Slot, dto.Validator + prefix := s.makeValidatorPrefix(role, slot, index) + + value, err := dto.MarshalSSZ() if err != nil { - return nil, err + return fmt.Errorf("marshall validator duty: %w", err) } - return -} + tx := s.db.Begin() + defer tx.Discard() -func (s *DutyTraceStore) GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) (out []*model.ValidatorDutyTrace, err error) { - panic("fix me") -} + if err = s.db.Using(tx).Set(prefix, nil, value); err != nil { + return fmt.Errorf("save validator duty: %w", err) + } -func (s *DutyTraceStore) GetCommitteeDuties(slot phase0.Slot, role spectypes.BeaconRole) (out []*model.CommitteeDutyTrace, err error) { - panic("fix me") -} + if err := tx.Commit(); err != nil { + return fmt.Errorf("commit transaction: %w", err) + } -func (s *DutyTraceStore) GetCommitteeDutiesByValidator(indexes []phase0.ValidatorIndex, slot phase0.Slot) (out []*model.CommitteeDutyTrace, err error) { - prefixes := s.makeCommiteeValidatorIndexPrefixes(indexes, slot) - keys := make([][]byte, 0) + return nil +} - tx := s.db.BeginRead() - defer tx.Discard() +func (s *DutyTraceStore) GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (duty *model.ValidatorDutyTrace, err error) { + prefix := s.makeValidatorPrefix(role, slot, index) + obj, found, err := s.db.Get(prefix, nil) + if err != nil { + return nil, fmt.Errorf("get validator duty: %w", err) + } + if !found { + return nil, fmt.Errorf("validator duty not found") + } - for _, prefix := range prefixes { - err = s.db.GetAll(prefix, func(_ int, o basedb.Obj) error { - keys = append(keys, o.Value) - return nil - }) - if err != nil { - return nil, err - } + duty = new(model.ValidatorDutyTrace) + if err := duty.UnmarshalSSZ(obj.Value); err != nil { + return nil, fmt.Errorf("unmarshall validator duty: %w", err) } - err = s.db.GetMany(nil, keys, func(o basedb.Obj) error { - vdt := new(model.CommitteeDutyTrace) - if err := vdt.UnmarshalSSZ(o.Value); err != nil { - return fmt.Errorf("unmarshall ValidatorDutyTrace: %w", err) + return duty, nil +} + +func (s *DutyTraceStore) GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) (duties []*model.ValidatorDutyTrace, err error) { + prefix := s.makeValidatorPrefix(role, slot) + err = s.db.GetAll(prefix, func(_ int, obj basedb.Obj) error { + duty := new(model.ValidatorDutyTrace) + if err := duty.UnmarshalSSZ(obj.Value); err != nil { + return fmt.Errorf("unmarshall validator duty: %w", err) } - out = append(out, vdt) + duties = append(duties, duty) return nil }) if err != nil { @@ -84,74 +84,113 @@ func (s *DutyTraceStore) GetCommitteeDutiesByValidator(indexes []phase0.Validato return } -func (s *DutyTraceStore) SaveValidatorDuty(dto *model.ValidatorDutyTrace) error { - prefix := s.makeValidatorPrefix(dto) +func (s *DutyTraceStore) SaveCommiteeDuty(duty *model.CommitteeDutyTrace) error { + prefix := s.makeCommitteePrefix(duty.Slot, duty.CommitteeID) - value, err := dto.MarshalSSZ() + value, err := duty.MarshalSSZ() if err != nil { - return fmt.Errorf("marshall ValidatorDutyTrace: %w", err) + return fmt.Errorf("marshall committee duty: %w", err) } tx := s.db.Begin() defer tx.Discard() - err = s.db.Using(tx).Set(prefix, nil, value) - if err != nil { - return fmt.Errorf("save full ValidatorDutyTrace: %w", err) + if err = s.db.Using(tx).Set(prefix, nil, value); err != nil { + return fmt.Errorf("save committee duty: %w", err) } - return tx.Commit() -} + prefixes := s.makeCommiteeOperatorPrefixes(duty.OperatorIDs, duty.Slot) + + for _, ref := range prefixes { + if err = s.db.Using(tx).Set(ref, nil, prefix); err != nil { + return fmt.Errorf("save committee duty index: %w", err) + } + } -func (s *DutyTraceStore) SaveCommiteeDuty(dto *model.CommitteeDutyTrace) error { - prefix := s.makeCommitteePrefix(dto) + if err := tx.Commit(); err != nil { + return fmt.Errorf("commit transaction: %w", err) + } - value, err := dto.MarshalSSZ() + return nil +} + +func (s *DutyTraceStore) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (duty *model.CommitteeDutyTrace, err error) { + prefix := s.makeCommitteePrefix(slot, committeeID) + obj, found, err := s.db.Get(prefix, nil) if err != nil { - return fmt.Errorf("marshall ValidatorDutyTrace: %w", err) + return nil, fmt.Errorf("get committee duty: %w", err) + } + if !found { + return nil, fmt.Errorf("committee duty not found") } - tx := s.db.Begin() + duty = new(model.CommitteeDutyTrace) + if err := duty.UnmarshalSSZ(obj.Value); err != nil { + return nil, fmt.Errorf("unmarshall committee duty: %w", err) + } + + return +} + +func (s *DutyTraceStore) GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) (out []*model.CommitteeDutyTrace, err error) { + prefixes := s.makeCommiteeOperatorPrefixes(indexes, slot) + keys := make([][]byte, 0) + + tx := s.db.BeginRead() defer tx.Discard() - err = s.db.Using(tx).Set(prefix, nil, value) + for _, prefix := range prefixes { + obj, found, err := s.db.Get(prefix, nil) + if err != nil { + return nil, fmt.Errorf("get committee duty index: %w", err) + } + if !found { + return nil, fmt.Errorf("committee duty index not found") + } + keys = append(keys, obj.Value) + } + + err = s.db.GetMany(nil, keys, func(obj basedb.Obj) error { + duty := new(model.CommitteeDutyTrace) + if err := duty.UnmarshalSSZ(obj.Value); err != nil { + return fmt.Errorf("unmarshall committee duty: %w", err) + } + out = append(out, duty) + return nil + }) if err != nil { - return fmt.Errorf("save full ValidatorDutyTrace: %w", err) + return nil, err } - return tx.Commit() + return } -func (s *DutyTraceStore) makeValidatorSlotPrefix(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) []byte { +// role + slot + ?index +func (s *DutyTraceStore) makeValidatorPrefix(role spectypes.BeaconRole, slot phase0.Slot, index ...phase0.ValidatorIndex) []byte { prefix := make([]byte, 0, len(validatorDutyTraceKey)+1+4) prefix = append(prefix, []byte(validatorDutyTraceKey)...) prefix = append(prefix, byte(role&0xff)) prefix = append(prefix, slotToByteSlice(slot)...) - prefix = append(prefix, uInt64ToByteSlice(uint64(index))...) - return prefix -} - -func (s *DutyTraceStore) makeValidatorPrefix(dto *model.ValidatorDutyTrace) []byte { - prefix := make([]byte, 0, len(validatorDutyTraceKey)+4+1+8) - prefix = append(prefix, []byte(validatorDutyTraceKey)...) - prefix = append(prefix, byte(dto.Role&0xff)) - prefix = append(prefix, slotToByteSlice(dto.Slot)...) - prefix = append(prefix, uInt64ToByteSlice(uint64(dto.Validator))...) + if len(index) > 0 { // optional + prefix = append(prefix, uInt64ToByteSlice(uint64(index[0]))...) + } return prefix } -func (s *DutyTraceStore) makeCommitteePrefix(dto *model.CommitteeDutyTrace) []byte { - prefix := make([]byte, 0, len(commiteeDutyTraceKey)+4+1+8) +// role + slot +func (s *DutyTraceStore) makeCommitteePrefix(slot phase0.Slot, id spectypes.CommitteeID) []byte { + prefix := make([]byte, 0, len(commiteeDutyTraceKey)+4+32) prefix = append(prefix, []byte(commiteeDutyTraceKey)...) - prefix = append(prefix, slotToByteSlice(dto.Slot)...) - prefix = append(prefix, dto.CommitteeID[:]...) + prefix = append(prefix, slotToByteSlice(slot)...) + prefix = append(prefix, id[:]...) return prefix } -func (s *DutyTraceStore) makeCommiteeValidatorIndexPrefixes(ii []phase0.ValidatorIndex, slot phase0.Slot) (keys [][]byte) { +// slot + index +func (s *DutyTraceStore) makeCommiteeOperatorPrefixes(ii []spectypes.OperatorID, slot phase0.Slot) (keys [][]byte) { for _, index := range ii { - prefix := make([]byte, 0, len(commiteeVIndexKey)+1+8) - prefix = append(prefix, []byte(commiteeVIndexKey)...) + prefix := make([]byte, 0, len(commiteeOperatorIndexKey)+4+32) + prefix = append(prefix, []byte(commiteeOperatorIndexKey)...) prefix = append(prefix, slotToByteSlice(slot)...) prefix = append(prefix, uInt64ToByteSlice(uint64(index))...) keys = append(keys, prefix) diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go index 4c076bd518..077235ea3d 100644 --- a/exporter/v2/store/store_test.go +++ b/exporter/v2/store/store_test.go @@ -9,6 +9,7 @@ import ( store "github.com/ssvlabs/ssv/exporter/v2/store" "github.com/ssvlabs/ssv/storage/basedb" "github.com/ssvlabs/ssv/storage/kv" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -23,6 +24,14 @@ func TestSaveCommitteeDutyTrace(t *testing.T) { trace2 := makeCTrace(2) _, _ = trace1, trace2 + + store := store.New(db) + require.NoError(t, store.SaveCommiteeDuty(trace1)) + require.NoError(t, store.SaveCommiteeDuty(trace2)) + + duty, err := store.GetCommitteeDuty(phase0.Slot(1), [32]byte{'a'}) + require.NoError(t, err) + assert.Equal(t, phase0.Slot(1), duty.Slot) } func TestSaveValidatorDutyTrace(t *testing.T) { @@ -38,17 +47,26 @@ func TestSaveValidatorDutyTrace(t *testing.T) { require.NoError(t, store.SaveValidatorDuty(trace1)) require.NoError(t, store.SaveValidatorDuty(trace2)) - traces, err := store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(1), phase0.ValidatorIndex(39393)) + trace, err := store.GetValidatorDuty(types.BNRoleAttester, phase0.Slot(1), phase0.ValidatorIndex(39393)) require.NoError(t, err) - require.Len(t, traces, 1) + require.Equal(t, phase0.Slot(1), trace.Slot) + require.Equal(t, phase0.ValidatorIndex(39393), trace.Validator) - traces, err = store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(2), phase0.ValidatorIndex(39393)) + trace, err = store.GetValidatorDuty(types.BNRoleAttester, phase0.Slot(2), phase0.ValidatorIndex(39393)) + require.NoError(t, err) + require.Equal(t, phase0.Slot(2), trace.Slot) + require.Equal(t, phase0.ValidatorIndex(39393), trace.Validator) + + trace, err = store.GetValidatorDuty(types.BNRoleAttester, phase0.Slot(3), phase0.ValidatorIndex(39393)) + require.Error(t, err) + + traces, err := store.GetAllValidatorDuties(types.BNRoleAttester, phase0.Slot(1)) require.NoError(t, err) require.Len(t, traces, 1) - traces, err = store.GetValidatorDuties(types.BNRoleAttester, phase0.Slot(3), phase0.ValidatorIndex(39393)) + traces, err = store.GetAllValidatorDuties(types.BNRoleAttester, phase0.Slot(2)) require.NoError(t, err) - require.Empty(t, traces) + require.Len(t, traces, 1) } func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { @@ -72,7 +90,7 @@ func makeCTrace(slot phase0.Slot) *model.CommitteeDutyTrace { Rounds: nil, Post: nil, }, - CommitteeID: [32]byte{}, + CommitteeID: [32]byte{'a'}, OperatorIDs: nil, AttestationDataRoot: [32]byte{}, SyncCommitteeMessageRoot: [32]byte{}, From eaa735c09367b9f8f38348b7a51556e57925acbc Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 21 Jan 2025 10:30:15 +0000 Subject: [PATCH 03/24] gen script --- exporter/v2/model.go | 1 + exporter/v2/model_encoding.go | 2 +- scripts/exporter_model_ssz.sh | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 scripts/exporter_model_ssz.sh diff --git a/exporter/v2/model.go b/exporter/v2/model.go index e10df64ae5..7f7b3a33e3 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -5,6 +5,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" ) +//go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path . --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { DutyTrace Role spectypes.BeaconRole diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 65a3721600..544c26b4ab 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 5827b29a742e60a78d47747ecf052a0ff9466558698dd85659565964ec1e3610 +// Hash: cb7b038f485e6b710677a90cbbffa048fdbf8636e81ea388baaec2c10015b9da // Version: 0.1.3 package exporter diff --git a/scripts/exporter_model_ssz.sh b/scripts/exporter_model_ssz.sh new file mode 100755 index 0000000000..a6151a967c --- /dev/null +++ b/scripts/exporter_model_ssz.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eo pipefail +go mod vendor + +go generate ./exporter/v2/model.go From 40ee8cdc5b4070492514f699d9edab1386919b0e Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 21 Jan 2025 21:43:32 +0000 Subject: [PATCH 04/24] wip --- exporter/v2/model.go | 51 ++- exporter/v2/model_encoding.go | 785 +++++++++++++++++++++++--------- exporter/v2/store/store_test.go | 10 +- 3 files changed, 598 insertions(+), 248 deletions(-) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 7f7b3a33e3..21238a3412 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -8,35 +8,56 @@ import ( //go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path . --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { DutyTrace + Pre []*MessageTrace `ssz-max:"13"` + Post []*MessageTrace `ssz-max:"13"` Role spectypes.BeaconRole Validator phase0.ValidatorIndex } type CommitteeDutyTrace struct { DutyTrace - CommitteeID spectypes.CommitteeID `ssz-size:"32"` - OperatorIDs []spectypes.OperatorID `ssz-max:"4"` - AttestationDataRoot phase0.Root `ssz-size:"32"` // Computed from BeaconVote: See example in CommitteeObserver - SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` // Computed from BeaconVote: See example in CommitteeObserver + Post []*CommitteeMessageTrace `ssz-max:"13"` + + CommitteeID spectypes.CommitteeID `ssz-size:"32"` + OperatorIDs []spectypes.OperatorID `ssz-max:"13"` + + // maybe not needed + AttestationDataRoot phase0.Root `ssz-size:"32"` + SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` +} + +type CommitteeMessageTrace struct { + BeaconRoot []phase0.Root `ssz-max:"1500" ssz-size:"32"` + Validators []phase0.ValidatorIndex `ssz-max:"1500"` + + Signer spectypes.OperatorID + ReceivedTime uint64 // TODO fixme } type DutyTrace struct { Slot phase0.Slot - Pre []*MessageTrace `ssz-max:"4"` - Rounds []*RoundTrace `ssz-max:"4"` - Post []*MessageTrace `ssz-max:"4"` + Rounds []*RoundTrace `ssz-max:"15"` } type RoundTrace struct { - ProposalRoot [32]byte `ssz-size:"32"` - ProposalReceived uint64 // TODO fixme - Prepares []*MessageTrace `ssz-max:"4"` // Only recorded if root matches proposal. - Commits []*MessageTrace `ssz-max:"4"` // Only recorded if root matches proposal. + Proposer spectypes.OperatorID // can be computed or saved + // ProposalData + ProposalRoot phase0.Root `ssz-size:"32"` + ProposalReceivedTime uint64 // TODO fix time + Prepares []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. + Commits []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. + RoundChanges []*RoundChangeTrace `ssz-max:"13"` +} + +type RoundChangeTrace struct { + MessageTrace + preparedRound uint8 + PrepareMessages []*MessageTrace `ssz-max:"32"` } type MessageTrace struct { - BeaconRoot phase0.Root `ssz-size:"32"` - Signer spectypes.OperatorID - Validator phase0.ValidatorIndex // Only present in CommitteeDutyTrace.Post - Received uint64 // TODO fixme + BeaconRoot phase0.Root `ssz-size:"32"` + Signer spectypes.OperatorID + Validator phase0.ValidatorIndex // Only present in CommitteeDutyTrace.Post -> remove + ReceivedTime uint64 // TODO fix time } diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 544c26b4ab..a1c41850be 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: cb7b038f485e6b710677a90cbbffa048fdbf8636e81ea388baaec2c10015b9da +// Hash: a0dae63eb54e425273196828e076eb25245229bfc7107a06ec3d480951ea7349 // Version: 0.1.3 package exporter @@ -22,17 +22,17 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (0) 'Slot' dst = ssz.MarshalUint64(dst, uint64(v.Slot)) - // Offset (1) 'Pre' - dst = ssz.WriteOffset(dst, offset) - offset += len(v.Pre) * 56 - - // Offset (2) 'Rounds' + // Offset (1) 'Rounds' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(v.Rounds); ii++ { offset += 4 offset += v.Rounds[ii].SizeSSZ() } + // Offset (2) 'Pre' + dst = ssz.WriteOffset(dst, offset) + offset += len(v.Pre) * 56 + // Offset (3) 'Post' dst = ssz.WriteOffset(dst, offset) offset += len(v.Post) * 56 @@ -43,20 +43,9 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (5) 'Validator' dst = ssz.MarshalUint64(dst, uint64(v.Validator)) - // Field (1) 'Pre' - if size := len(v.Pre); size > 4 { - err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Pre", size, 4) - return - } - for ii := 0; ii < len(v.Pre); ii++ { - if dst, err = v.Pre[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (2) 'Rounds' - if size := len(v.Rounds); size > 4 { - err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Rounds", size, 4) + // Field (1) 'Rounds' + if size := len(v.Rounds); size > 15 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Rounds", size, 15) return } { @@ -72,9 +61,20 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } + // Field (2) 'Pre' + if size := len(v.Pre); size > 13 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Pre", size, 13) + return + } + for ii := 0; ii < len(v.Pre); ii++ { + if dst, err = v.Pre[ii].MarshalSSZTo(dst); err != nil { + return + } + } + // Field (3) 'Post' - if size := len(v.Post); size > 4 { - err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Post", size, 4) + if size := len(v.Post); size > 13 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Post", size, 13) return } for ii := 0; ii < len(v.Post); ii++ { @@ -100,7 +100,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // Field (0) 'Slot' v.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) - // Offset (1) 'Pre' + // Offset (1) 'Rounds' if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { return ssz.ErrOffset } @@ -109,7 +109,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { return ssz.ErrInvalidVariableOffset } - // Offset (2) 'Rounds' + // Offset (2) 'Pre' if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { return ssz.ErrOffset } @@ -125,50 +125,50 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // Field (5) 'Validator' v.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[28:36])) - // Field (1) 'Pre' + // Field (1) 'Rounds' { buf = tail[o1:o2] - num, err := ssz.DivideInt2(len(buf), 56, 4) + num, err := ssz.DecodeDynamicLength(buf, 15) if err != nil { return err } - v.Pre = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if v.Pre[ii] == nil { - v.Pre[ii] = new(MessageTrace) + v.Rounds = make([]*RoundTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if v.Rounds[indx] == nil { + v.Rounds[indx] = new(RoundTrace) } - if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = v.Rounds[indx].UnmarshalSSZ(buf); err != nil { return err } + return nil + }) + if err != nil { + return err } } - // Field (2) 'Rounds' + // Field (2) 'Pre' { buf = tail[o2:o3] - num, err := ssz.DecodeDynamicLength(buf, 4) + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } - v.Rounds = make([]*RoundTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if v.Rounds[indx] == nil { - v.Rounds[indx] = new(RoundTrace) + v.Pre = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if v.Pre[ii] == nil { + v.Pre[ii] = new(MessageTrace) } - if err = v.Rounds[indx].UnmarshalSSZ(buf); err != nil { + if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } - return nil - }) - if err != nil { - return err } } // Field (3) 'Post' { buf = tail[o3:] - num, err := ssz.DivideInt2(len(buf), 56, 4) + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -189,15 +189,15 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { func (v *ValidatorDutyTrace) SizeSSZ() (size int) { size = 36 - // Field (1) 'Pre' - size += len(v.Pre) * 56 - - // Field (2) 'Rounds' + // Field (1) 'Rounds' for ii := 0; ii < len(v.Rounds); ii++ { size += 4 size += v.Rounds[ii].SizeSSZ() } + // Field (2) 'Pre' + size += len(v.Pre) * 56 + // Field (3) 'Post' size += len(v.Post) * 56 @@ -216,43 +216,43 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { // Field (0) 'Slot' hh.PutUint64(uint64(v.Slot)) - // Field (1) 'Pre' + // Field (1) 'Rounds' { subIndx := hh.Index() - num := uint64(len(v.Pre)) - if num > 4 { + num := uint64(len(v.Rounds)) + if num > 15 { err = ssz.ErrIncorrectListSize return } - for _, elem := range v.Pre { + for _, elem := range v.Rounds { if err = elem.HashTreeRootWith(hh); err != nil { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 15) } - // Field (2) 'Rounds' + // Field (2) 'Pre' { subIndx := hh.Index() - num := uint64(len(v.Rounds)) - if num > 4 { + num := uint64(len(v.Pre)) + if num > 13 { err = ssz.ErrIncorrectListSize return } - for _, elem := range v.Rounds { + for _, elem := range v.Pre { if err = elem.HashTreeRootWith(hh); err != nil { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 13) } // Field (3) 'Post' { subIndx := hh.Index() num := uint64(len(v.Post)) - if num > 4 { + if num > 13 { err = ssz.ErrIncorrectListSize return } @@ -261,7 +261,7 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 13) } // Field (4) 'Role' @@ -287,53 +287,41 @@ func (c *CommitteeDutyTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the CommitteeDutyTrace object to a target array func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(120) + offset := int(116) // Field (0) 'Slot' dst = ssz.MarshalUint64(dst, uint64(c.Slot)) - // Offset (1) 'Pre' - dst = ssz.WriteOffset(dst, offset) - offset += len(c.Pre) * 56 - - // Offset (2) 'Rounds' + // Offset (1) 'Rounds' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(c.Rounds); ii++ { offset += 4 offset += c.Rounds[ii].SizeSSZ() } - // Offset (3) 'Post' + // Offset (2) 'Post' dst = ssz.WriteOffset(dst, offset) - offset += len(c.Post) * 56 + for ii := 0; ii < len(c.Post); ii++ { + offset += 4 + offset += c.Post[ii].SizeSSZ() + } - // Field (4) 'CommitteeID' + // Field (3) 'CommitteeID' dst = append(dst, c.CommitteeID[:]...) - // Offset (5) 'OperatorIDs' + // Offset (4) 'OperatorIDs' dst = ssz.WriteOffset(dst, offset) offset += len(c.OperatorIDs) * 8 - // Field (6) 'AttestationDataRoot' + // Field (5) 'AttestationDataRoot' dst = append(dst, c.AttestationDataRoot[:]...) - // Field (7) 'SyncCommitteeMessageRoot' + // Field (6) 'SyncCommitteeMessageRoot' dst = append(dst, c.SyncCommitteeMessageRoot[:]...) - // Field (1) 'Pre' - if size := len(c.Pre); size > 4 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Pre", size, 4) - return - } - for ii := 0; ii < len(c.Pre); ii++ { - if dst, err = c.Pre[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (2) 'Rounds' - if size := len(c.Rounds); size > 4 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Rounds", size, 4) + // Field (1) 'Rounds' + if size := len(c.Rounds); size > 15 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Rounds", size, 15) return } { @@ -349,20 +337,27 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (3) 'Post' - if size := len(c.Post); size > 4 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 4) + // Field (2) 'Post' + if size := len(c.Post); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 13) return } + { + offset = 4 * len(c.Post) + for ii := 0; ii < len(c.Post); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Post[ii].SizeSSZ() + } + } for ii := 0; ii < len(c.Post); ii++ { if dst, err = c.Post[ii].MarshalSSZTo(dst); err != nil { return } } - // Field (5) 'OperatorIDs' - if size := len(c.OperatorIDs); size > 4 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 4) + // Field (4) 'OperatorIDs' + if size := len(c.OperatorIDs); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) return } for ii := 0; ii < len(c.OperatorIDs); ii++ { @@ -376,71 +371,48 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 120 { + if size < 116 { return ssz.ErrSize } tail := buf - var o1, o2, o3, o5 uint64 + var o1, o2, o4 uint64 // Field (0) 'Slot' c.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) - // Offset (1) 'Pre' + // Offset (1) 'Rounds' if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { return ssz.ErrOffset } - if o1 < 120 { + if o1 < 116 { return ssz.ErrInvalidVariableOffset } - // Offset (2) 'Rounds' + // Offset (2) 'Post' if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { return ssz.ErrOffset } - // Offset (3) 'Post' - if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { - return ssz.ErrOffset - } - - // Field (4) 'CommitteeID' - copy(c.CommitteeID[:], buf[20:52]) + // Field (3) 'CommitteeID' + copy(c.CommitteeID[:], buf[16:48]) - // Offset (5) 'OperatorIDs' - if o5 = ssz.ReadOffset(buf[52:56]); o5 > size || o3 > o5 { + // Offset (4) 'OperatorIDs' + if o4 = ssz.ReadOffset(buf[48:52]); o4 > size || o2 > o4 { return ssz.ErrOffset } - // Field (6) 'AttestationDataRoot' - copy(c.AttestationDataRoot[:], buf[56:88]) + // Field (5) 'AttestationDataRoot' + copy(c.AttestationDataRoot[:], buf[52:84]) - // Field (7) 'SyncCommitteeMessageRoot' - copy(c.SyncCommitteeMessageRoot[:], buf[88:120]) + // Field (6) 'SyncCommitteeMessageRoot' + copy(c.SyncCommitteeMessageRoot[:], buf[84:116]) - // Field (1) 'Pre' + // Field (1) 'Rounds' { buf = tail[o1:o2] - num, err := ssz.DivideInt2(len(buf), 56, 4) - if err != nil { - return err - } - c.Pre = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if c.Pre[ii] == nil { - c.Pre[ii] = new(MessageTrace) - } - if err = c.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { - return err - } - } - } - - // Field (2) 'Rounds' - { - buf = tail[o2:o3] - num, err := ssz.DecodeDynamicLength(buf, 4) + num, err := ssz.DecodeDynamicLength(buf, 15) if err != nil { return err } @@ -459,28 +431,29 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (3) 'Post' + // Field (2) 'Post' { - buf = tail[o3:o5] - num, err := ssz.DivideInt2(len(buf), 56, 4) + buf = tail[o2:o4] + num, err := ssz.DecodeDynamicLength(buf, 13) if err != nil { return err } - c.Post = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if c.Post[ii] == nil { - c.Post[ii] = new(MessageTrace) - } - if err = c.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + c.Post = make([]*CommitteeMessageTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if err = c.Post[indx].UnmarshalSSZ(buf); err != nil { return err } + return nil + }) + if err != nil { + return err } } - // Field (5) 'OperatorIDs' + // Field (4) 'OperatorIDs' { - buf = tail[o5:] - num, err := ssz.DivideInt2(len(buf), 8, 4) + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 8, 13) if err != nil { return err } @@ -494,21 +467,21 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the CommitteeDutyTrace object func (c *CommitteeDutyTrace) SizeSSZ() (size int) { - size = 120 - - // Field (1) 'Pre' - size += len(c.Pre) * 56 + size = 116 - // Field (2) 'Rounds' + // Field (1) 'Rounds' for ii := 0; ii < len(c.Rounds); ii++ { size += 4 size += c.Rounds[ii].SizeSSZ() } - // Field (3) 'Post' - size += len(c.Post) * 56 + // Field (2) 'Post' + for ii := 0; ii < len(c.Post); ii++ { + size += 4 + size += c.Post[ii].SizeSSZ() + } - // Field (5) 'OperatorIDs' + // Field (4) 'OperatorIDs' size += len(c.OperatorIDs) * 8 return @@ -526,27 +499,11 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { // Field (0) 'Slot' hh.PutUint64(uint64(c.Slot)) - // Field (1) 'Pre' - { - subIndx := hh.Index() - num := uint64(len(c.Pre)) - if num > 4 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range c.Pre { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 4) - } - - // Field (2) 'Rounds' + // Field (1) 'Rounds' { subIndx := hh.Index() num := uint64(len(c.Rounds)) - if num > 4 { + if num > 15 { err = ssz.ErrIncorrectListSize return } @@ -555,14 +512,14 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 15) } - // Field (3) 'Post' + // Field (2) 'Post' { subIndx := hh.Index() num := uint64(len(c.Post)) - if num > 4 { + if num > 13 { err = ssz.ErrIncorrectListSize return } @@ -571,16 +528,16 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (4) 'CommitteeID' + // Field (3) 'CommitteeID' hh.PutBytes(c.CommitteeID[:]) - // Field (5) 'OperatorIDs' + // Field (4) 'OperatorIDs' { - if size := len(c.OperatorIDs); size > 4 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 4) + if size := len(c.OperatorIDs); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) return } subIndx := hh.Index() @@ -589,13 +546,13 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { } hh.FillUpTo32() numItems := uint64(len(c.OperatorIDs)) - hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(4, numItems, 8)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) } - // Field (6) 'AttestationDataRoot' + // Field (5) 'AttestationDataRoot' hh.PutBytes(c.AttestationDataRoot[:]) - // Field (7) 'SyncCommitteeMessageRoot' + // Field (6) 'SyncCommitteeMessageRoot' hh.PutBytes(c.SyncCommitteeMessageRoot[:]) hh.Merkleize(indx) @@ -607,6 +564,156 @@ func (c *CommitteeDutyTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(c) } +// MarshalSSZ ssz marshals the CommitteeMessageTrace object +func (c *CommitteeMessageTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) +} + +// MarshalSSZTo ssz marshals the CommitteeMessageTrace object to a target array +func (c *CommitteeMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(1044) + + // Field (0) 'BeaconRoot' + if size := len(c.BeaconRoot); size != 32 { + err = ssz.ErrVectorLengthFn("CommitteeMessageTrace.BeaconRoot", size, 32) + return + } + for ii := 0; ii < 32; ii++ { + dst = append(dst, c.BeaconRoot[ii][:]...) + } + + // Offset (1) 'Validators' + dst = ssz.WriteOffset(dst, offset) + offset += len(c.Validators) * 8 + + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(c.Signer)) + + // Field (3) 'ReceivedTime' + dst = ssz.MarshalUint64(dst, c.ReceivedTime) + + // Field (1) 'Validators' + if size := len(c.Validators); size > 1500 { + err = ssz.ErrListTooBigFn("CommitteeMessageTrace.Validators", size, 1500) + return + } + for ii := 0; ii < len(c.Validators); ii++ { + dst = ssz.MarshalUint64(dst, uint64(c.Validators[ii])) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the CommitteeMessageTrace object +func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 1044 { + return ssz.ErrSize + } + + tail := buf + var o1 uint64 + + // Field (0) 'BeaconRoot' + c.BeaconRoot = make([]phase0.Root, 32) + for ii := 0; ii < 32; ii++ { + copy(c.BeaconRoot[ii][:], buf[0:1024][ii*32:(ii+1)*32]) + } + + // Offset (1) 'Validators' + if o1 = ssz.ReadOffset(buf[1024:1028]); o1 > size { + return ssz.ErrOffset + } + + if o1 < 1044 { + return ssz.ErrInvalidVariableOffset + } + + // Field (2) 'Signer' + c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[1028:1036])) + + // Field (3) 'ReceivedTime' + c.ReceivedTime = ssz.UnmarshallUint64(buf[1036:1044]) + + // Field (1) 'Validators' + { + buf = tail[o1:] + num, err := ssz.DivideInt2(len(buf), 8, 1500) + if err != nil { + return err + } + c.Validators = make([]phase0.ValidatorIndex, num) + for ii := 0; ii < num; ii++ { + c.Validators[ii] = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the CommitteeMessageTrace object +func (c *CommitteeMessageTrace) SizeSSZ() (size int) { + size = 1044 + + // Field (1) 'Validators' + size += len(c.Validators) * 8 + + return +} + +// HashTreeRoot ssz hashes the CommitteeMessageTrace object +func (c *CommitteeMessageTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) +} + +// HashTreeRootWith ssz hashes the CommitteeMessageTrace object with a hasher +func (c *CommitteeMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'BeaconRoot' + { + if size := len(c.BeaconRoot); size != 32 { + err = ssz.ErrVectorLengthFn("CommitteeMessageTrace.BeaconRoot", size, 32) + return + } + subIndx := hh.Index() + for _, i := range c.BeaconRoot { + hh.Append(i[:]) + } + hh.Merkleize(subIndx) + } + + // Field (1) 'Validators' + { + if size := len(c.Validators); size > 1500 { + err = ssz.ErrListTooBigFn("CommitteeMessageTrace.Validators", size, 1500) + return + } + subIndx := hh.Index() + for _, i := range c.Validators { + hh.AppendUint64(uint64(i)) + } + hh.FillUpTo32() + numItems := uint64(len(c.Validators)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1500, numItems, 8)) + } + + // Field (2) 'Signer' + hh.PutUint64(uint64(c.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(c.ReceivedTime) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the CommitteeMessageTrace object +func (c *CommitteeMessageTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) +} + // MarshalSSZ ssz marshals the RoundTrace object func (r *RoundTrace) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(r) @@ -615,25 +722,35 @@ func (r *RoundTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the RoundTrace object to a target array func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(48) + offset := int(60) + + // Field (0) 'Proposer' + dst = ssz.MarshalUint64(dst, uint64(r.Proposer)) - // Field (0) 'ProposalRoot' + // Field (1) 'ProposalRoot' dst = append(dst, r.ProposalRoot[:]...) - // Field (1) 'ProposalReceived' - dst = ssz.MarshalUint64(dst, r.ProposalReceived) + // Field (2) 'ProposalReceivedTime' + dst = ssz.MarshalUint64(dst, r.ProposalReceivedTime) - // Offset (2) 'Prepares' + // Offset (3) 'Prepares' dst = ssz.WriteOffset(dst, offset) offset += len(r.Prepares) * 56 - // Offset (3) 'Commits' + // Offset (4) 'Commits' dst = ssz.WriteOffset(dst, offset) offset += len(r.Commits) * 56 - // Field (2) 'Prepares' - if size := len(r.Prepares); size > 4 { - err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 4) + // Offset (5) 'RoundChanges' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(r.RoundChanges); ii++ { + offset += 4 + offset += r.RoundChanges[ii].SizeSSZ() + } + + // Field (3) 'Prepares' + if size := len(r.Prepares); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 13) return } for ii := 0; ii < len(r.Prepares); ii++ { @@ -642,9 +759,9 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (3) 'Commits' - if size := len(r.Commits); size > 4 { - err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 4) + // Field (4) 'Commits' + if size := len(r.Commits); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 13) return } for ii := 0; ii < len(r.Commits); ii++ { @@ -653,6 +770,24 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } + // Field (5) 'RoundChanges' + if size := len(r.RoundChanges); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.RoundChanges", size, 13) + return + } + { + offset = 4 * len(r.RoundChanges) + for ii := 0; ii < len(r.RoundChanges); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += r.RoundChanges[ii].SizeSSZ() + } + } + for ii := 0; ii < len(r.RoundChanges); ii++ { + if dst, err = r.RoundChanges[ii].MarshalSSZTo(dst); err != nil { + return + } + } + return } @@ -660,37 +795,45 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 48 { + if size < 60 { return ssz.ErrSize } tail := buf - var o2, o3 uint64 + var o3, o4, o5 uint64 - // Field (0) 'ProposalRoot' - copy(r.ProposalRoot[:], buf[0:32]) + // Field (0) 'Proposer' + r.Proposer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[0:8])) - // Field (1) 'ProposalReceived' - r.ProposalReceived = ssz.UnmarshallUint64(buf[32:40]) + // Field (1) 'ProposalRoot' + copy(r.ProposalRoot[:], buf[8:40]) - // Offset (2) 'Prepares' - if o2 = ssz.ReadOffset(buf[40:44]); o2 > size { + // Field (2) 'ProposalReceivedTime' + r.ProposalReceivedTime = ssz.UnmarshallUint64(buf[40:48]) + + // Offset (3) 'Prepares' + if o3 = ssz.ReadOffset(buf[48:52]); o3 > size { return ssz.ErrOffset } - if o2 < 48 { + if o3 < 60 { return ssz.ErrInvalidVariableOffset } - // Offset (3) 'Commits' - if o3 = ssz.ReadOffset(buf[44:48]); o3 > size || o2 > o3 { + // Offset (4) 'Commits' + if o4 = ssz.ReadOffset(buf[52:56]); o4 > size || o3 > o4 { return ssz.ErrOffset } - // Field (2) 'Prepares' + // Offset (5) 'RoundChanges' + if o5 = ssz.ReadOffset(buf[56:60]); o5 > size || o4 > o5 { + return ssz.ErrOffset + } + + // Field (3) 'Prepares' { - buf = tail[o2:o3] - num, err := ssz.DivideInt2(len(buf), 56, 4) + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -705,10 +848,10 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (3) 'Commits' + // Field (4) 'Commits' { - buf = tail[o3:] - num, err := ssz.DivideInt2(len(buf), 56, 4) + buf = tail[o4:o5] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -722,19 +865,44 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { } } } + + // Field (5) 'RoundChanges' + { + buf = tail[o5:] + num, err := ssz.DecodeDynamicLength(buf, 13) + if err != nil { + return err + } + r.RoundChanges = make([]*RoundChangeTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if err = r.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } return err } // SizeSSZ returns the ssz encoded size in bytes for the RoundTrace object func (r *RoundTrace) SizeSSZ() (size int) { - size = 48 + size = 60 - // Field (2) 'Prepares' + // Field (3) 'Prepares' size += len(r.Prepares) * 56 - // Field (3) 'Commits' + // Field (4) 'Commits' size += len(r.Commits) * 56 + // Field (5) 'RoundChanges' + for ii := 0; ii < len(r.RoundChanges); ii++ { + size += 4 + size += r.RoundChanges[ii].SizeSSZ() + } + return } @@ -747,17 +915,20 @@ func (r *RoundTrace) HashTreeRoot() ([32]byte, error) { func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'ProposalRoot' + // Field (0) 'Proposer' + hh.PutUint64(uint64(r.Proposer)) + + // Field (1) 'ProposalRoot' hh.PutBytes(r.ProposalRoot[:]) - // Field (1) 'ProposalReceived' - hh.PutUint64(r.ProposalReceived) + // Field (2) 'ProposalReceivedTime' + hh.PutUint64(r.ProposalReceivedTime) - // Field (2) 'Prepares' + // Field (3) 'Prepares' { subIndx := hh.Index() num := uint64(len(r.Prepares)) - if num > 4 { + if num > 13 { err = ssz.ErrIncorrectListSize return } @@ -766,14 +937,14 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (3) 'Commits' + // Field (4) 'Commits' { subIndx := hh.Index() num := uint64(len(r.Commits)) - if num > 4 { + if num > 13 { err = ssz.ErrIncorrectListSize return } @@ -782,7 +953,23 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 4) + hh.MerkleizeWithMixin(subIndx, num, 13) + } + + // Field (5) 'RoundChanges' + { + subIndx := hh.Index() + num := uint64(len(r.RoundChanges)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.RoundChanges { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) } hh.Merkleize(indx) @@ -794,6 +981,154 @@ func (r *RoundTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(r) } +// MarshalSSZ ssz marshals the RoundChangeTrace object +func (r *RoundChangeTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(r) +} + +// MarshalSSZTo ssz marshals the RoundChangeTrace object to a target array +func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(60) + + // Field (0) 'BeaconRoot' + dst = append(dst, r.BeaconRoot[:]...) + + // Field (1) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(r.Signer)) + + // Field (2) 'Validator' + dst = ssz.MarshalUint64(dst, uint64(r.Validator)) + + // Field (3) 'ReceivedTime' + dst = ssz.MarshalUint64(dst, r.ReceivedTime) + + // Offset (4) 'PrepareMessages' + dst = ssz.WriteOffset(dst, offset) + offset += len(r.PrepareMessages) * 56 + + // Field (4) 'PrepareMessages' + if size := len(r.PrepareMessages); size > 32 { + err = ssz.ErrListTooBigFn("RoundChangeTrace.PrepareMessages", size, 32) + return + } + for ii := 0; ii < len(r.PrepareMessages); ii++ { + if dst, err = r.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the RoundChangeTrace object +func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 60 { + return ssz.ErrSize + } + + tail := buf + var o4 uint64 + + // Field (0) 'BeaconRoot' + copy(r.BeaconRoot[:], buf[0:32]) + + // Field (1) 'Signer' + r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[32:40])) + + // Field (2) 'Validator' + r.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'ReceivedTime' + r.ReceivedTime = ssz.UnmarshallUint64(buf[48:56]) + + // Offset (4) 'PrepareMessages' + if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + return ssz.ErrOffset + } + + if o4 < 60 { + return ssz.ErrInvalidVariableOffset + } + + // Field (4) 'PrepareMessages' + { + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 56, 32) + if err != nil { + return err + } + r.PrepareMessages = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if r.PrepareMessages[ii] == nil { + r.PrepareMessages[ii] = new(MessageTrace) + } + if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the RoundChangeTrace object +func (r *RoundChangeTrace) SizeSSZ() (size int) { + size = 60 + + // Field (4) 'PrepareMessages' + size += len(r.PrepareMessages) * 56 + + return +} + +// HashTreeRoot ssz hashes the RoundChangeTrace object +func (r *RoundChangeTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(r) +} + +// HashTreeRootWith ssz hashes the RoundChangeTrace object with a hasher +func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'BeaconRoot' + hh.PutBytes(r.BeaconRoot[:]) + + // Field (1) 'Signer' + hh.PutUint64(uint64(r.Signer)) + + // Field (2) 'Validator' + hh.PutUint64(uint64(r.Validator)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(r.ReceivedTime) + + // Field (4) 'PrepareMessages' + { + subIndx := hh.Index() + num := uint64(len(r.PrepareMessages)) + if num > 32 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.PrepareMessages { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 32) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the RoundChangeTrace object +func (r *RoundChangeTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(r) +} + // MarshalSSZ ssz marshals the MessageTrace object func (m *MessageTrace) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(m) @@ -812,8 +1147,8 @@ func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (2) 'Validator' dst = ssz.MarshalUint64(dst, uint64(m.Validator)) - // Field (3) 'Received' - dst = ssz.MarshalUint64(dst, m.Received) + // Field (3) 'ReceivedTime' + dst = ssz.MarshalUint64(dst, m.ReceivedTime) return } @@ -835,8 +1170,8 @@ func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { // Field (2) 'Validator' m.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) - // Field (3) 'Received' - m.Received = ssz.UnmarshallUint64(buf[48:56]) + // Field (3) 'ReceivedTime' + m.ReceivedTime = ssz.UnmarshallUint64(buf[48:56]) return err } @@ -865,8 +1200,8 @@ func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { // Field (2) 'Validator' hh.PutUint64(uint64(m.Validator)) - // Field (3) 'Received' - hh.PutUint64(m.Received) + // Field (3) 'ReceivedTime' + hh.PutUint64(m.ReceivedTime) hh.Merkleize(indx) return diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go index 077235ea3d..a20b829336 100644 --- a/exporter/v2/store/store_test.go +++ b/exporter/v2/store/store_test.go @@ -72,10 +72,7 @@ func TestSaveValidatorDutyTrace(t *testing.T) { func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { return &model.ValidatorDutyTrace{ DutyTrace: model.DutyTrace{ - Slot: slot, - Pre: nil, - Rounds: nil, - Post: nil, + Slot: slot, }, Role: types.BNRoleAttester, Validator: phase0.ValidatorIndex(39393), @@ -85,10 +82,7 @@ func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { func makeCTrace(slot phase0.Slot) *model.CommitteeDutyTrace { return &model.CommitteeDutyTrace{ DutyTrace: model.DutyTrace{ - Slot: slot, - Pre: nil, - Rounds: nil, - Post: nil, + Slot: slot, }, CommitteeID: [32]byte{'a'}, OperatorIDs: nil, From de40dfa90d997db28a598b0973419a0575134155 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 28 Jan 2025 11:19:25 +0000 Subject: [PATCH 05/24] json --- api/handlers/exporter.go | 46 ++------ api/handlers/model.go | 120 +++++++++++++++++++ eth/executionclient/multi_client.go | 2 +- exporter/v2/model.go | 10 +- exporter/v2/model_encoding.go | 172 ++++++++++++++++------------ 5 files changed, 233 insertions(+), 117 deletions(-) create mode 100644 api/handlers/model.go diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index 8ea92d5a61..2988569f67 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -101,26 +101,6 @@ func transformToParticipantResponse(role spectypes.BeaconRole, entry qbftstorage return response } -type committeeTraceResponse struct { - Data []committeeTrace `json:"data"` -} - -type committeeTrace struct { - Slot phase0.Slot `json:"slot"` -} - -func toCommitteeTrace(*model.CommitteeDutyTrace) committeeTrace { - return committeeTrace{} -} - -func toCommitteeTraceResponse(duties []*model.CommitteeDutyTrace) *committeeTraceResponse { - r := &committeeTraceResponse{} - for _, t := range duties { - r.Data = append(r.Data, toCommitteeTrace(t)) - } - return r -} - func (e *Exporter) OperatorTraces(w http.ResponseWriter, r *http.Request) error { var request struct { From uint64 `json:"from"` @@ -185,22 +165,10 @@ func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error return api.Render(w, r, toCommitteeTraceResponse(duties)) } -type validatorTraceResponse struct { - Data []validatorTrace `json:"data"` -} - -type validatorTrace struct { - Slot phase0.Slot `json:"slot"` -} - -func toValidatorTrace(*model.ValidatorDutyTrace) validatorTrace { - return validatorTrace{} -} - -func toValidatorTraceResponse(duties []*model.ValidatorDutyTrace) *validatorTraceResponse { - r := &validatorTraceResponse{} +func toCommitteeTraceResponse(duties []*model.CommitteeDutyTrace) *committeeTraceResponse { + r := new(committeeTraceResponse) for _, t := range duties { - r.Data = append(r.Data, toValidatorTrace(t)) + r.Data = append(r.Data, toCommitteeTrace(t)) } return r } @@ -258,3 +226,11 @@ func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error return api.Render(w, r, toValidatorTraceResponse(results)) } + +func toValidatorTraceResponse(duties []*model.ValidatorDutyTrace) *validatorTraceResponse { + r := new(validatorTraceResponse) + for _, t := range duties { + r.Data = append(r.Data, toValidatorTrace(t)) + } + return r +} diff --git a/api/handlers/model.go b/api/handlers/model.go new file mode 100644 index 0000000000..023bea5223 --- /dev/null +++ b/api/handlers/model.go @@ -0,0 +1,120 @@ +package handlers + +import ( + "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" + model "github.com/ssvlabs/ssv/exporter/v2" +) + +type validatorTraceResponse struct { + Data []validatorTrace `json:"data"` +} + +type validatorTrace struct { + Slot phase0.Slot `json:"slot"` + Rounds []round + Pre []message `json:"pre"` + Post []message `json:"post"` + Role spectypes.BeaconRole `json:"role"` + Validator phase0.ValidatorIndex `json:"validator"` +} + +type round struct { + Proposer spectypes.OperatorID `json:"proposer"` + ProposalRoot phase0.Root `json:"proposalRoot"` + ProposalReceivedTime uint64 `json:"time"` + Prepares []message `json:"prepares"` + Commits []message `json:"commits"` + RoundChanges []roundChange `json:"roundChanges"` +} + +type roundChange struct { + message + PreparedRound uint8 `json:"preparedRound"` + PrepareMessages []message `json:"prepareMessages"` +} + +type message struct { + Round uint8 `json:"round"` + BeaconRoot phase0.Root `json:"beaconRoot"` + Signer spectypes.OperatorID `json:"signer"` + ReceivedTime uint64 `json:"time"` +} + +func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { + return validatorTrace{ + Slot: t.Slot, + Role: t.Role, + Validator: t.Validator, + Pre: toMessageTrace(t.Pre), + Post: toMessageTrace(t.Post), + Rounds: toRoundTrace(t.Rounds), + } +} + +func toMessageTrace(m []*model.MessageTrace) (out []message) { + for _, mt := range m { + out = append(out, message{ + Round: mt.Round, + BeaconRoot: mt.BeaconRoot, + Signer: mt.Signer, + ReceivedTime: mt.ReceivedTime, + }) + } + return +} + +func toRoundTrace(r []*model.RoundTrace) (out []round) { + for _, rt := range r { + out = append(out, round{ + Proposer: rt.Proposer, + ProposalRoot: rt.ProposalRoot, + ProposalReceivedTime: rt.ProposalReceivedTime, + }) + } + return +} + +// committee + +type committeeTraceResponse struct { + Data []committeeTrace `json:"data"` +} + +type committeeTrace struct { + Slot phase0.Slot `json:"slot"` + Rounds []round `json:"rounds"` + Post []committeeMessage `json:"post"` + + CommitteeID spectypes.CommitteeID `json:"committeeID"` + OperatorIDs []spectypes.OperatorID `json:"operatorIDs"` +} + +type committeeMessage struct { + BeaconRoot []phase0.Root `json:"beaconRoot"` + Validators []phase0.ValidatorIndex `json:"validators"` + Signer spectypes.OperatorID `json:"signer"` + ReceivedTime uint64 `json:"time"` +} + +func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { + return committeeTrace{ + Slot: t.Slot, + Rounds: toRoundTrace(t.Rounds), + Post: toCommitteeMessageTrace(t.Post), + CommitteeID: t.CommitteeID, + OperatorIDs: t.OperatorIDs, + } +} + +func toCommitteeMessageTrace(m []*model.CommitteeMessageTrace) (out []committeeMessage) { + for _, mt := range m { + out = append(out, committeeMessage{ + BeaconRoot: mt.BeaconRoot, + Validators: mt.Validators, + Signer: mt.Signer, + ReceivedTime: mt.ReceivedTime, + }) + } + return +} diff --git a/eth/executionclient/multi_client.go b/eth/executionclient/multi_client.go index 2f29cfc8bf..84ac6047f8 100644 --- a/eth/executionclient/multi_client.go +++ b/eth/executionclient/multi_client.go @@ -2,12 +2,12 @@ package executionclient import ( "context" + "errors" "fmt" "math/big" "sync/atomic" "time" - "github.com/cockroachdb/errors" "github.com/ethereum/go-ethereum" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 21238a3412..fe1ce21ee2 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -5,7 +5,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" ) -//go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path . --objs ValidatorDutyTrace,CommitteeDutyTrace +//go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path model.go --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { DutyTrace Pre []*MessageTrace `ssz-max:"13"` @@ -51,13 +51,13 @@ type RoundTrace struct { type RoundChangeTrace struct { MessageTrace - preparedRound uint8 - PrepareMessages []*MessageTrace `ssz-max:"32"` + PreparedRound uint8 + PrepareMessages []*MessageTrace `ssz-max:"13"` } type MessageTrace struct { + Round uint8 BeaconRoot phase0.Root `ssz-size:"32"` Signer spectypes.OperatorID - Validator phase0.ValidatorIndex // Only present in CommitteeDutyTrace.Post -> remove - ReceivedTime uint64 // TODO fix time + ReceivedTime uint64 // TODO fix time } diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index a1c41850be..bf7f62f11b 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: a0dae63eb54e425273196828e076eb25245229bfc7107a06ec3d480951ea7349 +// Hash: 8b96e2ad90ff76b0dc3ac251b6b58d7c99f2e39cddbdae053c453e97728ccef5 // Version: 0.1.3 package exporter @@ -31,11 +31,11 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Offset (2) 'Pre' dst = ssz.WriteOffset(dst, offset) - offset += len(v.Pre) * 56 + offset += len(v.Pre) * 49 // Offset (3) 'Post' dst = ssz.WriteOffset(dst, offset) - offset += len(v.Post) * 56 + offset += len(v.Post) * 49 // Field (4) 'Role' dst = ssz.MarshalUint64(dst, uint64(v.Role)) @@ -150,7 +150,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // Field (2) 'Pre' { buf = tail[o2:o3] - num, err := ssz.DivideInt2(len(buf), 56, 13) + num, err := ssz.DivideInt2(len(buf), 49, 13) if err != nil { return err } @@ -159,7 +159,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if v.Pre[ii] == nil { v.Pre[ii] = new(MessageTrace) } - if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = v.Pre[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { return err } } @@ -168,7 +168,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // Field (3) 'Post' { buf = tail[o3:] - num, err := ssz.DivideInt2(len(buf), 56, 13) + num, err := ssz.DivideInt2(len(buf), 49, 13) if err != nil { return err } @@ -177,7 +177,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if v.Post[ii] == nil { v.Post[ii] = new(MessageTrace) } - if err = v.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = v.Post[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { return err } } @@ -196,10 +196,10 @@ func (v *ValidatorDutyTrace) SizeSSZ() (size int) { } // Field (2) 'Pre' - size += len(v.Pre) * 56 + size += len(v.Pre) * 49 // Field (3) 'Post' - size += len(v.Post) * 56 + size += len(v.Post) * 49 return } @@ -440,6 +440,9 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { } c.Post = make([]*CommitteeMessageTrace, num) err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Post[indx] == nil { + c.Post[indx] = new(CommitteeMessageTrace) + } if err = c.Post[indx].UnmarshalSSZ(buf); err != nil { return err } @@ -644,11 +647,16 @@ func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - c.Validators = make([]phase0.ValidatorIndex, num) + tempValidators := make([]uint64, num) for ii := 0; ii < num; ii++ { - c.Validators[ii] = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + tempValidators[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + } + c.Validators = make([]phase0.ValidatorIndex, num) + for ii, val := range tempValidators { + c.Validators[ii] = phase0.ValidatorIndex(val) } } + return err } @@ -735,11 +743,11 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Offset (3) 'Prepares' dst = ssz.WriteOffset(dst, offset) - offset += len(r.Prepares) * 56 + offset += len(r.Prepares) * 49 // Offset (4) 'Commits' dst = ssz.WriteOffset(dst, offset) - offset += len(r.Commits) * 56 + offset += len(r.Commits) * 49 // Offset (5) 'RoundChanges' dst = ssz.WriteOffset(dst, offset) @@ -833,7 +841,7 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { // Field (3) 'Prepares' { buf = tail[o3:o4] - num, err := ssz.DivideInt2(len(buf), 56, 13) + num, err := ssz.DivideInt2(len(buf), 49, 13) if err != nil { return err } @@ -842,7 +850,7 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if r.Prepares[ii] == nil { r.Prepares[ii] = new(MessageTrace) } - if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { return err } } @@ -851,7 +859,7 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { // Field (4) 'Commits' { buf = tail[o4:o5] - num, err := ssz.DivideInt2(len(buf), 56, 13) + num, err := ssz.DivideInt2(len(buf), 49, 13) if err != nil { return err } @@ -860,7 +868,7 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if r.Commits[ii] == nil { r.Commits[ii] = new(MessageTrace) } - if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = r.Commits[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { return err } } @@ -875,6 +883,9 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { } r.RoundChanges = make([]*RoundChangeTrace, num) err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if r.RoundChanges[indx] == nil { + r.RoundChanges[indx] = new(RoundChangeTrace) + } if err = r.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { return err } @@ -892,10 +903,10 @@ func (r *RoundTrace) SizeSSZ() (size int) { size = 60 // Field (3) 'Prepares' - size += len(r.Prepares) * 56 + size += len(r.Prepares) * 49 // Field (4) 'Commits' - size += len(r.Commits) * 56 + size += len(r.Commits) * 49 // Field (5) 'RoundChanges' for ii := 0; ii < len(r.RoundChanges); ii++ { @@ -989,27 +1000,30 @@ func (r *RoundChangeTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the RoundChangeTrace object to a target array func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(60) + offset := int(54) - // Field (0) 'BeaconRoot' + // Field (0) 'Round' + dst = ssz.MarshalUint8(dst, r.Round) + + // Field (1) 'BeaconRoot' dst = append(dst, r.BeaconRoot[:]...) - // Field (1) 'Signer' + // Field (2) 'Signer' dst = ssz.MarshalUint64(dst, uint64(r.Signer)) - // Field (2) 'Validator' - dst = ssz.MarshalUint64(dst, uint64(r.Validator)) - // Field (3) 'ReceivedTime' dst = ssz.MarshalUint64(dst, r.ReceivedTime) - // Offset (4) 'PrepareMessages' + // Field (4) 'PreparedRound' + dst = ssz.MarshalUint8(dst, r.PreparedRound) + + // Offset (5) 'PrepareMessages' dst = ssz.WriteOffset(dst, offset) - offset += len(r.PrepareMessages) * 56 + offset += len(r.PrepareMessages) * 49 - // Field (4) 'PrepareMessages' - if size := len(r.PrepareMessages); size > 32 { - err = ssz.ErrListTooBigFn("RoundChangeTrace.PrepareMessages", size, 32) + // Field (5) 'PrepareMessages' + if size := len(r.PrepareMessages); size > 13 { + err = ssz.ErrListTooBigFn("RoundChangeTrace.PrepareMessages", size, 13) return } for ii := 0; ii < len(r.PrepareMessages); ii++ { @@ -1025,38 +1039,41 @@ func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 60 { + if size < 54 { return ssz.ErrSize } tail := buf - var o4 uint64 + var o5 uint64 - // Field (0) 'BeaconRoot' - copy(r.BeaconRoot[:], buf[0:32]) + // Field (0) 'Round' + r.Round = ssz.UnmarshallUint8(buf[0:1]) - // Field (1) 'Signer' - r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[32:40])) + // Field (1) 'BeaconRoot' + copy(r.BeaconRoot[:], buf[1:33]) - // Field (2) 'Validator' - r.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) + // Field (2) 'Signer' + r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[33:41])) // Field (3) 'ReceivedTime' - r.ReceivedTime = ssz.UnmarshallUint64(buf[48:56]) + r.ReceivedTime = ssz.UnmarshallUint64(buf[41:49]) - // Offset (4) 'PrepareMessages' - if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + // Field (4) 'PreparedRound' + r.PreparedRound = ssz.UnmarshallUint8(buf[49:50]) + + // Offset (5) 'PrepareMessages' + if o5 = ssz.ReadOffset(buf[50:54]); o5 > size { return ssz.ErrOffset } - if o4 < 60 { + if o5 < 54 { return ssz.ErrInvalidVariableOffset } - // Field (4) 'PrepareMessages' + // Field (5) 'PrepareMessages' { - buf = tail[o4:] - num, err := ssz.DivideInt2(len(buf), 56, 32) + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 49, 13) if err != nil { return err } @@ -1065,7 +1082,7 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { if r.PrepareMessages[ii] == nil { r.PrepareMessages[ii] = new(MessageTrace) } - if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { return err } } @@ -1075,10 +1092,10 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the RoundChangeTrace object func (r *RoundChangeTrace) SizeSSZ() (size int) { - size = 60 + size = 54 - // Field (4) 'PrepareMessages' - size += len(r.PrepareMessages) * 56 + // Field (5) 'PrepareMessages' + size += len(r.PrepareMessages) * 49 return } @@ -1092,23 +1109,26 @@ func (r *RoundChangeTrace) HashTreeRoot() ([32]byte, error) { func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'BeaconRoot' + // Field (0) 'Round' + hh.PutUint8(r.Round) + + // Field (1) 'BeaconRoot' hh.PutBytes(r.BeaconRoot[:]) - // Field (1) 'Signer' + // Field (2) 'Signer' hh.PutUint64(uint64(r.Signer)) - // Field (2) 'Validator' - hh.PutUint64(uint64(r.Validator)) - // Field (3) 'ReceivedTime' hh.PutUint64(r.ReceivedTime) - // Field (4) 'PrepareMessages' + // Field (4) 'PreparedRound' + hh.PutUint8(r.PreparedRound) + + // Field (5) 'PrepareMessages' { subIndx := hh.Index() num := uint64(len(r.PrepareMessages)) - if num > 32 { + if num > 13 { err = ssz.ErrIncorrectListSize return } @@ -1117,7 +1137,7 @@ func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { return } } - hh.MerkleizeWithMixin(subIndx, num, 32) + hh.MerkleizeWithMixin(subIndx, num, 13) } hh.Merkleize(indx) @@ -1138,15 +1158,15 @@ func (m *MessageTrace) MarshalSSZ() ([]byte, error) { func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - // Field (0) 'BeaconRoot' + // Field (0) 'Round' + dst = ssz.MarshalUint8(dst, m.Round) + + // Field (1) 'BeaconRoot' dst = append(dst, m.BeaconRoot[:]...) - // Field (1) 'Signer' + // Field (2) 'Signer' dst = ssz.MarshalUint64(dst, uint64(m.Signer)) - // Field (2) 'Validator' - dst = ssz.MarshalUint64(dst, uint64(m.Validator)) - // Field (3) 'ReceivedTime' dst = ssz.MarshalUint64(dst, m.ReceivedTime) @@ -1157,28 +1177,28 @@ func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size != 56 { + if size != 49 { return ssz.ErrSize } - // Field (0) 'BeaconRoot' - copy(m.BeaconRoot[:], buf[0:32]) + // Field (0) 'Round' + m.Round = ssz.UnmarshallUint8(buf[0:1]) - // Field (1) 'Signer' - m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[32:40])) + // Field (1) 'BeaconRoot' + copy(m.BeaconRoot[:], buf[1:33]) - // Field (2) 'Validator' - m.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) + // Field (2) 'Signer' + m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[33:41])) // Field (3) 'ReceivedTime' - m.ReceivedTime = ssz.UnmarshallUint64(buf[48:56]) + m.ReceivedTime = ssz.UnmarshallUint64(buf[41:49]) return err } // SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object func (m *MessageTrace) SizeSSZ() (size int) { - size = 56 + size = 49 return } @@ -1191,15 +1211,15 @@ func (m *MessageTrace) HashTreeRoot() ([32]byte, error) { func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'BeaconRoot' + // Field (0) 'Round' + hh.PutUint8(m.Round) + + // Field (1) 'BeaconRoot' hh.PutBytes(m.BeaconRoot[:]) - // Field (1) 'Signer' + // Field (2) 'Signer' hh.PutUint64(uint64(m.Signer)) - // Field (2) 'Validator' - hh.PutUint64(uint64(m.Validator)) - // Field (3) 'ReceivedTime' hh.PutUint64(m.ReceivedTime) From 006b114b2899d61cf50ebc98148370cc0e85c806 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Wed, 29 Jan 2025 16:09:06 +0000 Subject: [PATCH 06/24] lint --- api/handlers/exporter.go | 4 +--- eth/executionclient/execution_client.go | 2 +- exporter/v2/model_encoding.go | 2 +- exporter/v2/store/store.go | 2 +- exporter/v2/store/store_test.go | 2 +- network/peers/connections/handshaker.go | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index 2988569f67..85f3fbdc4c 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -113,9 +113,7 @@ func (e *Exporter) OperatorTraces(w http.ResponseWriter, r *http.Request) error } var indexes []spectypes.OperatorID - for _, id := range request.OperatorID { - indexes = append(indexes, spectypes.OperatorID(id)) - } + indexes = append(indexes, request.OperatorID...) var duties []*model.CommitteeDutyTrace for s := request.From; s <= request.To; s++ { diff --git a/eth/executionclient/execution_client.go b/eth/executionclient/execution_client.go index 4783a41a62..5b2b2a9437 100644 --- a/eth/executionclient/execution_client.go +++ b/eth/executionclient/execution_client.go @@ -184,7 +184,7 @@ func (ec *ExecutionClient) fetchLogsInBatches(ctx context.Context, startBlock, e return } - ec.logger.Info("fetched registry events", + ec.logger.Debug("fetched registry events", fields.FromBlock(fromBlock), fields.ToBlock(toBlock), zap.Uint64("target_block", endBlock), diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index bf7f62f11b..25f04f8f39 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -656,7 +656,7 @@ func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { c.Validators[ii] = phase0.ValidatorIndex(val) } } - + return err } diff --git a/exporter/v2/store/store.go b/exporter/v2/store/store.go index 3596526774..7a2e64f729 100644 --- a/exporter/v2/store/store.go +++ b/exporter/v2/store/store.go @@ -192,7 +192,7 @@ func (s *DutyTraceStore) makeCommiteeOperatorPrefixes(ii []spectypes.OperatorID, prefix := make([]byte, 0, len(commiteeOperatorIndexKey)+4+32) prefix = append(prefix, []byte(commiteeOperatorIndexKey)...) prefix = append(prefix, slotToByteSlice(slot)...) - prefix = append(prefix, uInt64ToByteSlice(uint64(index))...) + prefix = append(prefix, uInt64ToByteSlice(index)...) keys = append(keys, prefix) } diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go index a20b829336..4785a33ce7 100644 --- a/exporter/v2/store/store_test.go +++ b/exporter/v2/store/store_test.go @@ -57,7 +57,7 @@ func TestSaveValidatorDutyTrace(t *testing.T) { require.Equal(t, phase0.Slot(2), trace.Slot) require.Equal(t, phase0.ValidatorIndex(39393), trace.Validator) - trace, err = store.GetValidatorDuty(types.BNRoleAttester, phase0.Slot(3), phase0.ValidatorIndex(39393)) + _, err = store.GetValidatorDuty(types.BNRoleAttester, phase0.Slot(3), phase0.ValidatorIndex(39393)) require.Error(t, err) traces, err := store.GetAllValidatorDuties(types.BNRoleAttester, phase0.Slot(1)) diff --git a/network/peers/connections/handshaker.go b/network/peers/connections/handshaker.go index 26b94051db..a7eabceae8 100644 --- a/network/peers/connections/handshaker.go +++ b/network/peers/connections/handshaker.go @@ -151,7 +151,7 @@ func (h *handshaker) verifyTheirNodeInfo(logger *zap.Logger, sender peer.ID, ni h.nodeInfos.SetNodeInfo(sender, ni.GetNodeInfo()) - logger.Info("Verified handshake nodeinfo", + logger.Debug("Verified handshake nodeinfo", fields.PeerID(sender), zap.Any("metadata", ni.GetNodeInfo().Metadata), zap.String("networkID", ni.GetNodeInfo().NetworkID), From 38acd95a845bcb52708efac3629a40ec2035ac53 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Wed, 29 Jan 2025 16:09:15 +0000 Subject: [PATCH 07/24] in mem --- operator/validator/controller.go | 17 +++- operator/validator/dutytracer.go | 152 +++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 operator/validator/dutytracer.go diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 0b088c9ad9..67337d4994 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -14,6 +14,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jellydator/ttlcache/v3" "github.com/pkg/errors" + "go.uber.org/zap" + specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/ibft/storage" @@ -39,11 +41,9 @@ import ( "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" "github.com/ssvlabs/ssv/protocol/v2/ssv/runner" "github.com/ssvlabs/ssv/protocol/v2/ssv/validator" - "github.com/ssvlabs/ssv/protocol/v2/types" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" registrystorage "github.com/ssvlabs/ssv/registry/storage" "github.com/ssvlabs/ssv/storage/basedb" - "go.uber.org/zap" ) //go:generate mockgen -package=mocks -destination=./mocks/controller.go -source=./controller.go @@ -185,6 +185,12 @@ type controller struct { recentlyStartedValidators uint64 indicesChange chan struct{} validatorExitCh chan duties.ExitDescriptor + + tracer DutyTracer +} + +type DutyTracer interface { + Trace(*queue.SSVMessage) } // NewController creates a new validator controller instance @@ -229,7 +235,10 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { beaconNetwork := options.NetworkConfig.Beacon cacheTTL := beaconNetwork.SlotDurationSec() * time.Duration(beaconNetwork.SlotsPerEpoch()*2) // #nosec G115 + inMem := NewTracer(logger) + ctrl := controller{ + tracer: inMem, logger: logger.Named(logging.NameController), networkConfig: options.NetworkConfig, sharesStorage: options.RegistryStorage.Shares(), @@ -362,6 +371,8 @@ func (c *controller) handleWorkerMessages(msg network.DecodedSSVMessage) error { var ncv *committeeObserver ssvMsg := msg.(*queue.SSVMessage) + c.tracer.Trace(ssvMsg) + item := c.getNonCommitteeValidators(ssvMsg.GetID()) if item == nil { committeeObserverOptions := validator.CommitteeObserverOptions{ @@ -774,7 +785,7 @@ func (c *controller) onShareInit(share *ssvtypes.SSVShare) (*validator.Validator opts.SSVShare = share opts.Operator = operator - committeeOpIDs := types.OperatorIDsFromOperators(operator.Committee) + committeeOpIDs := ssvtypes.OperatorIDsFromOperators(operator.Committee) logger := c.logger.With([]zap.Field{ zap.String("committee", fields.FormatCommittee(committeeOpIDs)), diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go new file mode 100644 index 0000000000..4abd23e55e --- /dev/null +++ b/operator/validator/dutytracer.go @@ -0,0 +1,152 @@ +package validator + +import ( + "encoding/hex" + "sync" + + "github.com/attestantio/go-eth2-client/spec/phase0" + "go.uber.org/zap" + + specqbft "github.com/ssvlabs/ssv-spec/qbft" + spectypes "github.com/ssvlabs/ssv-spec/types" + model "github.com/ssvlabs/ssv/exporter/v2" + "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" +) + +type InMemTracer struct { + logger *zap.Logger + validatorTraces map[uint64]*model.ValidatorDutyTrace + mu sync.Mutex +} + +func NewTracer(logger *zap.Logger) *InMemTracer { + return &InMemTracer{ + logger: logger, + validatorTraces: make(map[uint64]*model.ValidatorDutyTrace), + } +} + +func (n *InMemTracer) qbft(msg *specqbft.Message) { + slot := uint64(msg.Height) + + fields := []zap.Field{ + fields.Slot(phase0.Slot(slot)), + fields.Round(msg.Round), + zap.String("identifier", hex.EncodeToString(msg.Identifier[len(msg.Identifier)-5:])), + zap.String("root", hex.EncodeToString(msg.Root[len(msg.Root)-5:])), + } + + n.mu.Lock() + trace, ok := n.validatorTraces[slot] + if !ok { + trace = new(model.ValidatorDutyTrace) + n.validatorTraces[slot] = trace + fields = append(fields, zap.String("new Trace", "true")) + } + n.mu.Unlock() + + switch msg.MsgType { + case specqbft.ProposalMsgType: + fields = append(fields, zap.String("messageType", "proposal")) + case specqbft.PrepareMsgType: + fields = append(fields, zap.String("messageType", "prepare")) + case specqbft.CommitMsgType: + fields = append(fields, zap.String("messageType", "commit")) + case specqbft.RoundChangeMsgType: + fields = append(fields, zap.String("messageType", "round change")) + } + + var lastRound *model.RoundTrace + + currentRound := specqbft.Round(len(trace.Rounds)) + if currentRound == 0 || currentRound > msg.Round { + lastRound = &model.RoundTrace{} + trace.Rounds = append(trace.Rounds, lastRound) + fields = append(fields, zap.String("new Round", "true")) + } else { + lastRound = trace.Rounds[len(trace.Rounds)-1] + } + + fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) + + lastRound.Proposer = 0 //fixme + lastRound.ProposalRoot = msg.Root + // lastRound.ProposalReceivedTime = uint64(time.Now().Unix()) + + n.logger.Info("qbft", fields...) + + _ = msg.DataRound + _ = msg.RoundChangeJustification + _ = msg.PrepareJustification + + // trace the message + // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) +} + +func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages) { + slot := uint64(msg.Slot) + + fields := []zap.Field{ + fields.Slot(phase0.Slot(slot)), + } + + n.mu.Lock() + trace, ok := n.validatorTraces[slot] + if !ok { + trace = new(model.ValidatorDutyTrace) + n.validatorTraces[slot] = trace + fields = append(fields, zap.String("new Trace", "true")) + } + n.mu.Unlock() + + fields = append(fields, zap.Int("messages", len(msg.Messages))) + fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) + + lastRound := trace.Rounds[len(trace.Rounds)-1] + for _, pSigMsg := range msg.Messages { + lastRound.Proposer = pSigMsg.Signer + lastRound.ProposalRoot = pSigMsg.SigningRoot + // lastRound.ProposalReceivedTime = uint64(time.Now().Unix()) + + _ = pSigMsg.ValidatorIndex + } + + switch msg.Type { + case spectypes.PostConsensusPartialSig: + fields = append(fields, zap.String("messageType", "post consensus")) + case spectypes.ContributionProofs: + fields = append(fields, zap.String("messageType", "contribution proofs")) + case spectypes.RandaoPartialSig: + fields = append(fields, zap.String("messageType", "randao")) + case spectypes.SelectionProofPartialSig: + fields = append(fields, zap.String("messageType", "selection proof")) + case spectypes.ValidatorRegistrationPartialSig: + fields = append(fields, zap.String("messageType", "validator registration")) + case spectypes.VoluntaryExitPartialSig: + fields = append(fields, zap.String("messageType", "voluntary exit")) + } + + n.logger.Info("signed", fields...) +} + +func (n *InMemTracer) Trace(msg *queue.SSVMessage) { + switch msg.MsgType { + case spectypes.SSVConsensusMsgType: + if subMsg, ok := msg.Body.(*specqbft.Message); ok { + n.qbft(subMsg) + } + case spectypes.SSVPartialSignatureMsgType: + pSigMessages := new(spectypes.PartialSignatureMessages) + signedMsg := msg.SignedSSVMessage + ssvMsg := signedMsg.SSVMessage + + _ = signedMsg.OperatorIDs + _ = signedMsg.Signatures + + err := pSigMessages.Decode(ssvMsg.GetData()) + if err == nil { + n.signed(pSigMessages) + } + } +} From eec9c904af5d0f1f33ba4a66cc61b60f3e3a6dcb Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Fri, 31 Jan 2025 16:30:06 +0000 Subject: [PATCH 08/24] wip decideds --- exporter/v2/model.go | 35 +++-- exporter/v2/store/store_test.go | 2 - operator/validator/dutytracer.go | 228 +++++++++++++++++++++++++------ 3 files changed, 210 insertions(+), 55 deletions(-) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index fe1ce21ee2..d9dbb4adc2 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -8,9 +8,10 @@ import ( //go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path model.go --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { DutyTrace - Pre []*MessageTrace `ssz-max:"13"` - Post []*MessageTrace `ssz-max:"13"` - Role spectypes.BeaconRole + Pre []*MessageTrace `ssz-max:"13"` + Post []*MessageTrace `ssz-max:"13"` + Role spectypes.BeaconRole + // this could be a pubkey Validator phase0.ValidatorIndex } @@ -35,18 +36,24 @@ type CommitteeMessageTrace struct { } type DutyTrace struct { - Slot phase0.Slot - Rounds []*RoundTrace `ssz-max:"15"` + Slot phase0.Slot + Rounds []*RoundTrace `ssz-max:"15"` + Decideds []*DecidedTrace `ssz-max:"256"` // TODO max +} + +type DecidedTrace struct { + MessageTrace + // Value []byte // full data needed? + Signers []spectypes.OperatorID } type RoundTrace struct { Proposer spectypes.OperatorID // can be computed or saved // ProposalData - ProposalRoot phase0.Root `ssz-size:"32"` - ProposalReceivedTime uint64 // TODO fix time - Prepares []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. - Commits []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. - RoundChanges []*RoundChangeTrace `ssz-max:"13"` + ProposalTrace *ProposalTrace + Prepares []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. + Commits []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. + RoundChanges []*RoundChangeTrace `ssz-max:"13"` } type RoundChangeTrace struct { @@ -55,8 +62,14 @@ type RoundChangeTrace struct { PrepareMessages []*MessageTrace `ssz-max:"13"` } +type ProposalTrace struct { + MessageTrace + RoundChanges []*RoundChangeTrace `ssz-max:"13"` + PrepareMessages []*MessageTrace `ssz-max:"13"` +} + type MessageTrace struct { - Round uint8 + Round uint8 // same for BeaconRoot phase0.Root `ssz-size:"32"` Signer spectypes.OperatorID ReceivedTime uint64 // TODO fix time diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go index 4785a33ce7..47a75cd1ae 100644 --- a/exporter/v2/store/store_test.go +++ b/exporter/v2/store/store_test.go @@ -23,8 +23,6 @@ func TestSaveCommitteeDutyTrace(t *testing.T) { trace1 := makeCTrace(1) trace2 := makeCTrace(2) - _, _ = trace1, trace2 - store := store.New(db) require.NoError(t, store.SaveCommiteeDuty(trace1)) require.NoError(t, store.SaveCommiteeDuty(trace2)) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 4abd23e55e..d99dfffb54 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -3,6 +3,7 @@ package validator import ( "encoding/hex" "sync" + "time" "github.com/attestantio/go-eth2-client/spec/phase0" "go.uber.org/zap" @@ -15,19 +16,142 @@ import ( ) type InMemTracer struct { - logger *zap.Logger - validatorTraces map[uint64]*model.ValidatorDutyTrace - mu sync.Mutex + sync.Mutex + logger *zap.Logger + // consider having the validator pubkey before of the slot + validatorTraces map[uint64]map[string]*model.ValidatorDutyTrace } func NewTracer(logger *zap.Logger) *InMemTracer { return &InMemTracer{ logger: logger, - validatorTraces: make(map[uint64]*model.ValidatorDutyTrace), + validatorTraces: make(map[uint64]map[string]*model.ValidatorDutyTrace), } } -func (n *InMemTracer) qbft(msg *specqbft.Message) { +func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *model.ValidatorDutyTrace { + mp, ok := n.validatorTraces[slot] + if !ok { + mp = make(map[string]*model.ValidatorDutyTrace) + n.validatorTraces[slot] = mp + } + + trace, ok := mp[vPubKey] + if !ok { + trace = new(model.ValidatorDutyTrace) + mp[vPubKey] = trace + } + + return trace +} + +func (n *InMemTracer) getRound(trace *model.ValidatorDutyTrace, round uint64) *model.RoundTrace { + var count = uint64(len(trace.Rounds)) + if count == 0 || count > round-1 { + var r model.RoundTrace + trace.Rounds = append(trace.Rounds, &r) + return &r + } + + return trace.Rounds[round-1] +} + +// id -> validator or committee id +func getOperators(id string) []spectypes.OperatorID { + return []spectypes.OperatorID{} +} + +func (n *InMemTracer) toMockState(msg *specqbft.Message, operatorIDs []spectypes.OperatorID) *specqbft.State { + var mockState = &specqbft.State{} + mockState.Height = specqbft.Height(msg.Height) + mockState.CommitteeMember = &spectypes.CommitteeMember{ + Committee: make([]*spectypes.Operator, 0, len(operatorIDs)), + } + for i := 0; i < len(operatorIDs); i++ { + mockState.CommitteeMember.Committee = append(mockState.CommitteeMember.Committee, &spectypes.Operator{ + OperatorID: operatorIDs[i], + }) + } + return mockState +} + +func decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTrace { + var traces = make([]*model.MessageTrace, 0, len(justifications)) + for _, rcj := range justifications { + var signedMsg = new(spectypes.SignedSSVMessage) + err := signedMsg.Decode(rcj) + if err != nil { + // n.logger.Error("failed to decode round change justification", zap.Error(err)) + } + + var qbftMsg = new(specqbft.Message) + err = qbftMsg.Decode(signedMsg.SSVMessage.GetData()) + if err != nil { + // n.logger.Error("failed to decode round change justification", zap.Error(err)) + } + + var justificationTrace = new(model.MessageTrace) + justificationTrace.Round = uint8(qbftMsg.Round) + justificationTrace.BeaconRoot = qbftMsg.Root + justificationTrace.Signer = signedMsg.OperatorIDs[0] + traces = append(traces, justificationTrace) + } + + return traces +} + +func decodeJustificationWithRoundChanges(justifications [][]byte) []*model.RoundChangeTrace { + var traces = make([]*model.RoundChangeTrace, 0, len(justifications)) + for _, rcj := range justifications { + + var signedMsg = new(spectypes.SignedSSVMessage) + err := signedMsg.Decode(rcj) + if err != nil { + // n.logger.Error("failed to decode round change justification", zap.Error(err)) + } + + var qbftMsg = new(specqbft.Message) + err = qbftMsg.Decode(signedMsg.SSVMessage.GetData()) + if err != nil { + // n.logger.Error("failed to decode round change justification", zap.Error(err)) + } + + receivedTime := uint64(0) // we can't know the time when the sender received the message + + var roundChangeTrace = createRoundChangeTrace(qbftMsg, signedMsg, receivedTime) + traces = append(traces, roundChangeTrace) + } + + return traces +} + +func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, receivedTime uint64) *model.RoundChangeTrace { + var roundChangeTrace = new(model.RoundChangeTrace) + roundChangeTrace.PreparedRound = uint8(msg.DataRound) + roundChangeTrace.Round = uint8(msg.Round) + roundChangeTrace.BeaconRoot = msg.Root + roundChangeTrace.Signer = signedMsg.OperatorIDs[0] + roundChangeTrace.ReceivedTime = receivedTime + + roundChangeTrace.PrepareMessages = decodeJustificationWithPrepares(msg.RoundChangeJustification) + + return roundChangeTrace +} + +func createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) *model.ProposalTrace { + var proposalTrace = new(model.ProposalTrace) + proposalTrace.Round = uint8(msg.Round) + proposalTrace.BeaconRoot = msg.Root + proposalTrace.Signer = signedMsg.OperatorIDs[0] + proposalTrace.ReceivedTime = uint64(time.Now().Unix()) // correct + + proposalTrace.RoundChanges = decodeJustificationWithRoundChanges(msg.RoundChangeJustification) + proposalTrace.PrepareMessages = decodeJustificationWithPrepares(msg.PrepareJustification) + + return proposalTrace +} + +func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { slot := uint64(msg.Height) fields := []zap.Field{ @@ -37,49 +161,73 @@ func (n *InMemTracer) qbft(msg *specqbft.Message) { zap.String("root", hex.EncodeToString(msg.Root[len(msg.Root)-5:])), } - n.mu.Lock() - trace, ok := n.validatorTraces[slot] - if !ok { - trace = new(model.ValidatorDutyTrace) - n.validatorTraces[slot] = trace - fields = append(fields, zap.String("new Trace", "true")) + // validator pubkey + msgID := spectypes.MessageID(msg.Identifier[:]) // validator + pubkey + role + network + validatorPubKey := msgID.GetDutyExecutorID() // validator pubkey or committee id + validatorID := string(validatorPubKey) + + // get or create trace + trace := n.getTrace(slot, validatorID) + + // first round is 1 + var round = n.getRound(trace, uint64(msg.Round)) + + { // proposer + operatorIDs := getOperators(validatorID) + var mockState = n.toMockState(msg, operatorIDs) + round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) } - n.mu.Unlock() switch msg.MsgType { case specqbft.ProposalMsgType: fields = append(fields, zap.String("messageType", "proposal")) + + var data = new(spectypes.ValidatorConsensusData) + err := data.Decode(signedMsg.FullData) + if err != nil { + n.logger.Error("failed to decode proposal data", zap.Error(err)) + } + // beacon vote (for committee duty) + + trace.Validator = data.Duty.ValidatorIndex + round.ProposalTrace = createProposalTrace(msg, signedMsg) + case specqbft.PrepareMsgType: fields = append(fields, zap.String("messageType", "prepare")) + + var m = new(model.MessageTrace) + m.Round = uint8(msg.Round) + m.BeaconRoot = msg.Root + m.Signer = signedMsg.OperatorIDs[0] + m.ReceivedTime = uint64(time.Now().Unix()) // TODO fixme + + round.Prepares = append(round.Prepares, m) + case specqbft.CommitMsgType: fields = append(fields, zap.String("messageType", "commit")) + + var m = new(model.MessageTrace) + m.Round = uint8(msg.Round) + m.BeaconRoot = msg.Root + m.Signer = signedMsg.OperatorIDs[0] + m.ReceivedTime = uint64(time.Now().Unix()) // TODO fixme + + round.Commits = append(round.Commits, m) + case specqbft.RoundChangeMsgType: fields = append(fields, zap.String("messageType", "round change")) - } + // optional - only if round change is proposing a value - var lastRound *model.RoundTrace + now := uint64(time.Now().Unix()) + roundChangeTrace := createRoundChangeTrace(msg, signedMsg, now) - currentRound := specqbft.Round(len(trace.Rounds)) - if currentRound == 0 || currentRound > msg.Round { - lastRound = &model.RoundTrace{} - trace.Rounds = append(trace.Rounds, lastRound) - fields = append(fields, zap.String("new Round", "true")) - } else { - lastRound = trace.Rounds[len(trace.Rounds)-1] + round.RoundChanges = append(round.RoundChanges, roundChangeTrace) } fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) - lastRound.Proposer = 0 //fixme - lastRound.ProposalRoot = msg.Root - // lastRound.ProposalReceivedTime = uint64(time.Now().Unix()) - n.logger.Info("qbft", fields...) - _ = msg.DataRound - _ = msg.RoundChangeJustification - _ = msg.PrepareJustification - // trace the message // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) } @@ -91,27 +239,23 @@ func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages) { fields.Slot(phase0.Slot(slot)), } - n.mu.Lock() - trace, ok := n.validatorTraces[slot] - if !ok { - trace = new(model.ValidatorDutyTrace) - n.validatorTraces[slot] = trace - fields = append(fields, zap.String("new Trace", "true")) - } - n.mu.Unlock() + validatorID := string("TODO") + + trace := n.getTrace(slot, validatorID) fields = append(fields, zap.Int("messages", len(msg.Messages))) fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) - lastRound := trace.Rounds[len(trace.Rounds)-1] + round := uint64(0) // TODO + lastRound := n.getRound(trace, round) + + // Q: how to map Message to RoundTrace? for _, pSigMsg := range msg.Messages { lastRound.Proposer = pSigMsg.Signer - lastRound.ProposalRoot = pSigMsg.SigningRoot - // lastRound.ProposalReceivedTime = uint64(time.Now().Unix()) - _ = pSigMsg.ValidatorIndex } + // Q: usage of msg.Type? switch msg.Type { case spectypes.PostConsensusPartialSig: fields = append(fields, zap.String("messageType", "post consensus")) @@ -134,7 +278,7 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { switch msg.MsgType { case spectypes.SSVConsensusMsgType: if subMsg, ok := msg.Body.(*specqbft.Message); ok { - n.qbft(subMsg) + n.qbft(subMsg, msg.SignedSSVMessage) } case spectypes.SSVPartialSignatureMsgType: pSigMessages := new(spectypes.PartialSignatureMessages) From dc241469e65cb6610345531d79f9f75dc72ac1cd Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Sat, 1 Feb 2025 10:19:12 +0000 Subject: [PATCH 09/24] wip bugfixes --- api/handlers/model.go | 14 +- exporter/v2/model.go | 12 +- exporter/v2/model_encoding.go | 853 +++++++++++++++++++++++++------ operator/validator/dutytracer.go | 58 +-- 4 files changed, 727 insertions(+), 210 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index 023bea5223..d154f44d63 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -1,6 +1,8 @@ package handlers import ( + "time" + "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/ssvlabs/ssv-spec/types" model "github.com/ssvlabs/ssv/exporter/v2" @@ -30,15 +32,15 @@ type round struct { type roundChange struct { message - PreparedRound uint8 `json:"preparedRound"` + PreparedRound uint64 `json:"preparedRound"` PrepareMessages []message `json:"prepareMessages"` } type message struct { - Round uint8 `json:"round"` + Round uint64 `json:"round"` BeaconRoot phase0.Root `json:"beaconRoot"` Signer spectypes.OperatorID `json:"signer"` - ReceivedTime uint64 `json:"time"` + ReceivedTime time.Time `json:"time"` } func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { @@ -67,9 +69,7 @@ func toMessageTrace(m []*model.MessageTrace) (out []message) { func toRoundTrace(r []*model.RoundTrace) (out []round) { for _, rt := range r { out = append(out, round{ - Proposer: rt.Proposer, - ProposalRoot: rt.ProposalRoot, - ProposalReceivedTime: rt.ProposalReceivedTime, + Proposer: rt.Proposer, }) } return @@ -94,7 +94,7 @@ type committeeMessage struct { BeaconRoot []phase0.Root `json:"beaconRoot"` Validators []phase0.ValidatorIndex `json:"validators"` Signer spectypes.OperatorID `json:"signer"` - ReceivedTime uint64 `json:"time"` + ReceivedTime time.Time `json:"time"` } func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { diff --git a/exporter/v2/model.go b/exporter/v2/model.go index d9dbb4adc2..c3d286f674 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -1,6 +1,8 @@ package exporter import ( + "time" + "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/ssvlabs/ssv-spec/types" ) @@ -32,7 +34,7 @@ type CommitteeMessageTrace struct { Validators []phase0.ValidatorIndex `ssz-max:"1500"` Signer spectypes.OperatorID - ReceivedTime uint64 // TODO fixme + ReceivedTime time.Time } type DutyTrace struct { @@ -44,7 +46,7 @@ type DutyTrace struct { type DecidedTrace struct { MessageTrace // Value []byte // full data needed? - Signers []spectypes.OperatorID + Signers []spectypes.OperatorID `ssz-max:"13"` } type RoundTrace struct { @@ -58,7 +60,7 @@ type RoundTrace struct { type RoundChangeTrace struct { MessageTrace - PreparedRound uint8 + PreparedRound uint64 PrepareMessages []*MessageTrace `ssz-max:"13"` } @@ -69,8 +71,8 @@ type ProposalTrace struct { } type MessageTrace struct { - Round uint8 // same for + Round uint64 // same for BeaconRoot phase0.Root `ssz-size:"32"` Signer spectypes.OperatorID - ReceivedTime uint64 // TODO fix time + ReceivedTime time.Time } diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 25f04f8f39..1f9d665297 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 8b96e2ad90ff76b0dc3ac251b6b58d7c99f2e39cddbdae053c453e97728ccef5 +// Hash: 6e19b9b279acb29d30d15b4029d06aa4f5657ccf263e0879c519bee66c5d38cc // Version: 0.1.3 package exporter @@ -17,7 +17,7 @@ func (v *ValidatorDutyTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the ValidatorDutyTrace object to a target array func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(36) + offset := int(40) // Field (0) 'Slot' dst = ssz.MarshalUint64(dst, uint64(v.Slot)) @@ -29,18 +29,25 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { offset += v.Rounds[ii].SizeSSZ() } - // Offset (2) 'Pre' + // Offset (2) 'Decideds' dst = ssz.WriteOffset(dst, offset) - offset += len(v.Pre) * 49 + for ii := 0; ii < len(v.Decideds); ii++ { + offset += 4 + offset += v.Decideds[ii].SizeSSZ() + } - // Offset (3) 'Post' + // Offset (3) 'Pre' dst = ssz.WriteOffset(dst, offset) - offset += len(v.Post) * 49 + offset += len(v.Pre) * 56 - // Field (4) 'Role' + // Offset (4) 'Post' + dst = ssz.WriteOffset(dst, offset) + offset += len(v.Post) * 56 + + // Field (5) 'Role' dst = ssz.MarshalUint64(dst, uint64(v.Role)) - // Field (5) 'Validator' + // Field (6) 'Validator' dst = ssz.MarshalUint64(dst, uint64(v.Validator)) // Field (1) 'Rounds' @@ -61,7 +68,25 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (2) 'Pre' + // Field (2) 'Decideds' + if size := len(v.Decideds); size > 256 { + err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Decideds", size, 256) + return + } + { + offset = 4 * len(v.Decideds) + for ii := 0; ii < len(v.Decideds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += v.Decideds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(v.Decideds); ii++ { + if dst, err = v.Decideds[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Pre' if size := len(v.Pre); size > 13 { err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Pre", size, 13) return @@ -72,7 +97,7 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (3) 'Post' + // Field (4) 'Post' if size := len(v.Post); size > 13 { err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Post", size, 13) return @@ -90,12 +115,12 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 36 { + if size < 40 { return ssz.ErrSize } tail := buf - var o1, o2, o3 uint64 + var o1, o2, o3, o4 uint64 // Field (0) 'Slot' v.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) @@ -105,25 +130,30 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { return ssz.ErrOffset } - if o1 < 36 { + if o1 < 40 { return ssz.ErrInvalidVariableOffset } - // Offset (2) 'Pre' + // Offset (2) 'Decideds' if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { return ssz.ErrOffset } - // Offset (3) 'Post' + // Offset (3) 'Pre' if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { return ssz.ErrOffset } - // Field (4) 'Role' - v.Role = spectypes.BeaconRole(ssz.UnmarshallUint64(buf[20:28])) + // Offset (4) 'Post' + if o4 = ssz.ReadOffset(buf[20:24]); o4 > size || o3 > o4 { + return ssz.ErrOffset + } + + // Field (5) 'Role' + v.Role = spectypes.BeaconRole(ssz.UnmarshallUint64(buf[24:32])) - // Field (5) 'Validator' - v.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[28:36])) + // Field (6) 'Validator' + v.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[32:40])) // Field (1) 'Rounds' { @@ -147,10 +177,32 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (2) 'Pre' + // Field (2) 'Decideds' { buf = tail[o2:o3] - num, err := ssz.DivideInt2(len(buf), 49, 13) + num, err := ssz.DecodeDynamicLength(buf, 256) + if err != nil { + return err + } + v.Decideds = make([]*DecidedTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if v.Decideds[indx] == nil { + v.Decideds[indx] = new(DecidedTrace) + } + if err = v.Decideds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (3) 'Pre' + { + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -159,16 +211,16 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if v.Pre[ii] == nil { v.Pre[ii] = new(MessageTrace) } - if err = v.Pre[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { + if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } } } - // Field (3) 'Post' + // Field (4) 'Post' { - buf = tail[o3:] - num, err := ssz.DivideInt2(len(buf), 49, 13) + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -177,7 +229,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if v.Post[ii] == nil { v.Post[ii] = new(MessageTrace) } - if err = v.Post[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { + if err = v.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } } @@ -187,7 +239,7 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the ValidatorDutyTrace object func (v *ValidatorDutyTrace) SizeSSZ() (size int) { - size = 36 + size = 40 // Field (1) 'Rounds' for ii := 0; ii < len(v.Rounds); ii++ { @@ -195,11 +247,17 @@ func (v *ValidatorDutyTrace) SizeSSZ() (size int) { size += v.Rounds[ii].SizeSSZ() } - // Field (2) 'Pre' - size += len(v.Pre) * 49 + // Field (2) 'Decideds' + for ii := 0; ii < len(v.Decideds); ii++ { + size += 4 + size += v.Decideds[ii].SizeSSZ() + } + + // Field (3) 'Pre' + size += len(v.Pre) * 56 - // Field (3) 'Post' - size += len(v.Post) * 49 + // Field (4) 'Post' + size += len(v.Post) * 56 return } @@ -232,7 +290,23 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 15) } - // Field (2) 'Pre' + // Field (2) 'Decideds' + { + subIndx := hh.Index() + num := uint64(len(v.Decideds)) + if num > 256 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range v.Decideds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 256) + } + + // Field (3) 'Pre' { subIndx := hh.Index() num := uint64(len(v.Pre)) @@ -248,7 +322,7 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (3) 'Post' + // Field (4) 'Post' { subIndx := hh.Index() num := uint64(len(v.Post)) @@ -264,10 +338,10 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (4) 'Role' + // Field (5) 'Role' hh.PutUint64(uint64(v.Role)) - // Field (5) 'Validator' + // Field (6) 'Validator' hh.PutUint64(uint64(v.Validator)) hh.Merkleize(indx) @@ -287,7 +361,7 @@ func (c *CommitteeDutyTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the CommitteeDutyTrace object to a target array func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(116) + offset := int(120) // Field (0) 'Slot' dst = ssz.MarshalUint64(dst, uint64(c.Slot)) @@ -299,24 +373,31 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { offset += c.Rounds[ii].SizeSSZ() } - // Offset (2) 'Post' + // Offset (2) 'Decideds' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(c.Decideds); ii++ { + offset += 4 + offset += c.Decideds[ii].SizeSSZ() + } + + // Offset (3) 'Post' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(c.Post); ii++ { offset += 4 offset += c.Post[ii].SizeSSZ() } - // Field (3) 'CommitteeID' + // Field (4) 'CommitteeID' dst = append(dst, c.CommitteeID[:]...) - // Offset (4) 'OperatorIDs' + // Offset (5) 'OperatorIDs' dst = ssz.WriteOffset(dst, offset) offset += len(c.OperatorIDs) * 8 - // Field (5) 'AttestationDataRoot' + // Field (6) 'AttestationDataRoot' dst = append(dst, c.AttestationDataRoot[:]...) - // Field (6) 'SyncCommitteeMessageRoot' + // Field (7) 'SyncCommitteeMessageRoot' dst = append(dst, c.SyncCommitteeMessageRoot[:]...) // Field (1) 'Rounds' @@ -337,7 +418,25 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (2) 'Post' + // Field (2) 'Decideds' + if size := len(c.Decideds); size > 256 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Decideds", size, 256) + return + } + { + offset = 4 * len(c.Decideds) + for ii := 0; ii < len(c.Decideds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Decideds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(c.Decideds); ii++ { + if dst, err = c.Decideds[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Post' if size := len(c.Post); size > 13 { err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 13) return @@ -355,7 +454,7 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (4) 'OperatorIDs' + // Field (5) 'OperatorIDs' if size := len(c.OperatorIDs); size > 13 { err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) return @@ -371,12 +470,12 @@ func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 116 { + if size < 120 { return ssz.ErrSize } tail := buf - var o1, o2, o4 uint64 + var o1, o2, o3, o5 uint64 // Field (0) 'Slot' c.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) @@ -386,28 +485,33 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { return ssz.ErrOffset } - if o1 < 116 { + if o1 < 120 { return ssz.ErrInvalidVariableOffset } - // Offset (2) 'Post' + // Offset (2) 'Decideds' if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { return ssz.ErrOffset } - // Field (3) 'CommitteeID' - copy(c.CommitteeID[:], buf[16:48]) + // Offset (3) 'Post' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + return ssz.ErrOffset + } + + // Field (4) 'CommitteeID' + copy(c.CommitteeID[:], buf[20:52]) - // Offset (4) 'OperatorIDs' - if o4 = ssz.ReadOffset(buf[48:52]); o4 > size || o2 > o4 { + // Offset (5) 'OperatorIDs' + if o5 = ssz.ReadOffset(buf[52:56]); o5 > size || o3 > o5 { return ssz.ErrOffset } - // Field (5) 'AttestationDataRoot' - copy(c.AttestationDataRoot[:], buf[52:84]) + // Field (6) 'AttestationDataRoot' + copy(c.AttestationDataRoot[:], buf[56:88]) - // Field (6) 'SyncCommitteeMessageRoot' - copy(c.SyncCommitteeMessageRoot[:], buf[84:116]) + // Field (7) 'SyncCommitteeMessageRoot' + copy(c.SyncCommitteeMessageRoot[:], buf[88:120]) // Field (1) 'Rounds' { @@ -431,9 +535,31 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (2) 'Post' + // Field (2) 'Decideds' + { + buf = tail[o2:o3] + num, err := ssz.DecodeDynamicLength(buf, 256) + if err != nil { + return err + } + c.Decideds = make([]*DecidedTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Decideds[indx] == nil { + c.Decideds[indx] = new(DecidedTrace) + } + if err = c.Decideds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (3) 'Post' { - buf = tail[o2:o4] + buf = tail[o3:o5] num, err := ssz.DecodeDynamicLength(buf, 13) if err != nil { return err @@ -453,9 +579,9 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (4) 'OperatorIDs' + // Field (5) 'OperatorIDs' { - buf = tail[o4:] + buf = tail[o5:] num, err := ssz.DivideInt2(len(buf), 8, 13) if err != nil { return err @@ -470,7 +596,7 @@ func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the CommitteeDutyTrace object func (c *CommitteeDutyTrace) SizeSSZ() (size int) { - size = 116 + size = 120 // Field (1) 'Rounds' for ii := 0; ii < len(c.Rounds); ii++ { @@ -478,13 +604,19 @@ func (c *CommitteeDutyTrace) SizeSSZ() (size int) { size += c.Rounds[ii].SizeSSZ() } - // Field (2) 'Post' + // Field (2) 'Decideds' + for ii := 0; ii < len(c.Decideds); ii++ { + size += 4 + size += c.Decideds[ii].SizeSSZ() + } + + // Field (3) 'Post' for ii := 0; ii < len(c.Post); ii++ { size += 4 size += c.Post[ii].SizeSSZ() } - // Field (4) 'OperatorIDs' + // Field (5) 'OperatorIDs' size += len(c.OperatorIDs) * 8 return @@ -518,7 +650,23 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 15) } - // Field (2) 'Post' + // Field (2) 'Decideds' + { + subIndx := hh.Index() + num := uint64(len(c.Decideds)) + if num > 256 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Decideds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 256) + } + + // Field (3) 'Post' { subIndx := hh.Index() num := uint64(len(c.Post)) @@ -534,10 +682,10 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (3) 'CommitteeID' + // Field (4) 'CommitteeID' hh.PutBytes(c.CommitteeID[:]) - // Field (4) 'OperatorIDs' + // Field (5) 'OperatorIDs' { if size := len(c.OperatorIDs); size > 13 { err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) @@ -552,10 +700,10 @@ func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) } - // Field (5) 'AttestationDataRoot' + // Field (6) 'AttestationDataRoot' hh.PutBytes(c.AttestationDataRoot[:]) - // Field (6) 'SyncCommitteeMessageRoot' + // Field (7) 'SyncCommitteeMessageRoot' hh.PutBytes(c.SyncCommitteeMessageRoot[:]) hh.Merkleize(indx) @@ -594,7 +742,7 @@ func (c *CommitteeMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) dst = ssz.MarshalUint64(dst, uint64(c.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalUint64(dst, c.ReceivedTime) + dst = ssz.MarshalTime(dst, c.ReceivedTime) // Field (1) 'Validators' if size := len(c.Validators); size > 1500 { @@ -638,7 +786,7 @@ func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[1028:1036])) // Field (3) 'ReceivedTime' - c.ReceivedTime = ssz.UnmarshallUint64(buf[1036:1044]) + c.ReceivedTime = ssz.UnmarshalTime(buf[1036:1044]) // Field (1) 'Validators' { @@ -647,16 +795,18 @@ func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - tempValidators := make([]uint64, num) - for ii := 0; ii < num; ii++ { - tempValidators[ii] = ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8]) + // Convert existing Validators to uint64 slice for ExtendUint64 + tmp := make([]uint64, len(c.Validators)) + for i, v := range c.Validators { + tmp[i] = uint64(v) } - c.Validators = make([]phase0.ValidatorIndex, num) - for ii, val := range tempValidators { - c.Validators[ii] = phase0.ValidatorIndex(val) + // Extend the temporary slice and create new Validators slice with correct type + tmp = ssz.ExtendUint64(tmp, num) + c.Validators = make([]phase0.ValidatorIndex, len(tmp)) + for ii := 0; ii < num; ii++ { + c.Validators[ii] = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) } } - return err } @@ -711,7 +861,7 @@ func (c *CommitteeMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) hh.PutUint64(uint64(c.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(c.ReceivedTime) + hh.PutUint64(uint64(c.ReceivedTime.Unix())) hh.Merkleize(indx) return @@ -722,6 +872,146 @@ func (c *CommitteeMessageTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(c) } +// MarshalSSZ ssz marshals the DecidedTrace object +func (d *DecidedTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) +} + +// MarshalSSZTo ssz marshals the DecidedTrace object to a target array +func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(60) + + // Field (0) 'Round' + dst = ssz.MarshalUint64(dst, d.Round) + + // Field (1) 'BeaconRoot' + dst = append(dst, d.BeaconRoot[:]...) + + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(d.Signer)) + + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, d.ReceivedTime) + + // Offset (4) 'Signers' + dst = ssz.WriteOffset(dst, offset) + offset += len(d.Signers) * 8 + + // Field (4) 'Signers' + if size := len(d.Signers); size > 13 { + err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) + return + } + for ii := 0; ii < len(d.Signers); ii++ { + dst = ssz.MarshalUint64(dst, uint64(d.Signers[ii])) + } + + return +} + +// UnmarshalSSZ ssz unmarshals the DecidedTrace object +func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 60 { + return ssz.ErrSize + } + + tail := buf + var o4 uint64 + + // Field (0) 'Round' + d.Round = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'BeaconRoot' + copy(d.BeaconRoot[:], buf[8:40]) + + // Field (2) 'Signer' + d.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'ReceivedTime' + d.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + + // Offset (4) 'Signers' + if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + return ssz.ErrOffset + } + + if o4 < 60 { + return ssz.ErrInvalidVariableOffset + } + + // Field (4) 'Signers' + { + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 8, 13) + if err != nil { + return err + } + d.Signers = ssz.ExtendUint64(d.Signers, num) + for ii := 0; ii < num; ii++ { + d.Signers[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the DecidedTrace object +func (d *DecidedTrace) SizeSSZ() (size int) { + size = 60 + + // Field (4) 'Signers' + size += len(d.Signers) * 8 + + return +} + +// HashTreeRoot ssz hashes the DecidedTrace object +func (d *DecidedTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) +} + +// HashTreeRootWith ssz hashes the DecidedTrace object with a hasher +func (d *DecidedTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Round' + hh.PutUint64(d.Round) + + // Field (1) 'BeaconRoot' + hh.PutBytes(d.BeaconRoot[:]) + + // Field (2) 'Signer' + hh.PutUint64(uint64(d.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(d.ReceivedTime.Unix())) + + // Field (4) 'Signers' + { + if size := len(d.Signers); size > 13 { + err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) + return + } + subIndx := hh.Index() + for _, i := range d.Signers { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(d.Signers)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the DecidedTrace object +func (d *DecidedTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) +} + // MarshalSSZ ssz marshals the RoundTrace object func (r *RoundTrace) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(r) @@ -730,33 +1020,39 @@ func (r *RoundTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the RoundTrace object to a target array func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(60) + offset := int(24) // Field (0) 'Proposer' dst = ssz.MarshalUint64(dst, uint64(r.Proposer)) - // Field (1) 'ProposalRoot' - dst = append(dst, r.ProposalRoot[:]...) - - // Field (2) 'ProposalReceivedTime' - dst = ssz.MarshalUint64(dst, r.ProposalReceivedTime) + // Offset (1) 'ProposalTrace' + dst = ssz.WriteOffset(dst, offset) + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) + } + offset += r.ProposalTrace.SizeSSZ() - // Offset (3) 'Prepares' + // Offset (2) 'Prepares' dst = ssz.WriteOffset(dst, offset) - offset += len(r.Prepares) * 49 + offset += len(r.Prepares) * 56 - // Offset (4) 'Commits' + // Offset (3) 'Commits' dst = ssz.WriteOffset(dst, offset) - offset += len(r.Commits) * 49 + offset += len(r.Commits) * 56 - // Offset (5) 'RoundChanges' + // Offset (4) 'RoundChanges' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(r.RoundChanges); ii++ { offset += 4 offset += r.RoundChanges[ii].SizeSSZ() } - // Field (3) 'Prepares' + // Field (1) 'ProposalTrace' + if dst, err = r.ProposalTrace.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Prepares' if size := len(r.Prepares); size > 13 { err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 13) return @@ -767,7 +1063,7 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (4) 'Commits' + // Field (3) 'Commits' if size := len(r.Commits); size > 13 { err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 13) return @@ -778,7 +1074,7 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (5) 'RoundChanges' + // Field (4) 'RoundChanges' if size := len(r.RoundChanges); size > 13 { err = ssz.ErrListTooBigFn("RoundTrace.RoundChanges", size, 13) return @@ -803,45 +1099,55 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 60 { + if size < 24 { return ssz.ErrSize } tail := buf - var o3, o4, o5 uint64 + var o1, o2, o3, o4 uint64 // Field (0) 'Proposer' r.Proposer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[0:8])) - // Field (1) 'ProposalRoot' - copy(r.ProposalRoot[:], buf[8:40]) - - // Field (2) 'ProposalReceivedTime' - r.ProposalReceivedTime = ssz.UnmarshallUint64(buf[40:48]) - - // Offset (3) 'Prepares' - if o3 = ssz.ReadOffset(buf[48:52]); o3 > size { + // Offset (1) 'ProposalTrace' + if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { return ssz.ErrOffset } - if o3 < 60 { + if o1 < 24 { return ssz.ErrInvalidVariableOffset } - // Offset (4) 'Commits' - if o4 = ssz.ReadOffset(buf[52:56]); o4 > size || o3 > o4 { + // Offset (2) 'Prepares' + if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } + + // Offset (3) 'Commits' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { return ssz.ErrOffset } - // Offset (5) 'RoundChanges' - if o5 = ssz.ReadOffset(buf[56:60]); o5 > size || o4 > o5 { + // Offset (4) 'RoundChanges' + if o4 = ssz.ReadOffset(buf[20:24]); o4 > size || o3 > o4 { return ssz.ErrOffset } - // Field (3) 'Prepares' + // Field (1) 'ProposalTrace' { - buf = tail[o3:o4] - num, err := ssz.DivideInt2(len(buf), 49, 13) + buf = tail[o1:o2] + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) + } + if err = r.ProposalTrace.UnmarshalSSZ(buf); err != nil { + return err + } + } + + // Field (2) 'Prepares' + { + buf = tail[o2:o3] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -850,16 +1156,16 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if r.Prepares[ii] == nil { r.Prepares[ii] = new(MessageTrace) } - if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { + if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } } } - // Field (4) 'Commits' + // Field (3) 'Commits' { - buf = tail[o4:o5] - num, err := ssz.DivideInt2(len(buf), 49, 13) + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -868,15 +1174,15 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if r.Commits[ii] == nil { r.Commits[ii] = new(MessageTrace) } - if err = r.Commits[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { + if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } } } - // Field (5) 'RoundChanges' + // Field (4) 'RoundChanges' { - buf = tail[o5:] + buf = tail[o4:] num, err := ssz.DecodeDynamicLength(buf, 13) if err != nil { return err @@ -900,15 +1206,21 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the RoundTrace object func (r *RoundTrace) SizeSSZ() (size int) { - size = 60 + size = 24 + + // Field (1) 'ProposalTrace' + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) + } + size += r.ProposalTrace.SizeSSZ() - // Field (3) 'Prepares' - size += len(r.Prepares) * 49 + // Field (2) 'Prepares' + size += len(r.Prepares) * 56 - // Field (4) 'Commits' - size += len(r.Commits) * 49 + // Field (3) 'Commits' + size += len(r.Commits) * 56 - // Field (5) 'RoundChanges' + // Field (4) 'RoundChanges' for ii := 0; ii < len(r.RoundChanges); ii++ { size += 4 size += r.RoundChanges[ii].SizeSSZ() @@ -929,13 +1241,12 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { // Field (0) 'Proposer' hh.PutUint64(uint64(r.Proposer)) - // Field (1) 'ProposalRoot' - hh.PutBytes(r.ProposalRoot[:]) - - // Field (2) 'ProposalReceivedTime' - hh.PutUint64(r.ProposalReceivedTime) + // Field (1) 'ProposalTrace' + if err = r.ProposalTrace.HashTreeRootWith(hh); err != nil { + return + } - // Field (3) 'Prepares' + // Field (2) 'Prepares' { subIndx := hh.Index() num := uint64(len(r.Prepares)) @@ -951,7 +1262,7 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (4) 'Commits' + // Field (3) 'Commits' { subIndx := hh.Index() num := uint64(len(r.Commits)) @@ -967,7 +1278,7 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (5) 'RoundChanges' + // Field (4) 'RoundChanges' { subIndx := hh.Index() num := uint64(len(r.RoundChanges)) @@ -1000,10 +1311,10 @@ func (r *RoundChangeTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the RoundChangeTrace object to a target array func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(54) + offset := int(68) // Field (0) 'Round' - dst = ssz.MarshalUint8(dst, r.Round) + dst = ssz.MarshalUint64(dst, r.Round) // Field (1) 'BeaconRoot' dst = append(dst, r.BeaconRoot[:]...) @@ -1012,14 +1323,14 @@ func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = ssz.MarshalUint64(dst, uint64(r.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalUint64(dst, r.ReceivedTime) + dst = ssz.MarshalTime(dst, r.ReceivedTime) // Field (4) 'PreparedRound' - dst = ssz.MarshalUint8(dst, r.PreparedRound) + dst = ssz.MarshalUint64(dst, r.PreparedRound) // Offset (5) 'PrepareMessages' dst = ssz.WriteOffset(dst, offset) - offset += len(r.PrepareMessages) * 49 + offset += len(r.PrepareMessages) * 56 // Field (5) 'PrepareMessages' if size := len(r.PrepareMessages); size > 13 { @@ -1039,7 +1350,7 @@ func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 54 { + if size < 68 { return ssz.ErrSize } @@ -1047,33 +1358,33 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { var o5 uint64 // Field (0) 'Round' - r.Round = ssz.UnmarshallUint8(buf[0:1]) + r.Round = ssz.UnmarshallUint64(buf[0:8]) // Field (1) 'BeaconRoot' - copy(r.BeaconRoot[:], buf[1:33]) + copy(r.BeaconRoot[:], buf[8:40]) // Field (2) 'Signer' - r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[33:41])) + r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) // Field (3) 'ReceivedTime' - r.ReceivedTime = ssz.UnmarshallUint64(buf[41:49]) + r.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) // Field (4) 'PreparedRound' - r.PreparedRound = ssz.UnmarshallUint8(buf[49:50]) + r.PreparedRound = ssz.UnmarshallUint64(buf[56:64]) // Offset (5) 'PrepareMessages' - if o5 = ssz.ReadOffset(buf[50:54]); o5 > size { + if o5 = ssz.ReadOffset(buf[64:68]); o5 > size { return ssz.ErrOffset } - if o5 < 54 { + if o5 < 68 { return ssz.ErrInvalidVariableOffset } // Field (5) 'PrepareMessages' { buf = tail[o5:] - num, err := ssz.DivideInt2(len(buf), 49, 13) + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } @@ -1082,7 +1393,7 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { if r.PrepareMessages[ii] == nil { r.PrepareMessages[ii] = new(MessageTrace) } - if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*49 : (ii+1)*49]); err != nil { + if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } } @@ -1092,10 +1403,10 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the RoundChangeTrace object func (r *RoundChangeTrace) SizeSSZ() (size int) { - size = 54 + size = 68 // Field (5) 'PrepareMessages' - size += len(r.PrepareMessages) * 49 + size += len(r.PrepareMessages) * 56 return } @@ -1110,7 +1421,7 @@ func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Round' - hh.PutUint8(r.Round) + hh.PutUint64(r.Round) // Field (1) 'BeaconRoot' hh.PutBytes(r.BeaconRoot[:]) @@ -1119,10 +1430,10 @@ func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.PutUint64(uint64(r.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(r.ReceivedTime) + hh.PutUint64(uint64(r.ReceivedTime.Unix())) // Field (4) 'PreparedRound' - hh.PutUint8(r.PreparedRound) + hh.PutUint64(r.PreparedRound) // Field (5) 'PrepareMessages' { @@ -1149,6 +1460,228 @@ func (r *RoundChangeTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(r) } +// MarshalSSZ ssz marshals the ProposalTrace object +func (p *ProposalTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProposalTrace object to a target array +func (p *ProposalTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(64) + + // Field (0) 'Round' + dst = ssz.MarshalUint64(dst, p.Round) + + // Field (1) 'BeaconRoot' + dst = append(dst, p.BeaconRoot[:]...) + + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(p.Signer)) + + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, p.ReceivedTime) + + // Offset (4) 'RoundChanges' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(p.RoundChanges); ii++ { + offset += 4 + offset += p.RoundChanges[ii].SizeSSZ() + } + + // Offset (5) 'PrepareMessages' + dst = ssz.WriteOffset(dst, offset) + offset += len(p.PrepareMessages) * 56 + + // Field (4) 'RoundChanges' + if size := len(p.RoundChanges); size > 13 { + err = ssz.ErrListTooBigFn("ProposalTrace.RoundChanges", size, 13) + return + } + { + offset = 4 * len(p.RoundChanges) + for ii := 0; ii < len(p.RoundChanges); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += p.RoundChanges[ii].SizeSSZ() + } + } + for ii := 0; ii < len(p.RoundChanges); ii++ { + if dst, err = p.RoundChanges[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (5) 'PrepareMessages' + if size := len(p.PrepareMessages); size > 13 { + err = ssz.ErrListTooBigFn("ProposalTrace.PrepareMessages", size, 13) + return + } + for ii := 0; ii < len(p.PrepareMessages); ii++ { + if dst, err = p.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProposalTrace object +func (p *ProposalTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 64 { + return ssz.ErrSize + } + + tail := buf + var o4, o5 uint64 + + // Field (0) 'Round' + p.Round = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'BeaconRoot' + copy(p.BeaconRoot[:], buf[8:40]) + + // Field (2) 'Signer' + p.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'ReceivedTime' + p.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + + // Offset (4) 'RoundChanges' + if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + return ssz.ErrOffset + } + + if o4 < 64 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (5) 'PrepareMessages' + if o5 = ssz.ReadOffset(buf[60:64]); o5 > size || o4 > o5 { + return ssz.ErrOffset + } + + // Field (4) 'RoundChanges' + { + buf = tail[o4:o5] + num, err := ssz.DecodeDynamicLength(buf, 13) + if err != nil { + return err + } + p.RoundChanges = make([]*RoundChangeTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if p.RoundChanges[indx] == nil { + p.RoundChanges[indx] = new(RoundChangeTrace) + } + if err = p.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (5) 'PrepareMessages' + { + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 56, 13) + if err != nil { + return err + } + p.PrepareMessages = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if p.PrepareMessages[ii] == nil { + p.PrepareMessages[ii] = new(MessageTrace) + } + if err = p.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProposalTrace object +func (p *ProposalTrace) SizeSSZ() (size int) { + size = 64 + + // Field (4) 'RoundChanges' + for ii := 0; ii < len(p.RoundChanges); ii++ { + size += 4 + size += p.RoundChanges[ii].SizeSSZ() + } + + // Field (5) 'PrepareMessages' + size += len(p.PrepareMessages) * 56 + + return +} + +// HashTreeRoot ssz hashes the ProposalTrace object +func (p *ProposalTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProposalTrace object with a hasher +func (p *ProposalTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Round' + hh.PutUint64(p.Round) + + // Field (1) 'BeaconRoot' + hh.PutBytes(p.BeaconRoot[:]) + + // Field (2) 'Signer' + hh.PutUint64(uint64(p.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(p.ReceivedTime.Unix())) + + // Field (4) 'RoundChanges' + { + subIndx := hh.Index() + num := uint64(len(p.RoundChanges)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range p.RoundChanges { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) + } + + // Field (5) 'PrepareMessages' + { + subIndx := hh.Index() + num := uint64(len(p.PrepareMessages)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range p.PrepareMessages { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) + } + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ProposalTrace object +func (p *ProposalTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + // MarshalSSZ ssz marshals the MessageTrace object func (m *MessageTrace) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(m) @@ -1159,7 +1692,7 @@ func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf // Field (0) 'Round' - dst = ssz.MarshalUint8(dst, m.Round) + dst = ssz.MarshalUint64(dst, m.Round) // Field (1) 'BeaconRoot' dst = append(dst, m.BeaconRoot[:]...) @@ -1168,7 +1701,7 @@ func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = ssz.MarshalUint64(dst, uint64(m.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalUint64(dst, m.ReceivedTime) + dst = ssz.MarshalTime(dst, m.ReceivedTime) return } @@ -1177,28 +1710,28 @@ func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size != 49 { + if size != 56 { return ssz.ErrSize } // Field (0) 'Round' - m.Round = ssz.UnmarshallUint8(buf[0:1]) + m.Round = ssz.UnmarshallUint64(buf[0:8]) // Field (1) 'BeaconRoot' - copy(m.BeaconRoot[:], buf[1:33]) + copy(m.BeaconRoot[:], buf[8:40]) // Field (2) 'Signer' - m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[33:41])) + m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) // Field (3) 'ReceivedTime' - m.ReceivedTime = ssz.UnmarshallUint64(buf[41:49]) + m.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) return err } // SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object func (m *MessageTrace) SizeSSZ() (size int) { - size = 49 + size = 56 return } @@ -1212,7 +1745,7 @@ func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Round' - hh.PutUint8(m.Round) + hh.PutUint64(m.Round) // Field (1) 'BeaconRoot' hh.PutBytes(m.BeaconRoot[:]) @@ -1221,7 +1754,7 @@ func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.PutUint64(uint64(m.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(m.ReceivedTime) + hh.PutUint64(uint64(m.ReceivedTime.Unix())) hh.Merkleize(indx) return diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index d99dfffb54..ae763230f8 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -1,7 +1,6 @@ package validator import ( - "encoding/hex" "sync" "time" @@ -47,13 +46,13 @@ func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *model.ValidatorDuty func (n *InMemTracer) getRound(trace *model.ValidatorDutyTrace, round uint64) *model.RoundTrace { var count = uint64(len(trace.Rounds)) - if count == 0 || count > round-1 { + for round+1 > count { var r model.RoundTrace trace.Rounds = append(trace.Rounds, &r) - return &r + count = uint64(len(trace.Rounds)) } - return trace.Rounds[round-1] + return trace.Rounds[round] } // id -> validator or committee id @@ -63,7 +62,7 @@ func getOperators(id string) []spectypes.OperatorID { func (n *InMemTracer) toMockState(msg *specqbft.Message, operatorIDs []spectypes.OperatorID) *specqbft.State { var mockState = &specqbft.State{} - mockState.Height = specqbft.Height(msg.Height) + mockState.Height = msg.Height mockState.CommitteeMember = &spectypes.CommitteeMember{ Committee: make([]*spectypes.Operator, 0, len(operatorIDs)), } @@ -91,7 +90,7 @@ func decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTr } var justificationTrace = new(model.MessageTrace) - justificationTrace.Round = uint8(qbftMsg.Round) + justificationTrace.Round = uint64(qbftMsg.Round) justificationTrace.BeaconRoot = qbftMsg.Root justificationTrace.Signer = signedMsg.OperatorIDs[0] traces = append(traces, justificationTrace) @@ -116,7 +115,7 @@ func decodeJustificationWithRoundChanges(justifications [][]byte) []*model.Round // n.logger.Error("failed to decode round change justification", zap.Error(err)) } - receivedTime := uint64(0) // we can't know the time when the sender received the message + var receivedTime time.Time // we can't know the time when the sender received the message var roundChangeTrace = createRoundChangeTrace(qbftMsg, signedMsg, receivedTime) traces = append(traces, roundChangeTrace) @@ -125,10 +124,10 @@ func decodeJustificationWithRoundChanges(justifications [][]byte) []*model.Round return traces } -func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, receivedTime uint64) *model.RoundChangeTrace { +func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, receivedTime time.Time) *model.RoundChangeTrace { var roundChangeTrace = new(model.RoundChangeTrace) - roundChangeTrace.PreparedRound = uint8(msg.DataRound) - roundChangeTrace.Round = uint8(msg.Round) + roundChangeTrace.PreparedRound = uint64(msg.DataRound) + roundChangeTrace.Round = uint64(msg.Round) roundChangeTrace.BeaconRoot = msg.Root roundChangeTrace.Signer = signedMsg.OperatorIDs[0] roundChangeTrace.ReceivedTime = receivedTime @@ -140,10 +139,10 @@ func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSS func createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) *model.ProposalTrace { var proposalTrace = new(model.ProposalTrace) - proposalTrace.Round = uint8(msg.Round) + proposalTrace.Round = uint64(msg.Round) proposalTrace.BeaconRoot = msg.Root proposalTrace.Signer = signedMsg.OperatorIDs[0] - proposalTrace.ReceivedTime = uint64(time.Now().Unix()) // correct + proposalTrace.ReceivedTime = time.Now() // correct proposalTrace.RoundChanges = decodeJustificationWithRoundChanges(msg.RoundChangeJustification) proposalTrace.PrepareMessages = decodeJustificationWithPrepares(msg.PrepareJustification) @@ -154,13 +153,6 @@ func createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMe func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { slot := uint64(msg.Height) - fields := []zap.Field{ - fields.Slot(phase0.Slot(slot)), - fields.Round(msg.Round), - zap.String("identifier", hex.EncodeToString(msg.Identifier[len(msg.Identifier)-5:])), - zap.String("root", hex.EncodeToString(msg.Root[len(msg.Root)-5:])), - } - // validator pubkey msgID := spectypes.MessageID(msg.Identifier[:]) // validator + pubkey + role + network validatorPubKey := msgID.GetDutyExecutorID() // validator pubkey or committee id @@ -174,14 +166,14 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV { // proposer operatorIDs := getOperators(validatorID) - var mockState = n.toMockState(msg, operatorIDs) - round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) + if len(operatorIDs) > 0 { + var mockState = n.toMockState(msg, operatorIDs) + round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) + } } switch msg.MsgType { case specqbft.ProposalMsgType: - fields = append(fields, zap.String("messageType", "proposal")) - var data = new(spectypes.ValidatorConsensusData) err := data.Decode(signedMsg.FullData) if err != nil { @@ -193,42 +185,32 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV round.ProposalTrace = createProposalTrace(msg, signedMsg) case specqbft.PrepareMsgType: - fields = append(fields, zap.String("messageType", "prepare")) - var m = new(model.MessageTrace) - m.Round = uint8(msg.Round) + m.Round = uint64(msg.Round) m.BeaconRoot = msg.Root m.Signer = signedMsg.OperatorIDs[0] - m.ReceivedTime = uint64(time.Now().Unix()) // TODO fixme + m.ReceivedTime = time.Now() // TODO fixme round.Prepares = append(round.Prepares, m) case specqbft.CommitMsgType: - fields = append(fields, zap.String("messageType", "commit")) - var m = new(model.MessageTrace) - m.Round = uint8(msg.Round) + m.Round = uint64(msg.Round) m.BeaconRoot = msg.Root m.Signer = signedMsg.OperatorIDs[0] - m.ReceivedTime = uint64(time.Now().Unix()) // TODO fixme + m.ReceivedTime = time.Now() // TODO fixme round.Commits = append(round.Commits, m) case specqbft.RoundChangeMsgType: - fields = append(fields, zap.String("messageType", "round change")) // optional - only if round change is proposing a value - now := uint64(time.Now().Unix()) + now := time.Now() roundChangeTrace := createRoundChangeTrace(msg, signedMsg, now) round.RoundChanges = append(round.RoundChanges, roundChangeTrace) } - fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) - - n.logger.Info("qbft", fields...) - - // trace the message // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) } From 7b1fc136193d017cc712405891f84f6b360c8477 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Mon, 3 Feb 2025 14:33:30 +0000 Subject: [PATCH 10/24] trace lock --- exporter/v2/model.go | 3 +++ operator/validator/dutytracer.go | 33 ++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index c3d286f674..98f05b2822 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -1,6 +1,7 @@ package exporter import ( + "sync" "time" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -9,6 +10,7 @@ import ( //go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path model.go --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { + sync.Mutex DutyTrace Pre []*MessageTrace `ssz-max:"13"` Post []*MessageTrace `ssz-max:"13"` @@ -50,6 +52,7 @@ type DecidedTrace struct { } type RoundTrace struct { + sync.Mutex Proposer spectypes.OperatorID // can be computed or saved // ProposalData ProposalTrace *ProposalTrace diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index ae763230f8..10f17397cf 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -29,6 +29,9 @@ func NewTracer(logger *zap.Logger) *InMemTracer { } func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *model.ValidatorDutyTrace { + n.Lock() + defer n.Unlock() + mp, ok := n.validatorTraces[slot] if !ok { mp = make(map[string]*model.ValidatorDutyTrace) @@ -44,7 +47,10 @@ func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *model.ValidatorDuty return trace } -func (n *InMemTracer) getRound(trace *model.ValidatorDutyTrace, round uint64) *model.RoundTrace { +func getRound(trace *model.ValidatorDutyTrace, round uint64) *model.RoundTrace { + trace.Lock() + defer trace.Unlock() + var count = uint64(len(trace.Rounds)) for round+1 > count { var r model.RoundTrace @@ -162,14 +168,15 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV trace := n.getTrace(slot, validatorID) // first round is 1 - var round = n.getRound(trace, uint64(msg.Round)) - - { // proposer - operatorIDs := getOperators(validatorID) - if len(operatorIDs) > 0 { - var mockState = n.toMockState(msg, operatorIDs) - round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) - } + var round = getRound(trace, uint64(msg.Round)) + round.Lock() + defer round.Unlock() + + // proposer + operatorIDs := getOperators(validatorID) + if len(operatorIDs) > 0 { + var mockState = n.toMockState(msg, operatorIDs) + round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) } switch msg.MsgType { @@ -228,12 +235,14 @@ func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages) { fields = append(fields, zap.Int("messages", len(msg.Messages))) fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) - round := uint64(0) // TODO - lastRound := n.getRound(trace, round) + r := uint64(0) // TODO + round := getRound(trace, r) + round.Lock() + defer round.Unlock() // Q: how to map Message to RoundTrace? for _, pSigMsg := range msg.Messages { - lastRound.Proposer = pSigMsg.Signer + round.Proposer = pSigMsg.Signer _ = pSigMsg.ValidatorIndex } From 1c380dda61343f09fc83f10f27291ae54d998116 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Mon, 3 Feb 2025 18:43:30 +0000 Subject: [PATCH 11/24] validator close to done --- api/handlers/model.go | 35 +- exporter/v2/model.go | 79 +- exporter/v2/model_encoding.go | 1962 ++++++++++++++++-------------- exporter/v2/store/store_test.go | 8 +- operator/validator/dutytracer.go | 159 ++- 5 files changed, 1227 insertions(+), 1016 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index d154f44d63..59f9897880 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -15,12 +15,20 @@ type validatorTraceResponse struct { type validatorTrace struct { Slot phase0.Slot `json:"slot"` Rounds []round + Decideds []decided Pre []message `json:"pre"` Post []message `json:"post"` Role spectypes.BeaconRole `json:"role"` Validator phase0.ValidatorIndex `json:"validator"` } +type decided struct { + Round uint64 + BeaconRoot phase0.Root + Signer spectypes.OperatorID + ReceivedTime time.Time +} + type round struct { Proposer spectypes.OperatorID `json:"proposer"` ProposalRoot phase0.Root `json:"proposalRoot"` @@ -37,10 +45,10 @@ type roundChange struct { } type message struct { - Round uint64 `json:"round"` - BeaconRoot phase0.Root `json:"beaconRoot"` - Signer spectypes.OperatorID `json:"signer"` - ReceivedTime time.Time `json:"time"` + Type spectypes.PartialSigMsgType `json:"type"` + BeaconRoot phase0.Root `json:"beaconRoot"` + Signer spectypes.OperatorID `json:"signer"` + ReceivedTime time.Time `json:"time"` } func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { @@ -54,10 +62,10 @@ func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { } } -func toMessageTrace(m []*model.MessageTrace) (out []message) { +func toMessageTrace(m []*model.PartialSigMessageTrace) (out []message) { for _, mt := range m { out = append(out, message{ - Round: mt.Round, + Type: mt.Type, BeaconRoot: mt.BeaconRoot, Signer: mt.Signer, ReceivedTime: mt.ReceivedTime, @@ -82,15 +90,17 @@ type committeeTraceResponse struct { } type committeeTrace struct { - Slot phase0.Slot `json:"slot"` - Rounds []round `json:"rounds"` - Post []committeeMessage `json:"post"` + Slot phase0.Slot `json:"slot"` + Rounds []round `json:"rounds"` + Decideds []decided `json:"decideds"` + Post []committeeMessage `json:"post"` CommitteeID spectypes.CommitteeID `json:"committeeID"` OperatorIDs []spectypes.OperatorID `json:"operatorIDs"` } type committeeMessage struct { + message BeaconRoot []phase0.Root `json:"beaconRoot"` Validators []phase0.ValidatorIndex `json:"validators"` Signer spectypes.OperatorID `json:"signer"` @@ -107,11 +117,12 @@ func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { } } -func toCommitteeMessageTrace(m []*model.CommitteeMessageTrace) (out []committeeMessage) { +func toCommitteeMessageTrace(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) { for _, mt := range m { out = append(out, committeeMessage{ - BeaconRoot: mt.BeaconRoot, - Validators: mt.Validators, + // Type: mt.Type, + // BeaconRoot: mt.BeaconRoot, + // Validators: mt.Validators, Signer: mt.Signer, ReceivedTime: mt.ReceivedTime, }) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 98f05b2822..db2c813b6b 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -1,7 +1,6 @@ package exporter import ( - "sync" "time" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -10,37 +9,18 @@ import ( //go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path model.go --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { - sync.Mutex - DutyTrace - Pre []*MessageTrace `ssz-max:"13"` - Post []*MessageTrace `ssz-max:"13"` + // sync.Mutex + ConsensusTrace + + Slot phase0.Slot + Pre []*PartialSigMessageTrace `ssz-max:"13"` + Post []*PartialSigMessageTrace `ssz-max:"13"` Role spectypes.BeaconRole // this could be a pubkey Validator phase0.ValidatorIndex } -type CommitteeDutyTrace struct { - DutyTrace - Post []*CommitteeMessageTrace `ssz-max:"13"` - - CommitteeID spectypes.CommitteeID `ssz-size:"32"` - OperatorIDs []spectypes.OperatorID `ssz-max:"13"` - - // maybe not needed - AttestationDataRoot phase0.Root `ssz-size:"32"` - SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` -} - -type CommitteeMessageTrace struct { - BeaconRoot []phase0.Root `ssz-max:"1500" ssz-size:"32"` - Validators []phase0.ValidatorIndex `ssz-max:"1500"` - - Signer spectypes.OperatorID - ReceivedTime time.Time -} - -type DutyTrace struct { - Slot phase0.Slot +type ConsensusTrace struct { Rounds []*RoundTrace `ssz-max:"15"` Decideds []*DecidedTrace `ssz-max:"256"` // TODO max } @@ -52,7 +32,7 @@ type DecidedTrace struct { } type RoundTrace struct { - sync.Mutex + // sync.Mutex Proposer spectypes.OperatorID // can be computed or saved // ProposalData ProposalTrace *ProposalTrace @@ -79,3 +59,46 @@ type MessageTrace struct { Signer spectypes.OperatorID ReceivedTime time.Time } + +type PartialSigMessageTrace struct { + Type spectypes.PartialSigMsgType + BeaconRoot phase0.Root `ssz-size:"32"` + Signer spectypes.OperatorID + ReceivedTime time.Time +} + +// Committee +type CommitteeDutyTrace struct { + ConsensusTrace + + Slot phase0.Slot + Post []*CommitteePartialSigMessageTrace `ssz-max:"13"` + + CommitteeID spectypes.CommitteeID `ssz-size:"32"` + OperatorIDs []spectypes.OperatorID `ssz-max:"13"` + + // maybe not needed + AttestationDataRoot phase0.Root `ssz-size:"32"` + SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` +} + +// where used? +type CommitteeMessageTrace struct { + BeaconRoot []phase0.Root `ssz-max:"1500" ssz-size:"32"` + Validators []phase0.ValidatorIndex `ssz-max:"1500"` + + Signer spectypes.OperatorID + ReceivedTime time.Time +} + +type CommitteePartialSigMessageTrace struct { + PartialSigMessageTrace + + Messages []*PartialSigMessage `ssz-max:"15"` // TODO confirm +} + +type PartialSigMessage struct { + BeaconRoot phase0.Root `ssz-size:"32"` + Signer spectypes.OperatorID + ValidatorIndex phase0.ValidatorIndex +} diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 1f9d665297..5dda947516 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 6e19b9b279acb29d30d15b4029d06aa4f5657ccf263e0879c519bee66c5d38cc +// Hash: 02e1063c48ea6cbe2c9f3645e11f00a9b7dfea58e6f937545867db8fd98a39dc // Version: 0.1.3 package exporter @@ -19,23 +19,23 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf offset := int(40) - // Field (0) 'Slot' - dst = ssz.MarshalUint64(dst, uint64(v.Slot)) - - // Offset (1) 'Rounds' + // Offset (0) 'Rounds' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(v.Rounds); ii++ { offset += 4 offset += v.Rounds[ii].SizeSSZ() } - // Offset (2) 'Decideds' + // Offset (1) 'Decideds' dst = ssz.WriteOffset(dst, offset) for ii := 0; ii < len(v.Decideds); ii++ { offset += 4 offset += v.Decideds[ii].SizeSSZ() } + // Field (2) 'Slot' + dst = ssz.MarshalUint64(dst, uint64(v.Slot)) + // Offset (3) 'Pre' dst = ssz.WriteOffset(dst, offset) offset += len(v.Pre) * 56 @@ -50,7 +50,7 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (6) 'Validator' dst = ssz.MarshalUint64(dst, uint64(v.Validator)) - // Field (1) 'Rounds' + // Field (0) 'Rounds' if size := len(v.Rounds); size > 15 { err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Rounds", size, 15) return @@ -68,7 +68,7 @@ func (v *ValidatorDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { } } - // Field (2) 'Decideds' + // Field (1) 'Decideds' if size := len(v.Decideds); size > 256 { err = ssz.ErrListTooBigFn("ValidatorDutyTrace.Decideds", size, 256) return @@ -120,27 +120,27 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { } tail := buf - var o1, o2, o3, o4 uint64 - - // Field (0) 'Slot' - v.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) + var o0, o1, o3, o4 uint64 - // Offset (1) 'Rounds' - if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { + // Offset (0) 'Rounds' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { return ssz.ErrOffset } - if o1 < 40 { + if o0 < 40 { return ssz.ErrInvalidVariableOffset } - // Offset (2) 'Decideds' - if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { + // Offset (1) 'Decideds' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { return ssz.ErrOffset } + // Field (2) 'Slot' + v.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[8:16])) + // Offset (3) 'Pre' - if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o1 > o3 { return ssz.ErrOffset } @@ -155,9 +155,9 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { // Field (6) 'Validator' v.Validator = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[32:40])) - // Field (1) 'Rounds' + // Field (0) 'Rounds' { - buf = tail[o1:o2] + buf = tail[o0:o1] num, err := ssz.DecodeDynamicLength(buf, 15) if err != nil { return err @@ -177,9 +177,9 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { } } - // Field (2) 'Decideds' + // Field (1) 'Decideds' { - buf = tail[o2:o3] + buf = tail[o1:o3] num, err := ssz.DecodeDynamicLength(buf, 256) if err != nil { return err @@ -206,10 +206,10 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - v.Pre = make([]*MessageTrace, num) + v.Pre = make([]*PartialSigMessageTrace, num) for ii := 0; ii < num; ii++ { if v.Pre[ii] == nil { - v.Pre[ii] = new(MessageTrace) + v.Pre[ii] = new(PartialSigMessageTrace) } if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -224,10 +224,10 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - v.Post = make([]*MessageTrace, num) + v.Post = make([]*PartialSigMessageTrace, num) for ii := 0; ii < num; ii++ { if v.Post[ii] == nil { - v.Post[ii] = new(MessageTrace) + v.Post[ii] = new(PartialSigMessageTrace) } if err = v.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -241,13 +241,13 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { func (v *ValidatorDutyTrace) SizeSSZ() (size int) { size = 40 - // Field (1) 'Rounds' + // Field (0) 'Rounds' for ii := 0; ii < len(v.Rounds); ii++ { size += 4 size += v.Rounds[ii].SizeSSZ() } - // Field (2) 'Decideds' + // Field (1) 'Decideds' for ii := 0; ii < len(v.Decideds); ii++ { size += 4 size += v.Decideds[ii].SizeSSZ() @@ -271,10 +271,7 @@ func (v *ValidatorDutyTrace) HashTreeRoot() ([32]byte, error) { func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Slot' - hh.PutUint64(uint64(v.Slot)) - - // Field (1) 'Rounds' + // Field (0) 'Rounds' { subIndx := hh.Index() num := uint64(len(v.Rounds)) @@ -290,7 +287,7 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 15) } - // Field (2) 'Decideds' + // Field (1) 'Decideds' { subIndx := hh.Index() num := uint64(len(v.Decideds)) @@ -306,6 +303,9 @@ func (v *ValidatorDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 256) } + // Field (2) 'Slot' + hh.PutUint64(uint64(v.Slot)) + // Field (3) 'Pre' { subIndx := hh.Index() @@ -353,741 +353,652 @@ func (v *ValidatorDutyTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(v) } -// MarshalSSZ ssz marshals the CommitteeDutyTrace object -func (c *CommitteeDutyTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(c) +// MarshalSSZ ssz marshals the DecidedTrace object +func (d *DecidedTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(d) } -// MarshalSSZTo ssz marshals the CommitteeDutyTrace object to a target array -func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the DecidedTrace object to a target array +func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(120) - - // Field (0) 'Slot' - dst = ssz.MarshalUint64(dst, uint64(c.Slot)) + offset := int(60) - // Offset (1) 'Rounds' - dst = ssz.WriteOffset(dst, offset) - for ii := 0; ii < len(c.Rounds); ii++ { - offset += 4 - offset += c.Rounds[ii].SizeSSZ() - } + // Field (0) 'Round' + dst = ssz.MarshalUint64(dst, d.Round) - // Offset (2) 'Decideds' - dst = ssz.WriteOffset(dst, offset) - for ii := 0; ii < len(c.Decideds); ii++ { - offset += 4 - offset += c.Decideds[ii].SizeSSZ() - } + // Field (1) 'BeaconRoot' + dst = append(dst, d.BeaconRoot[:]...) - // Offset (3) 'Post' - dst = ssz.WriteOffset(dst, offset) - for ii := 0; ii < len(c.Post); ii++ { - offset += 4 - offset += c.Post[ii].SizeSSZ() - } + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(d.Signer)) - // Field (4) 'CommitteeID' - dst = append(dst, c.CommitteeID[:]...) + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, d.ReceivedTime) - // Offset (5) 'OperatorIDs' + // Offset (4) 'Signers' dst = ssz.WriteOffset(dst, offset) - offset += len(c.OperatorIDs) * 8 - - // Field (6) 'AttestationDataRoot' - dst = append(dst, c.AttestationDataRoot[:]...) - - // Field (7) 'SyncCommitteeMessageRoot' - dst = append(dst, c.SyncCommitteeMessageRoot[:]...) - - // Field (1) 'Rounds' - if size := len(c.Rounds); size > 15 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Rounds", size, 15) - return - } - { - offset = 4 * len(c.Rounds) - for ii := 0; ii < len(c.Rounds); ii++ { - dst = ssz.WriteOffset(dst, offset) - offset += c.Rounds[ii].SizeSSZ() - } - } - for ii := 0; ii < len(c.Rounds); ii++ { - if dst, err = c.Rounds[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (2) 'Decideds' - if size := len(c.Decideds); size > 256 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Decideds", size, 256) - return - } - { - offset = 4 * len(c.Decideds) - for ii := 0; ii < len(c.Decideds); ii++ { - dst = ssz.WriteOffset(dst, offset) - offset += c.Decideds[ii].SizeSSZ() - } - } - for ii := 0; ii < len(c.Decideds); ii++ { - if dst, err = c.Decideds[ii].MarshalSSZTo(dst); err != nil { - return - } - } - - // Field (3) 'Post' - if size := len(c.Post); size > 13 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 13) - return - } - { - offset = 4 * len(c.Post) - for ii := 0; ii < len(c.Post); ii++ { - dst = ssz.WriteOffset(dst, offset) - offset += c.Post[ii].SizeSSZ() - } - } - for ii := 0; ii < len(c.Post); ii++ { - if dst, err = c.Post[ii].MarshalSSZTo(dst); err != nil { - return - } - } + offset += len(d.Signers) * 8 - // Field (5) 'OperatorIDs' - if size := len(c.OperatorIDs); size > 13 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) + // Field (4) 'Signers' + if size := len(d.Signers); size > 13 { + err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) return } - for ii := 0; ii < len(c.OperatorIDs); ii++ { - dst = ssz.MarshalUint64(dst, uint64(c.OperatorIDs[ii])) + for ii := 0; ii < len(d.Signers); ii++ { + dst = ssz.MarshalUint64(dst, uint64(d.Signers[ii])) } return } -// UnmarshalSSZ ssz unmarshals the CommitteeDutyTrace object -func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the DecidedTrace object +func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 120 { + if size < 60 { return ssz.ErrSize } tail := buf - var o1, o2, o3, o5 uint64 - - // Field (0) 'Slot' - c.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[0:8])) - - // Offset (1) 'Rounds' - if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { - return ssz.ErrOffset - } + var o4 uint64 - if o1 < 120 { - return ssz.ErrInvalidVariableOffset - } + // Field (0) 'Round' + d.Round = ssz.UnmarshallUint64(buf[0:8]) - // Offset (2) 'Decideds' - if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { - return ssz.ErrOffset - } + // Field (1) 'BeaconRoot' + copy(d.BeaconRoot[:], buf[8:40]) - // Offset (3) 'Post' - if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { - return ssz.ErrOffset - } + // Field (2) 'Signer' + d.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) - // Field (4) 'CommitteeID' - copy(c.CommitteeID[:], buf[20:52]) + // Field (3) 'ReceivedTime' + d.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) - // Offset (5) 'OperatorIDs' - if o5 = ssz.ReadOffset(buf[52:56]); o5 > size || o3 > o5 { + // Offset (4) 'Signers' + if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { return ssz.ErrOffset } - // Field (6) 'AttestationDataRoot' - copy(c.AttestationDataRoot[:], buf[56:88]) - - // Field (7) 'SyncCommitteeMessageRoot' - copy(c.SyncCommitteeMessageRoot[:], buf[88:120]) - - // Field (1) 'Rounds' - { - buf = tail[o1:o2] - num, err := ssz.DecodeDynamicLength(buf, 15) - if err != nil { - return err - } - c.Rounds = make([]*RoundTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if c.Rounds[indx] == nil { - c.Rounds[indx] = new(RoundTrace) - } - if err = c.Rounds[indx].UnmarshalSSZ(buf); err != nil { - return err - } - return nil - }) - if err != nil { - return err - } - } - - // Field (2) 'Decideds' - { - buf = tail[o2:o3] - num, err := ssz.DecodeDynamicLength(buf, 256) - if err != nil { - return err - } - c.Decideds = make([]*DecidedTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if c.Decideds[indx] == nil { - c.Decideds[indx] = new(DecidedTrace) - } - if err = c.Decideds[indx].UnmarshalSSZ(buf); err != nil { - return err - } - return nil - }) - if err != nil { - return err - } - } - - // Field (3) 'Post' - { - buf = tail[o3:o5] - num, err := ssz.DecodeDynamicLength(buf, 13) - if err != nil { - return err - } - c.Post = make([]*CommitteeMessageTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if c.Post[indx] == nil { - c.Post[indx] = new(CommitteeMessageTrace) - } - if err = c.Post[indx].UnmarshalSSZ(buf); err != nil { - return err - } - return nil - }) - if err != nil { - return err - } + if o4 < 60 { + return ssz.ErrInvalidVariableOffset } - // Field (5) 'OperatorIDs' + // Field (4) 'Signers' { - buf = tail[o5:] + buf = tail[o4:] num, err := ssz.DivideInt2(len(buf), 8, 13) if err != nil { return err } - c.OperatorIDs = ssz.ExtendUint64(c.OperatorIDs, num) + d.Signers = ssz.ExtendUint64(d.Signers, num) for ii := 0; ii < num; ii++ { - c.OperatorIDs[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + d.Signers[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) } } return err } -// SizeSSZ returns the ssz encoded size in bytes for the CommitteeDutyTrace object -func (c *CommitteeDutyTrace) SizeSSZ() (size int) { - size = 120 - - // Field (1) 'Rounds' - for ii := 0; ii < len(c.Rounds); ii++ { - size += 4 - size += c.Rounds[ii].SizeSSZ() - } +// SizeSSZ returns the ssz encoded size in bytes for the DecidedTrace object +func (d *DecidedTrace) SizeSSZ() (size int) { + size = 60 - // Field (2) 'Decideds' - for ii := 0; ii < len(c.Decideds); ii++ { - size += 4 - size += c.Decideds[ii].SizeSSZ() - } - - // Field (3) 'Post' - for ii := 0; ii < len(c.Post); ii++ { - size += 4 - size += c.Post[ii].SizeSSZ() - } - - // Field (5) 'OperatorIDs' - size += len(c.OperatorIDs) * 8 + // Field (4) 'Signers' + size += len(d.Signers) * 8 return } -// HashTreeRoot ssz hashes the CommitteeDutyTrace object -func (c *CommitteeDutyTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(c) +// HashTreeRoot ssz hashes the DecidedTrace object +func (d *DecidedTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(d) } -// HashTreeRootWith ssz hashes the CommitteeDutyTrace object with a hasher -func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the DecidedTrace object with a hasher +func (d *DecidedTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Slot' - hh.PutUint64(uint64(c.Slot)) - - // Field (1) 'Rounds' - { - subIndx := hh.Index() - num := uint64(len(c.Rounds)) - if num > 15 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range c.Rounds { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 15) - } + // Field (0) 'Round' + hh.PutUint64(d.Round) - // Field (2) 'Decideds' - { - subIndx := hh.Index() - num := uint64(len(c.Decideds)) - if num > 256 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range c.Decideds { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 256) - } + // Field (1) 'BeaconRoot' + hh.PutBytes(d.BeaconRoot[:]) - // Field (3) 'Post' - { - subIndx := hh.Index() - num := uint64(len(c.Post)) - if num > 13 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range c.Post { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 13) - } + // Field (2) 'Signer' + hh.PutUint64(uint64(d.Signer)) - // Field (4) 'CommitteeID' - hh.PutBytes(c.CommitteeID[:]) + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(d.ReceivedTime.Unix())) - // Field (5) 'OperatorIDs' + // Field (4) 'Signers' { - if size := len(c.OperatorIDs); size > 13 { - err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) + if size := len(d.Signers); size > 13 { + err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) return } subIndx := hh.Index() - for _, i := range c.OperatorIDs { + for _, i := range d.Signers { hh.AppendUint64(i) } hh.FillUpTo32() - numItems := uint64(len(c.OperatorIDs)) + numItems := uint64(len(d.Signers)) hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) } - // Field (6) 'AttestationDataRoot' - hh.PutBytes(c.AttestationDataRoot[:]) - - // Field (7) 'SyncCommitteeMessageRoot' - hh.PutBytes(c.SyncCommitteeMessageRoot[:]) - hh.Merkleize(indx) return } -// GetTree ssz hashes the CommitteeDutyTrace object -func (c *CommitteeDutyTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(c) +// GetTree ssz hashes the DecidedTrace object +func (d *DecidedTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(d) } -// MarshalSSZ ssz marshals the CommitteeMessageTrace object -func (c *CommitteeMessageTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(c) +// MarshalSSZ ssz marshals the RoundTrace object +func (r *RoundTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(r) } -// MarshalSSZTo ssz marshals the CommitteeMessageTrace object to a target array -func (c *CommitteeMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the RoundTrace object to a target array +func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(1044) + offset := int(24) - // Field (0) 'BeaconRoot' - if size := len(c.BeaconRoot); size != 32 { - err = ssz.ErrVectorLengthFn("CommitteeMessageTrace.BeaconRoot", size, 32) - return - } - for ii := 0; ii < 32; ii++ { - dst = append(dst, c.BeaconRoot[ii][:]...) + // Field (0) 'Proposer' + dst = ssz.MarshalUint64(dst, uint64(r.Proposer)) + + // Offset (1) 'ProposalTrace' + dst = ssz.WriteOffset(dst, offset) + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) } + offset += r.ProposalTrace.SizeSSZ() - // Offset (1) 'Validators' + // Offset (2) 'Prepares' dst = ssz.WriteOffset(dst, offset) - offset += len(c.Validators) * 8 + offset += len(r.Prepares) * 56 - // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(c.Signer)) + // Offset (3) 'Commits' + dst = ssz.WriteOffset(dst, offset) + offset += len(r.Commits) * 56 - // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, c.ReceivedTime) + // Offset (4) 'RoundChanges' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(r.RoundChanges); ii++ { + offset += 4 + offset += r.RoundChanges[ii].SizeSSZ() + } + + // Field (1) 'ProposalTrace' + if dst, err = r.ProposalTrace.MarshalSSZTo(dst); err != nil { + return + } + + // Field (2) 'Prepares' + if size := len(r.Prepares); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 13) + return + } + for ii := 0; ii < len(r.Prepares); ii++ { + if dst, err = r.Prepares[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (3) 'Commits' + if size := len(r.Commits); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 13) + return + } + for ii := 0; ii < len(r.Commits); ii++ { + if dst, err = r.Commits[ii].MarshalSSZTo(dst); err != nil { + return + } + } - // Field (1) 'Validators' - if size := len(c.Validators); size > 1500 { - err = ssz.ErrListTooBigFn("CommitteeMessageTrace.Validators", size, 1500) + // Field (4) 'RoundChanges' + if size := len(r.RoundChanges); size > 13 { + err = ssz.ErrListTooBigFn("RoundTrace.RoundChanges", size, 13) return } - for ii := 0; ii < len(c.Validators); ii++ { - dst = ssz.MarshalUint64(dst, uint64(c.Validators[ii])) + { + offset = 4 * len(r.RoundChanges) + for ii := 0; ii < len(r.RoundChanges); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += r.RoundChanges[ii].SizeSSZ() + } + } + for ii := 0; ii < len(r.RoundChanges); ii++ { + if dst, err = r.RoundChanges[ii].MarshalSSZTo(dst); err != nil { + return + } } return } -// UnmarshalSSZ ssz unmarshals the CommitteeMessageTrace object -func (c *CommitteeMessageTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the RoundTrace object +func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 1044 { + if size < 24 { return ssz.ErrSize } tail := buf - var o1 uint64 + var o1, o2, o3, o4 uint64 - // Field (0) 'BeaconRoot' - c.BeaconRoot = make([]phase0.Root, 32) - for ii := 0; ii < 32; ii++ { - copy(c.BeaconRoot[ii][:], buf[0:1024][ii*32:(ii+1)*32]) - } + // Field (0) 'Proposer' + r.Proposer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[0:8])) - // Offset (1) 'Validators' - if o1 = ssz.ReadOffset(buf[1024:1028]); o1 > size { + // Offset (1) 'ProposalTrace' + if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { return ssz.ErrOffset } - if o1 < 1044 { + if o1 < 24 { return ssz.ErrInvalidVariableOffset } - // Field (2) 'Signer' - c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[1028:1036])) + // Offset (2) 'Prepares' + if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { + return ssz.ErrOffset + } - // Field (3) 'ReceivedTime' - c.ReceivedTime = ssz.UnmarshalTime(buf[1036:1044]) + // Offset (3) 'Commits' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + return ssz.ErrOffset + } + + // Offset (4) 'RoundChanges' + if o4 = ssz.ReadOffset(buf[20:24]); o4 > size || o3 > o4 { + return ssz.ErrOffset + } - // Field (1) 'Validators' + // Field (1) 'ProposalTrace' { - buf = tail[o1:] - num, err := ssz.DivideInt2(len(buf), 8, 1500) - if err != nil { - return err - } - // Convert existing Validators to uint64 slice for ExtendUint64 - tmp := make([]uint64, len(c.Validators)) - for i, v := range c.Validators { - tmp[i] = uint64(v) + buf = tail[o1:o2] + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) } - // Extend the temporary slice and create new Validators slice with correct type - tmp = ssz.ExtendUint64(tmp, num) - c.Validators = make([]phase0.ValidatorIndex, len(tmp)) - for ii := 0; ii < num; ii++ { - c.Validators[ii] = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + if err = r.ProposalTrace.UnmarshalSSZ(buf); err != nil { + return err } } - return err -} -// SizeSSZ returns the ssz encoded size in bytes for the CommitteeMessageTrace object -func (c *CommitteeMessageTrace) SizeSSZ() (size int) { - size = 1044 - - // Field (1) 'Validators' - size += len(c.Validators) * 8 - - return -} - -// HashTreeRoot ssz hashes the CommitteeMessageTrace object -func (c *CommitteeMessageTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(c) -} - -// HashTreeRootWith ssz hashes the CommitteeMessageTrace object with a hasher -func (c *CommitteeMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { - indx := hh.Index() - - // Field (0) 'BeaconRoot' + // Field (2) 'Prepares' { - if size := len(c.BeaconRoot); size != 32 { - err = ssz.ErrVectorLengthFn("CommitteeMessageTrace.BeaconRoot", size, 32) - return + buf = tail[o2:o3] + num, err := ssz.DivideInt2(len(buf), 56, 13) + if err != nil { + return err } - subIndx := hh.Index() - for _, i := range c.BeaconRoot { - hh.Append(i[:]) + r.Prepares = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if r.Prepares[ii] == nil { + r.Prepares[ii] = new(MessageTrace) + } + if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } } - hh.Merkleize(subIndx) } - // Field (1) 'Validators' + // Field (3) 'Commits' { - if size := len(c.Validators); size > 1500 { - err = ssz.ErrListTooBigFn("CommitteeMessageTrace.Validators", size, 1500) - return + buf = tail[o3:o4] + num, err := ssz.DivideInt2(len(buf), 56, 13) + if err != nil { + return err } - subIndx := hh.Index() - for _, i := range c.Validators { - hh.AppendUint64(uint64(i)) + r.Commits = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if r.Commits[ii] == nil { + r.Commits[ii] = new(MessageTrace) + } + if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } } - hh.FillUpTo32() - numItems := uint64(len(c.Validators)) - hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(1500, numItems, 8)) } - // Field (2) 'Signer' - hh.PutUint64(uint64(c.Signer)) - - // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(c.ReceivedTime.Unix())) - + // Field (4) 'RoundChanges' + { + buf = tail[o4:] + num, err := ssz.DecodeDynamicLength(buf, 13) + if err != nil { + return err + } + r.RoundChanges = make([]*RoundChangeTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if r.RoundChanges[indx] == nil { + r.RoundChanges[indx] = new(RoundChangeTrace) + } + if err = r.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the RoundTrace object +func (r *RoundTrace) SizeSSZ() (size int) { + size = 24 + + // Field (1) 'ProposalTrace' + if r.ProposalTrace == nil { + r.ProposalTrace = new(ProposalTrace) + } + size += r.ProposalTrace.SizeSSZ() + + // Field (2) 'Prepares' + size += len(r.Prepares) * 56 + + // Field (3) 'Commits' + size += len(r.Commits) * 56 + + // Field (4) 'RoundChanges' + for ii := 0; ii < len(r.RoundChanges); ii++ { + size += 4 + size += r.RoundChanges[ii].SizeSSZ() + } + + return +} + +// HashTreeRoot ssz hashes the RoundTrace object +func (r *RoundTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(r) +} + +// HashTreeRootWith ssz hashes the RoundTrace object with a hasher +func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Proposer' + hh.PutUint64(uint64(r.Proposer)) + + // Field (1) 'ProposalTrace' + if err = r.ProposalTrace.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'Prepares' + { + subIndx := hh.Index() + num := uint64(len(r.Prepares)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.Prepares { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) + } + + // Field (3) 'Commits' + { + subIndx := hh.Index() + num := uint64(len(r.Commits)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.Commits { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) + } + + // Field (4) 'RoundChanges' + { + subIndx := hh.Index() + num := uint64(len(r.RoundChanges)) + if num > 13 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range r.RoundChanges { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 13) + } + hh.Merkleize(indx) return } -// GetTree ssz hashes the CommitteeMessageTrace object -func (c *CommitteeMessageTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(c) +// GetTree ssz hashes the RoundTrace object +func (r *RoundTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(r) } -// MarshalSSZ ssz marshals the DecidedTrace object -func (d *DecidedTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(d) +// MarshalSSZ ssz marshals the RoundChangeTrace object +func (r *RoundChangeTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(r) } -// MarshalSSZTo ssz marshals the DecidedTrace object to a target array -func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the RoundChangeTrace object to a target array +func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(60) + offset := int(68) // Field (0) 'Round' - dst = ssz.MarshalUint64(dst, d.Round) + dst = ssz.MarshalUint64(dst, r.Round) // Field (1) 'BeaconRoot' - dst = append(dst, d.BeaconRoot[:]...) + dst = append(dst, r.BeaconRoot[:]...) // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(d.Signer)) + dst = ssz.MarshalUint64(dst, uint64(r.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, d.ReceivedTime) + dst = ssz.MarshalTime(dst, r.ReceivedTime) - // Offset (4) 'Signers' + // Field (4) 'PreparedRound' + dst = ssz.MarshalUint64(dst, r.PreparedRound) + + // Offset (5) 'PrepareMessages' dst = ssz.WriteOffset(dst, offset) - offset += len(d.Signers) * 8 + offset += len(r.PrepareMessages) * 56 - // Field (4) 'Signers' - if size := len(d.Signers); size > 13 { - err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) + // Field (5) 'PrepareMessages' + if size := len(r.PrepareMessages); size > 13 { + err = ssz.ErrListTooBigFn("RoundChangeTrace.PrepareMessages", size, 13) return } - for ii := 0; ii < len(d.Signers); ii++ { - dst = ssz.MarshalUint64(dst, uint64(d.Signers[ii])) + for ii := 0; ii < len(r.PrepareMessages); ii++ { + if dst, err = r.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { + return + } } return } -// UnmarshalSSZ ssz unmarshals the DecidedTrace object -func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the RoundChangeTrace object +func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 60 { + if size < 68 { return ssz.ErrSize } tail := buf - var o4 uint64 + var o5 uint64 // Field (0) 'Round' - d.Round = ssz.UnmarshallUint64(buf[0:8]) + r.Round = ssz.UnmarshallUint64(buf[0:8]) // Field (1) 'BeaconRoot' - copy(d.BeaconRoot[:], buf[8:40]) + copy(r.BeaconRoot[:], buf[8:40]) // Field (2) 'Signer' - d.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) // Field (3) 'ReceivedTime' - d.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + r.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) - // Offset (4) 'Signers' - if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + // Field (4) 'PreparedRound' + r.PreparedRound = ssz.UnmarshallUint64(buf[56:64]) + + // Offset (5) 'PrepareMessages' + if o5 = ssz.ReadOffset(buf[64:68]); o5 > size { return ssz.ErrOffset } - if o4 < 60 { + if o5 < 68 { return ssz.ErrInvalidVariableOffset } - // Field (4) 'Signers' + // Field (5) 'PrepareMessages' { - buf = tail[o4:] - num, err := ssz.DivideInt2(len(buf), 8, 13) + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } - d.Signers = ssz.ExtendUint64(d.Signers, num) + r.PrepareMessages = make([]*MessageTrace, num) for ii := 0; ii < num; ii++ { - d.Signers[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) + if r.PrepareMessages[ii] == nil { + r.PrepareMessages[ii] = new(MessageTrace) + } + if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + return err + } } } return err } -// SizeSSZ returns the ssz encoded size in bytes for the DecidedTrace object -func (d *DecidedTrace) SizeSSZ() (size int) { - size = 60 +// SizeSSZ returns the ssz encoded size in bytes for the RoundChangeTrace object +func (r *RoundChangeTrace) SizeSSZ() (size int) { + size = 68 - // Field (4) 'Signers' - size += len(d.Signers) * 8 + // Field (5) 'PrepareMessages' + size += len(r.PrepareMessages) * 56 return } -// HashTreeRoot ssz hashes the DecidedTrace object -func (d *DecidedTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(d) +// HashTreeRoot ssz hashes the RoundChangeTrace object +func (r *RoundChangeTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(r) } -// HashTreeRootWith ssz hashes the DecidedTrace object with a hasher -func (d *DecidedTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the RoundChangeTrace object with a hasher +func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Round' - hh.PutUint64(d.Round) + hh.PutUint64(r.Round) // Field (1) 'BeaconRoot' - hh.PutBytes(d.BeaconRoot[:]) + hh.PutBytes(r.BeaconRoot[:]) // Field (2) 'Signer' - hh.PutUint64(uint64(d.Signer)) + hh.PutUint64(uint64(r.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(d.ReceivedTime.Unix())) + hh.PutUint64(uint64(r.ReceivedTime.Unix())) - // Field (4) 'Signers' + // Field (4) 'PreparedRound' + hh.PutUint64(r.PreparedRound) + + // Field (5) 'PrepareMessages' { - if size := len(d.Signers); size > 13 { - err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) + subIndx := hh.Index() + num := uint64(len(r.PrepareMessages)) + if num > 13 { + err = ssz.ErrIncorrectListSize return } - subIndx := hh.Index() - for _, i := range d.Signers { - hh.AppendUint64(i) + for _, elem := range r.PrepareMessages { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } } - hh.FillUpTo32() - numItems := uint64(len(d.Signers)) - hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) + hh.MerkleizeWithMixin(subIndx, num, 13) } hh.Merkleize(indx) return } -// GetTree ssz hashes the DecidedTrace object -func (d *DecidedTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(d) +// GetTree ssz hashes the RoundChangeTrace object +func (r *RoundChangeTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(r) } -// MarshalSSZ ssz marshals the RoundTrace object -func (r *RoundTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(r) +// MarshalSSZ ssz marshals the ProposalTrace object +func (p *ProposalTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) } -// MarshalSSZTo ssz marshals the RoundTrace object to a target array -func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the ProposalTrace object to a target array +func (p *ProposalTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(24) + offset := int(64) - // Field (0) 'Proposer' - dst = ssz.MarshalUint64(dst, uint64(r.Proposer)) + // Field (0) 'Round' + dst = ssz.MarshalUint64(dst, p.Round) - // Offset (1) 'ProposalTrace' - dst = ssz.WriteOffset(dst, offset) - if r.ProposalTrace == nil { - r.ProposalTrace = new(ProposalTrace) - } - offset += r.ProposalTrace.SizeSSZ() + // Field (1) 'BeaconRoot' + dst = append(dst, p.BeaconRoot[:]...) - // Offset (2) 'Prepares' - dst = ssz.WriteOffset(dst, offset) - offset += len(r.Prepares) * 56 + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(p.Signer)) - // Offset (3) 'Commits' - dst = ssz.WriteOffset(dst, offset) - offset += len(r.Commits) * 56 + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, p.ReceivedTime) // Offset (4) 'RoundChanges' dst = ssz.WriteOffset(dst, offset) - for ii := 0; ii < len(r.RoundChanges); ii++ { + for ii := 0; ii < len(p.RoundChanges); ii++ { offset += 4 - offset += r.RoundChanges[ii].SizeSSZ() + offset += p.RoundChanges[ii].SizeSSZ() } - // Field (1) 'ProposalTrace' - if dst, err = r.ProposalTrace.MarshalSSZTo(dst); err != nil { - return - } + // Offset (5) 'PrepareMessages' + dst = ssz.WriteOffset(dst, offset) + offset += len(p.PrepareMessages) * 56 - // Field (2) 'Prepares' - if size := len(r.Prepares); size > 13 { - err = ssz.ErrListTooBigFn("RoundTrace.Prepares", size, 13) + // Field (4) 'RoundChanges' + if size := len(p.RoundChanges); size > 13 { + err = ssz.ErrListTooBigFn("ProposalTrace.RoundChanges", size, 13) return } - for ii := 0; ii < len(r.Prepares); ii++ { - if dst, err = r.Prepares[ii].MarshalSSZTo(dst); err != nil { - return + { + offset = 4 * len(p.RoundChanges) + for ii := 0; ii < len(p.RoundChanges); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += p.RoundChanges[ii].SizeSSZ() } } - - // Field (3) 'Commits' - if size := len(r.Commits); size > 13 { - err = ssz.ErrListTooBigFn("RoundTrace.Commits", size, 13) - return - } - for ii := 0; ii < len(r.Commits); ii++ { - if dst, err = r.Commits[ii].MarshalSSZTo(dst); err != nil { + for ii := 0; ii < len(p.RoundChanges); ii++ { + if dst, err = p.RoundChanges[ii].MarshalSSZTo(dst); err != nil { return } } - // Field (4) 'RoundChanges' - if size := len(r.RoundChanges); size > 13 { - err = ssz.ErrListTooBigFn("RoundTrace.RoundChanges", size, 13) + // Field (5) 'PrepareMessages' + if size := len(p.PrepareMessages); size > 13 { + err = ssz.ErrListTooBigFn("ProposalTrace.PrepareMessages", size, 13) return } - { - offset = 4 * len(r.RoundChanges) - for ii := 0; ii < len(r.RoundChanges); ii++ { - dst = ssz.WriteOffset(dst, offset) - offset += r.RoundChanges[ii].SizeSSZ() - } - } - for ii := 0; ii < len(r.RoundChanges); ii++ { - if dst, err = r.RoundChanges[ii].MarshalSSZTo(dst); err != nil { + for ii := 0; ii < len(p.PrepareMessages); ii++ { + if dst, err = p.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { return } } @@ -1095,166 +1006,131 @@ func (r *RoundTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { return } -// UnmarshalSSZ ssz unmarshals the RoundTrace object -func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the ProposalTrace object +func (p *ProposalTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 24 { + if size < 64 { return ssz.ErrSize } tail := buf - var o1, o2, o3, o4 uint64 + var o4, o5 uint64 - // Field (0) 'Proposer' - r.Proposer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[0:8])) + // Field (0) 'Round' + p.Round = ssz.UnmarshallUint64(buf[0:8]) - // Offset (1) 'ProposalTrace' - if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { - return ssz.ErrOffset - } + // Field (1) 'BeaconRoot' + copy(p.BeaconRoot[:], buf[8:40]) - if o1 < 24 { - return ssz.ErrInvalidVariableOffset - } + // Field (2) 'Signer' + p.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) - // Offset (2) 'Prepares' - if o2 = ssz.ReadOffset(buf[12:16]); o2 > size || o1 > o2 { - return ssz.ErrOffset - } + // Field (3) 'ReceivedTime' + p.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) - // Offset (3) 'Commits' - if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o2 > o3 { + // Offset (4) 'RoundChanges' + if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { return ssz.ErrOffset } - // Offset (4) 'RoundChanges' - if o4 = ssz.ReadOffset(buf[20:24]); o4 > size || o3 > o4 { - return ssz.ErrOffset + if o4 < 64 { + return ssz.ErrInvalidVariableOffset } - // Field (1) 'ProposalTrace' - { - buf = tail[o1:o2] - if r.ProposalTrace == nil { - r.ProposalTrace = new(ProposalTrace) - } - if err = r.ProposalTrace.UnmarshalSSZ(buf); err != nil { - return err - } + // Offset (5) 'PrepareMessages' + if o5 = ssz.ReadOffset(buf[60:64]); o5 > size || o4 > o5 { + return ssz.ErrOffset } - // Field (2) 'Prepares' + // Field (4) 'RoundChanges' { - buf = tail[o2:o3] - num, err := ssz.DivideInt2(len(buf), 56, 13) + buf = tail[o4:o5] + num, err := ssz.DecodeDynamicLength(buf, 13) if err != nil { return err } - r.Prepares = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if r.Prepares[ii] == nil { - r.Prepares[ii] = new(MessageTrace) + p.RoundChanges = make([]*RoundChangeTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if p.RoundChanges[indx] == nil { + p.RoundChanges[indx] = new(RoundChangeTrace) } - if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = p.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { return err } - } - } - - // Field (3) 'Commits' - { - buf = tail[o3:o4] - num, err := ssz.DivideInt2(len(buf), 56, 13) + return nil + }) if err != nil { return err } - r.Commits = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if r.Commits[ii] == nil { - r.Commits[ii] = new(MessageTrace) - } - if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { - return err - } - } } - // Field (4) 'RoundChanges' + // Field (5) 'PrepareMessages' { - buf = tail[o4:] - num, err := ssz.DecodeDynamicLength(buf, 13) + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 56, 13) if err != nil { return err } - r.RoundChanges = make([]*RoundChangeTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if r.RoundChanges[indx] == nil { - r.RoundChanges[indx] = new(RoundChangeTrace) + p.PrepareMessages = make([]*MessageTrace, num) + for ii := 0; ii < num; ii++ { + if p.PrepareMessages[ii] == nil { + p.PrepareMessages[ii] = new(MessageTrace) } - if err = r.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { + if err = p.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err } - return nil - }) - if err != nil { - return err } } return err } -// SizeSSZ returns the ssz encoded size in bytes for the RoundTrace object -func (r *RoundTrace) SizeSSZ() (size int) { - size = 24 - - // Field (1) 'ProposalTrace' - if r.ProposalTrace == nil { - r.ProposalTrace = new(ProposalTrace) - } - size += r.ProposalTrace.SizeSSZ() - - // Field (2) 'Prepares' - size += len(r.Prepares) * 56 - - // Field (3) 'Commits' - size += len(r.Commits) * 56 +// SizeSSZ returns the ssz encoded size in bytes for the ProposalTrace object +func (p *ProposalTrace) SizeSSZ() (size int) { + size = 64 // Field (4) 'RoundChanges' - for ii := 0; ii < len(r.RoundChanges); ii++ { + for ii := 0; ii < len(p.RoundChanges); ii++ { size += 4 - size += r.RoundChanges[ii].SizeSSZ() + size += p.RoundChanges[ii].SizeSSZ() } + // Field (5) 'PrepareMessages' + size += len(p.PrepareMessages) * 56 + return } -// HashTreeRoot ssz hashes the RoundTrace object -func (r *RoundTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(r) +// HashTreeRoot ssz hashes the ProposalTrace object +func (p *ProposalTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) } -// HashTreeRootWith ssz hashes the RoundTrace object with a hasher -func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the ProposalTrace object with a hasher +func (p *ProposalTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Proposer' - hh.PutUint64(uint64(r.Proposer)) + // Field (0) 'Round' + hh.PutUint64(p.Round) - // Field (1) 'ProposalTrace' - if err = r.ProposalTrace.HashTreeRootWith(hh); err != nil { - return - } + // Field (1) 'BeaconRoot' + hh.PutBytes(p.BeaconRoot[:]) - // Field (2) 'Prepares' + // Field (2) 'Signer' + hh.PutUint64(uint64(p.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(p.ReceivedTime.Unix())) + + // Field (4) 'RoundChanges' { subIndx := hh.Index() - num := uint64(len(r.Prepares)) + num := uint64(len(p.RoundChanges)) if num > 13 { err = ssz.ErrIncorrectListSize return } - for _, elem := range r.Prepares { + for _, elem := range p.RoundChanges { if err = elem.HashTreeRootWith(hh); err != nil { return } @@ -1262,15 +1138,15 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (3) 'Commits' + // Field (5) 'PrepareMessages' { subIndx := hh.Index() - num := uint64(len(r.Commits)) + num := uint64(len(p.PrepareMessages)) if num > 13 { err = ssz.ErrIncorrectListSize return } - for _, elem := range r.Commits { + for _, elem := range p.PrepareMessages { if err = elem.HashTreeRootWith(hh); err != nil { return } @@ -1278,172 +1154,503 @@ func (r *RoundTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } - // Field (4) 'RoundChanges' - { - subIndx := hh.Index() - num := uint64(len(r.RoundChanges)) - if num > 13 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range r.RoundChanges { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 13) + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the ProposalTrace object +func (p *ProposalTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the MessageTrace object +func (m *MessageTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(m) +} + +// MarshalSSZTo ssz marshals the MessageTrace object to a target array +func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'Round' + dst = ssz.MarshalUint64(dst, m.Round) + + // Field (1) 'BeaconRoot' + dst = append(dst, m.BeaconRoot[:]...) + + // Field (2) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(m.Signer)) + + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, m.ReceivedTime) + + return +} + +// UnmarshalSSZ ssz unmarshals the MessageTrace object +func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 56 { + return ssz.ErrSize } + // Field (0) 'Round' + m.Round = ssz.UnmarshallUint64(buf[0:8]) + + // Field (1) 'BeaconRoot' + copy(m.BeaconRoot[:], buf[8:40]) + + // Field (2) 'Signer' + m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'ReceivedTime' + m.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object +func (m *MessageTrace) SizeSSZ() (size int) { + size = 56 + return +} + +// HashTreeRoot ssz hashes the MessageTrace object +func (m *MessageTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(m) +} + +// HashTreeRootWith ssz hashes the MessageTrace object with a hasher +func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Round' + hh.PutUint64(m.Round) + + // Field (1) 'BeaconRoot' + hh.PutBytes(m.BeaconRoot[:]) + + // Field (2) 'Signer' + hh.PutUint64(uint64(m.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(m.ReceivedTime.Unix())) + hh.Merkleize(indx) return } -// GetTree ssz hashes the RoundTrace object -func (r *RoundTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(r) +// GetTree ssz hashes the MessageTrace object +func (m *MessageTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(m) } -// MarshalSSZ ssz marshals the RoundChangeTrace object -func (r *RoundChangeTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(r) +// MarshalSSZ ssz marshals the PartialSigMessageTrace object +func (p *PartialSigMessageTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) } -// MarshalSSZTo ssz marshals the RoundChangeTrace object to a target array -func (r *RoundChangeTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the PartialSigMessageTrace object to a target array +func (p *PartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(68) - // Field (0) 'Round' - dst = ssz.MarshalUint64(dst, r.Round) + // Field (0) 'Type' + dst = ssz.MarshalUint64(dst, uint64(p.Type)) // Field (1) 'BeaconRoot' - dst = append(dst, r.BeaconRoot[:]...) + dst = append(dst, p.BeaconRoot[:]...) // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(r.Signer)) + dst = ssz.MarshalUint64(dst, uint64(p.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, r.ReceivedTime) + dst = ssz.MarshalTime(dst, p.ReceivedTime) - // Field (4) 'PreparedRound' - dst = ssz.MarshalUint64(dst, r.PreparedRound) + return +} - // Offset (5) 'PrepareMessages' +// UnmarshalSSZ ssz unmarshals the PartialSigMessageTrace object +func (p *PartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 56 { + return ssz.ErrSize + } + + // Field (0) 'Type' + p.Type = spectypes.PartialSigMsgType(ssz.UnmarshallUint64(buf[0:8])) + + // Field (1) 'BeaconRoot' + copy(p.BeaconRoot[:], buf[8:40]) + + // Field (2) 'Signer' + p.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + + // Field (3) 'ReceivedTime' + p.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the PartialSigMessageTrace object +func (p *PartialSigMessageTrace) SizeSSZ() (size int) { + size = 56 + return +} + +// HashTreeRoot ssz hashes the PartialSigMessageTrace object +func (p *PartialSigMessageTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the PartialSigMessageTrace object with a hasher +func (p *PartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { + indx := hh.Index() + + // Field (0) 'Type' + hh.PutUint64(uint64(p.Type)) + + // Field (1) 'BeaconRoot' + hh.PutBytes(p.BeaconRoot[:]) + + // Field (2) 'Signer' + hh.PutUint64(uint64(p.Signer)) + + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(p.ReceivedTime.Unix())) + + hh.Merkleize(indx) + return +} + +// GetTree ssz hashes the PartialSigMessageTrace object +func (p *PartialSigMessageTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) +} + +// MarshalSSZ ssz marshals the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) +} + +// MarshalSSZTo ssz marshals the CommitteeDutyTrace object to a target array +func (c *CommitteeDutyTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(120) + + // Offset (0) 'Rounds' dst = ssz.WriteOffset(dst, offset) - offset += len(r.PrepareMessages) * 56 + for ii := 0; ii < len(c.Rounds); ii++ { + offset += 4 + offset += c.Rounds[ii].SizeSSZ() + } - // Field (5) 'PrepareMessages' - if size := len(r.PrepareMessages); size > 13 { - err = ssz.ErrListTooBigFn("RoundChangeTrace.PrepareMessages", size, 13) + // Offset (1) 'Decideds' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(c.Decideds); ii++ { + offset += 4 + offset += c.Decideds[ii].SizeSSZ() + } + + // Field (2) 'Slot' + dst = ssz.MarshalUint64(dst, uint64(c.Slot)) + + // Offset (3) 'Post' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(c.Post); ii++ { + offset += 4 + offset += c.Post[ii].SizeSSZ() + } + + // Field (4) 'CommitteeID' + dst = append(dst, c.CommitteeID[:]...) + + // Offset (5) 'OperatorIDs' + dst = ssz.WriteOffset(dst, offset) + offset += len(c.OperatorIDs) * 8 + + // Field (6) 'AttestationDataRoot' + dst = append(dst, c.AttestationDataRoot[:]...) + + // Field (7) 'SyncCommitteeMessageRoot' + dst = append(dst, c.SyncCommitteeMessageRoot[:]...) + + // Field (0) 'Rounds' + if size := len(c.Rounds); size > 15 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Rounds", size, 15) return } - for ii := 0; ii < len(r.PrepareMessages); ii++ { - if dst, err = r.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { + { + offset = 4 * len(c.Rounds) + for ii := 0; ii < len(c.Rounds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Rounds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(c.Rounds); ii++ { + if dst, err = c.Rounds[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (1) 'Decideds' + if size := len(c.Decideds); size > 256 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Decideds", size, 256) + return + } + { + offset = 4 * len(c.Decideds) + for ii := 0; ii < len(c.Decideds); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Decideds[ii].SizeSSZ() + } + } + for ii := 0; ii < len(c.Decideds); ii++ { + if dst, err = c.Decideds[ii].MarshalSSZTo(dst); err != nil { return } } + // Field (3) 'Post' + if size := len(c.Post); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.Post", size, 13) + return + } + { + offset = 4 * len(c.Post) + for ii := 0; ii < len(c.Post); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += c.Post[ii].SizeSSZ() + } + } + for ii := 0; ii < len(c.Post); ii++ { + if dst, err = c.Post[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + // Field (5) 'OperatorIDs' + if size := len(c.OperatorIDs); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) + return + } + for ii := 0; ii < len(c.OperatorIDs); ii++ { + dst = ssz.MarshalUint64(dst, uint64(c.OperatorIDs[ii])) + } + return } -// UnmarshalSSZ ssz unmarshals the RoundChangeTrace object -func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 68 { + if size < 120 { return ssz.ErrSize } tail := buf - var o5 uint64 + var o0, o1, o3, o5 uint64 - // Field (0) 'Round' - r.Round = ssz.UnmarshallUint64(buf[0:8]) + // Offset (0) 'Rounds' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } - // Field (1) 'BeaconRoot' - copy(r.BeaconRoot[:], buf[8:40]) + if o0 < 120 { + return ssz.ErrInvalidVariableOffset + } - // Field (2) 'Signer' - r.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + // Offset (1) 'Decideds' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } - // Field (3) 'ReceivedTime' - r.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + // Field (2) 'Slot' + c.Slot = phase0.Slot(ssz.UnmarshallUint64(buf[8:16])) - // Field (4) 'PreparedRound' - r.PreparedRound = ssz.UnmarshallUint64(buf[56:64]) + // Offset (3) 'Post' + if o3 = ssz.ReadOffset(buf[16:20]); o3 > size || o1 > o3 { + return ssz.ErrOffset + } - // Offset (5) 'PrepareMessages' - if o5 = ssz.ReadOffset(buf[64:68]); o5 > size { + // Field (4) 'CommitteeID' + copy(c.CommitteeID[:], buf[20:52]) + + // Offset (5) 'OperatorIDs' + if o5 = ssz.ReadOffset(buf[52:56]); o5 > size || o3 > o5 { return ssz.ErrOffset } - if o5 < 68 { - return ssz.ErrInvalidVariableOffset + // Field (6) 'AttestationDataRoot' + copy(c.AttestationDataRoot[:], buf[56:88]) + + // Field (7) 'SyncCommitteeMessageRoot' + copy(c.SyncCommitteeMessageRoot[:], buf[88:120]) + + // Field (0) 'Rounds' + { + buf = tail[o0:o1] + num, err := ssz.DecodeDynamicLength(buf, 15) + if err != nil { + return err + } + c.Rounds = make([]*RoundTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Rounds[indx] == nil { + c.Rounds[indx] = new(RoundTrace) + } + if err = c.Rounds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } } - // Field (5) 'PrepareMessages' + // Field (1) 'Decideds' { - buf = tail[o5:] - num, err := ssz.DivideInt2(len(buf), 56, 13) + buf = tail[o1:o3] + num, err := ssz.DecodeDynamicLength(buf, 256) if err != nil { return err } - r.PrepareMessages = make([]*MessageTrace, num) - for ii := 0; ii < num; ii++ { - if r.PrepareMessages[ii] == nil { - r.PrepareMessages[ii] = new(MessageTrace) + c.Decideds = make([]*DecidedTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Decideds[indx] == nil { + c.Decideds[indx] = new(DecidedTrace) } - if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = c.Decideds[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + + // Field (3) 'Post' + { + buf = tail[o3:o5] + num, err := ssz.DecodeDynamicLength(buf, 13) + if err != nil { + return err + } + c.Post = make([]*CommitteePartialSigMessageTrace, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if c.Post[indx] == nil { + c.Post[indx] = new(CommitteePartialSigMessageTrace) + } + if err = c.Post[indx].UnmarshalSSZ(buf); err != nil { return err } + return nil + }) + if err != nil { + return err + } + } + + // Field (5) 'OperatorIDs' + { + buf = tail[o5:] + num, err := ssz.DivideInt2(len(buf), 8, 13) + if err != nil { + return err + } + c.OperatorIDs = ssz.ExtendUint64(c.OperatorIDs, num) + for ii := 0; ii < num; ii++ { + c.OperatorIDs[ii] = spectypes.OperatorID(ssz.UnmarshallUint64(buf[ii*8 : (ii+1)*8])) } } return err } -// SizeSSZ returns the ssz encoded size in bytes for the RoundChangeTrace object -func (r *RoundChangeTrace) SizeSSZ() (size int) { - size = 68 +// SizeSSZ returns the ssz encoded size in bytes for the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) SizeSSZ() (size int) { + size = 120 - // Field (5) 'PrepareMessages' - size += len(r.PrepareMessages) * 56 + // Field (0) 'Rounds' + for ii := 0; ii < len(c.Rounds); ii++ { + size += 4 + size += c.Rounds[ii].SizeSSZ() + } + + // Field (1) 'Decideds' + for ii := 0; ii < len(c.Decideds); ii++ { + size += 4 + size += c.Decideds[ii].SizeSSZ() + } + + // Field (3) 'Post' + for ii := 0; ii < len(c.Post); ii++ { + size += 4 + size += c.Post[ii].SizeSSZ() + } + + // Field (5) 'OperatorIDs' + size += len(c.OperatorIDs) * 8 return } -// HashTreeRoot ssz hashes the RoundChangeTrace object -func (r *RoundChangeTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(r) +// HashTreeRoot ssz hashes the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) } -// HashTreeRootWith ssz hashes the RoundChangeTrace object with a hasher -func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the CommitteeDutyTrace object with a hasher +func (c *CommitteeDutyTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Round' - hh.PutUint64(r.Round) - - // Field (1) 'BeaconRoot' - hh.PutBytes(r.BeaconRoot[:]) - - // Field (2) 'Signer' - hh.PutUint64(uint64(r.Signer)) + // Field (0) 'Rounds' + { + subIndx := hh.Index() + num := uint64(len(c.Rounds)) + if num > 15 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Rounds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 15) + } - // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(r.ReceivedTime.Unix())) + // Field (1) 'Decideds' + { + subIndx := hh.Index() + num := uint64(len(c.Decideds)) + if num > 256 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range c.Decideds { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 256) + } - // Field (4) 'PreparedRound' - hh.PutUint64(r.PreparedRound) + // Field (2) 'Slot' + hh.PutUint64(uint64(c.Slot)) - // Field (5) 'PrepareMessages' + // Field (3) 'Post' { subIndx := hh.Index() - num := uint64(len(r.PrepareMessages)) + num := uint64(len(c.Post)) if num > 13 { err = ssz.ErrIncorrectListSize return } - for _, elem := range r.PrepareMessages { + for _, elem := range c.Post { if err = elem.HashTreeRootWith(hh); err != nil { return } @@ -1451,73 +1658,72 @@ func (r *RoundChangeTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, num, 13) } + // Field (4) 'CommitteeID' + hh.PutBytes(c.CommitteeID[:]) + + // Field (5) 'OperatorIDs' + { + if size := len(c.OperatorIDs); size > 13 { + err = ssz.ErrListTooBigFn("CommitteeDutyTrace.OperatorIDs", size, 13) + return + } + subIndx := hh.Index() + for _, i := range c.OperatorIDs { + hh.AppendUint64(i) + } + hh.FillUpTo32() + numItems := uint64(len(c.OperatorIDs)) + hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) + } + + // Field (6) 'AttestationDataRoot' + hh.PutBytes(c.AttestationDataRoot[:]) + + // Field (7) 'SyncCommitteeMessageRoot' + hh.PutBytes(c.SyncCommitteeMessageRoot[:]) + hh.Merkleize(indx) return } -// GetTree ssz hashes the RoundChangeTrace object -func (r *RoundChangeTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(r) +// GetTree ssz hashes the CommitteeDutyTrace object +func (c *CommitteeDutyTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) } -// MarshalSSZ ssz marshals the ProposalTrace object -func (p *ProposalTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(p) +// MarshalSSZ ssz marshals the CommitteePartialSigMessageTrace object +func (c *CommitteePartialSigMessageTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(c) } -// MarshalSSZTo ssz marshals the ProposalTrace object to a target array -func (p *ProposalTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the CommitteePartialSigMessageTrace object to a target array +func (c *CommitteePartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(64) + offset := int(60) - // Field (0) 'Round' - dst = ssz.MarshalUint64(dst, p.Round) + // Field (0) 'Type' + dst = ssz.MarshalUint64(dst, uint64(c.Type)) // Field (1) 'BeaconRoot' - dst = append(dst, p.BeaconRoot[:]...) + dst = append(dst, c.BeaconRoot[:]...) // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(p.Signer)) + dst = ssz.MarshalUint64(dst, uint64(c.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, p.ReceivedTime) - - // Offset (4) 'RoundChanges' - dst = ssz.WriteOffset(dst, offset) - for ii := 0; ii < len(p.RoundChanges); ii++ { - offset += 4 - offset += p.RoundChanges[ii].SizeSSZ() - } + dst = ssz.MarshalTime(dst, c.ReceivedTime) - // Offset (5) 'PrepareMessages' + // Offset (4) 'Messages' dst = ssz.WriteOffset(dst, offset) - offset += len(p.PrepareMessages) * 56 - - // Field (4) 'RoundChanges' - if size := len(p.RoundChanges); size > 13 { - err = ssz.ErrListTooBigFn("ProposalTrace.RoundChanges", size, 13) - return - } - { - offset = 4 * len(p.RoundChanges) - for ii := 0; ii < len(p.RoundChanges); ii++ { - dst = ssz.WriteOffset(dst, offset) - offset += p.RoundChanges[ii].SizeSSZ() - } - } - for ii := 0; ii < len(p.RoundChanges); ii++ { - if dst, err = p.RoundChanges[ii].MarshalSSZTo(dst); err != nil { - return - } - } + offset += len(c.Messages) * 48 - // Field (5) 'PrepareMessages' - if size := len(p.PrepareMessages); size > 13 { - err = ssz.ErrListTooBigFn("ProposalTrace.PrepareMessages", size, 13) + // Field (4) 'Messages' + if size := len(c.Messages); size > 15 { + err = ssz.ErrListTooBigFn("CommitteePartialSigMessageTrace.Messages", size, 15) return } - for ii := 0; ii < len(p.PrepareMessages); ii++ { - if dst, err = p.PrepareMessages[ii].MarshalSSZTo(dst); err != nil { + for ii := 0; ii < len(c.Messages); ii++ { + if dst, err = c.Messages[ii].MarshalSSZTo(dst); err != nil { return } } @@ -1525,78 +1731,51 @@ func (p *ProposalTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { return } -// UnmarshalSSZ ssz unmarshals the ProposalTrace object -func (p *ProposalTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the CommitteePartialSigMessageTrace object +func (c *CommitteePartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 64 { + if size < 60 { return ssz.ErrSize } tail := buf - var o4, o5 uint64 + var o4 uint64 - // Field (0) 'Round' - p.Round = ssz.UnmarshallUint64(buf[0:8]) + // Field (0) 'Type' + c.Type = spectypes.PartialSigMsgType(ssz.UnmarshallUint64(buf[0:8])) // Field (1) 'BeaconRoot' - copy(p.BeaconRoot[:], buf[8:40]) + copy(c.BeaconRoot[:], buf[8:40]) // Field (2) 'Signer' - p.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) // Field (3) 'ReceivedTime' - p.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + c.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) - // Offset (4) 'RoundChanges' + // Offset (4) 'Messages' if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { return ssz.ErrOffset } - if o4 < 64 { + if o4 < 60 { return ssz.ErrInvalidVariableOffset } - // Offset (5) 'PrepareMessages' - if o5 = ssz.ReadOffset(buf[60:64]); o5 > size || o4 > o5 { - return ssz.ErrOffset - } - - // Field (4) 'RoundChanges' - { - buf = tail[o4:o5] - num, err := ssz.DecodeDynamicLength(buf, 13) - if err != nil { - return err - } - p.RoundChanges = make([]*RoundChangeTrace, num) - err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { - if p.RoundChanges[indx] == nil { - p.RoundChanges[indx] = new(RoundChangeTrace) - } - if err = p.RoundChanges[indx].UnmarshalSSZ(buf); err != nil { - return err - } - return nil - }) - if err != nil { - return err - } - } - - // Field (5) 'PrepareMessages' + // Field (4) 'Messages' { - buf = tail[o5:] - num, err := ssz.DivideInt2(len(buf), 56, 13) + buf = tail[o4:] + num, err := ssz.DivideInt2(len(buf), 48, 15) if err != nil { return err } - p.PrepareMessages = make([]*MessageTrace, num) + c.Messages = make([]*PartialSigMessage, num) for ii := 0; ii < num; ii++ { - if p.PrepareMessages[ii] == nil { - p.PrepareMessages[ii] = new(MessageTrace) + if c.Messages[ii] == nil { + c.Messages[ii] = new(PartialSigMessage) } - if err = p.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { + if err = c.Messages[ii].UnmarshalSSZ(buf[ii*48 : (ii+1)*48]); err != nil { return err } } @@ -1604,163 +1783,132 @@ func (p *ProposalTrace) UnmarshalSSZ(buf []byte) error { return err } -// SizeSSZ returns the ssz encoded size in bytes for the ProposalTrace object -func (p *ProposalTrace) SizeSSZ() (size int) { - size = 64 - - // Field (4) 'RoundChanges' - for ii := 0; ii < len(p.RoundChanges); ii++ { - size += 4 - size += p.RoundChanges[ii].SizeSSZ() - } +// SizeSSZ returns the ssz encoded size in bytes for the CommitteePartialSigMessageTrace object +func (c *CommitteePartialSigMessageTrace) SizeSSZ() (size int) { + size = 60 - // Field (5) 'PrepareMessages' - size += len(p.PrepareMessages) * 56 + // Field (4) 'Messages' + size += len(c.Messages) * 48 return } -// HashTreeRoot ssz hashes the ProposalTrace object -func (p *ProposalTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(p) +// HashTreeRoot ssz hashes the CommitteePartialSigMessageTrace object +func (c *CommitteePartialSigMessageTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(c) } -// HashTreeRootWith ssz hashes the ProposalTrace object with a hasher -func (p *ProposalTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the CommitteePartialSigMessageTrace object with a hasher +func (c *CommitteePartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Round' - hh.PutUint64(p.Round) + // Field (0) 'Type' + hh.PutUint64(uint64(c.Type)) // Field (1) 'BeaconRoot' - hh.PutBytes(p.BeaconRoot[:]) + hh.PutBytes(c.BeaconRoot[:]) // Field (2) 'Signer' - hh.PutUint64(uint64(p.Signer)) + hh.PutUint64(uint64(c.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(p.ReceivedTime.Unix())) - - // Field (4) 'RoundChanges' - { - subIndx := hh.Index() - num := uint64(len(p.RoundChanges)) - if num > 13 { - err = ssz.ErrIncorrectListSize - return - } - for _, elem := range p.RoundChanges { - if err = elem.HashTreeRootWith(hh); err != nil { - return - } - } - hh.MerkleizeWithMixin(subIndx, num, 13) - } + hh.PutUint64(uint64(c.ReceivedTime.Unix())) - // Field (5) 'PrepareMessages' + // Field (4) 'Messages' { subIndx := hh.Index() - num := uint64(len(p.PrepareMessages)) - if num > 13 { + num := uint64(len(c.Messages)) + if num > 15 { err = ssz.ErrIncorrectListSize return } - for _, elem := range p.PrepareMessages { + for _, elem := range c.Messages { if err = elem.HashTreeRootWith(hh); err != nil { return } } - hh.MerkleizeWithMixin(subIndx, num, 13) + hh.MerkleizeWithMixin(subIndx, num, 15) } hh.Merkleize(indx) return } -// GetTree ssz hashes the ProposalTrace object -func (p *ProposalTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(p) +// GetTree ssz hashes the CommitteePartialSigMessageTrace object +func (c *CommitteePartialSigMessageTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(c) } -// MarshalSSZ ssz marshals the MessageTrace object -func (m *MessageTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(m) +// MarshalSSZ ssz marshals the PartialSigMessage object +func (p *PartialSigMessage) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) } -// MarshalSSZTo ssz marshals the MessageTrace object to a target array -func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the PartialSigMessage object to a target array +func (p *PartialSigMessage) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - // Field (0) 'Round' - dst = ssz.MarshalUint64(dst, m.Round) - - // Field (1) 'BeaconRoot' - dst = append(dst, m.BeaconRoot[:]...) + // Field (0) 'BeaconRoot' + dst = append(dst, p.BeaconRoot[:]...) - // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(m.Signer)) + // Field (1) 'Signer' + dst = ssz.MarshalUint64(dst, uint64(p.Signer)) - // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, m.ReceivedTime) + // Field (2) 'ValidatorIndex' + dst = ssz.MarshalUint64(dst, uint64(p.ValidatorIndex)) return } -// UnmarshalSSZ ssz unmarshals the MessageTrace object -func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the PartialSigMessage object +func (p *PartialSigMessage) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size != 56 { + if size != 48 { return ssz.ErrSize } - // Field (0) 'Round' - m.Round = ssz.UnmarshallUint64(buf[0:8]) - - // Field (1) 'BeaconRoot' - copy(m.BeaconRoot[:], buf[8:40]) + // Field (0) 'BeaconRoot' + copy(p.BeaconRoot[:], buf[0:32]) - // Field (2) 'Signer' - m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + // Field (1) 'Signer' + p.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[32:40])) - // Field (3) 'ReceivedTime' - m.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + // Field (2) 'ValidatorIndex' + p.ValidatorIndex = phase0.ValidatorIndex(ssz.UnmarshallUint64(buf[40:48])) return err } -// SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object -func (m *MessageTrace) SizeSSZ() (size int) { - size = 56 +// SizeSSZ returns the ssz encoded size in bytes for the PartialSigMessage object +func (p *PartialSigMessage) SizeSSZ() (size int) { + size = 48 return } -// HashTreeRoot ssz hashes the MessageTrace object -func (m *MessageTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(m) +// HashTreeRoot ssz hashes the PartialSigMessage object +func (p *PartialSigMessage) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) } -// HashTreeRootWith ssz hashes the MessageTrace object with a hasher -func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the PartialSigMessage object with a hasher +func (p *PartialSigMessage) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() - // Field (0) 'Round' - hh.PutUint64(m.Round) - - // Field (1) 'BeaconRoot' - hh.PutBytes(m.BeaconRoot[:]) + // Field (0) 'BeaconRoot' + hh.PutBytes(p.BeaconRoot[:]) - // Field (2) 'Signer' - hh.PutUint64(uint64(m.Signer)) + // Field (1) 'Signer' + hh.PutUint64(uint64(p.Signer)) - // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(m.ReceivedTime.Unix())) + // Field (2) 'ValidatorIndex' + hh.PutUint64(uint64(p.ValidatorIndex)) hh.Merkleize(indx) return } -// GetTree ssz hashes the MessageTrace object -func (m *MessageTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(m) +// GetTree ssz hashes the PartialSigMessage object +func (p *PartialSigMessage) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(p) } diff --git a/exporter/v2/store/store_test.go b/exporter/v2/store/store_test.go index 47a75cd1ae..895f11d5a1 100644 --- a/exporter/v2/store/store_test.go +++ b/exporter/v2/store/store_test.go @@ -69,9 +69,7 @@ func TestSaveValidatorDutyTrace(t *testing.T) { func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { return &model.ValidatorDutyTrace{ - DutyTrace: model.DutyTrace{ - Slot: slot, - }, + Slot: slot, Role: types.BNRoleAttester, Validator: phase0.ValidatorIndex(39393), } @@ -79,9 +77,7 @@ func makeVTrace(slot phase0.Slot) *model.ValidatorDutyTrace { func makeCTrace(slot phase0.Slot) *model.CommitteeDutyTrace { return &model.CommitteeDutyTrace{ - DutyTrace: model.DutyTrace{ - Slot: slot, - }, + Slot: slot, CommitteeID: [32]byte{'a'}, OperatorIDs: nil, AttestationDataRoot: [32]byte{}, diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 10f17397cf..c36f739627 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -1,16 +1,15 @@ package validator import ( + "encoding/hex" "sync" "time" - "github.com/attestantio/go-eth2-client/spec/phase0" "go.uber.org/zap" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" model "github.com/ssvlabs/ssv/exporter/v2" - "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" ) @@ -18,50 +17,48 @@ type InMemTracer struct { sync.Mutex logger *zap.Logger // consider having the validator pubkey before of the slot - validatorTraces map[uint64]map[string]*model.ValidatorDutyTrace + validatorTraces map[uint64]map[string]*validatorDutyTrace } func NewTracer(logger *zap.Logger) *InMemTracer { return &InMemTracer{ logger: logger, - validatorTraces: make(map[uint64]map[string]*model.ValidatorDutyTrace), + validatorTraces: make(map[uint64]map[string]*validatorDutyTrace), } } -func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *model.ValidatorDutyTrace { +func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *validatorDutyTrace { n.Lock() defer n.Unlock() mp, ok := n.validatorTraces[slot] if !ok { - mp = make(map[string]*model.ValidatorDutyTrace) + mp = make(map[string]*validatorDutyTrace) n.validatorTraces[slot] = mp } trace, ok := mp[vPubKey] if !ok { - trace = new(model.ValidatorDutyTrace) + trace = new(validatorDutyTrace) mp[vPubKey] = trace } return trace } -func getRound(trace *model.ValidatorDutyTrace, round uint64) *model.RoundTrace { +func getRound(trace *validatorDutyTrace, round uint64) *round { trace.Lock() defer trace.Unlock() - var count = uint64(len(trace.Rounds)) - for round+1 > count { - var r model.RoundTrace - trace.Rounds = append(trace.Rounds, &r) - count = uint64(len(trace.Rounds)) + var count = len(trace.Rounds) + for round+1 > uint64(count) { //nolint:gosec + count = trace.addRound() } - return trace.Rounds[round] + return trace.getRound(round) } -// id -> validator or committee id +// HOW TO? id -> validator or committee id func getOperators(id string) []spectypes.OperatorID { return []spectypes.OperatorID{} } @@ -181,15 +178,23 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV switch msg.MsgType { case specqbft.ProposalMsgType: - var data = new(spectypes.ValidatorConsensusData) - err := data.Decode(signedMsg.FullData) - if err != nil { - n.logger.Error("failed to decode proposal data", zap.Error(err)) + msgID := signedMsg.SSVMessage.GetID() + switch msgID.GetRoleType() { + case spectypes.RoleCommittee: + n.logger.Info("qbft proposal for committee duty: to be implemented") + default: + var data = new(spectypes.ValidatorConsensusData) + err := data.Decode(signedMsg.FullData) + if err != nil { + n.logger.Error("failed to decode proposal data", zap.Error(err)) + } + // beacon vote (for committee duty) + trace.Lock() + trace.Validator = data.Duty.ValidatorIndex + trace.Unlock() + + round.ProposalTrace = createProposalTrace(msg, signedMsg) } - // beacon vote (for committee duty) - - trace.Validator = data.Duty.ValidatorIndex - round.ProposalTrace = createProposalTrace(msg, signedMsg) case specqbft.PrepareMsgType: var m = new(model.MessageTrace) @@ -221,48 +226,52 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) } -func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages) { +func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey string) { slot := uint64(msg.Slot) - fields := []zap.Field{ - fields.Slot(phase0.Slot(slot)), - } + trace := n.getTrace(slot, validatorPubKey) + trace.Lock() + defer trace.Unlock() - validatorID := string("TODO") + if trace.Validator == 0 { + trace.Validator = msg.Messages[0].ValidatorIndex + } - trace := n.getTrace(slot, validatorID) + switch ssvMsg.MsgID.GetRoleType() { + case spectypes.RoleCommittee: + n.logger.Warn("unexpected committee duty") // we get this every slot + case spectypes.RoleProposer: + trace.Role = spectypes.BNRoleProposer + case spectypes.RoleAggregator: + trace.Role = spectypes.BNRoleAggregator + case spectypes.RoleSyncCommitteeContribution: + trace.Role = spectypes.BNRoleSyncCommitteeContribution + case spectypes.RoleValidatorRegistration: + trace.Role = spectypes.BNRoleValidatorRegistration + case spectypes.RoleVoluntaryExit: + trace.Role = spectypes.BNRoleVoluntaryExit + } - fields = append(fields, zap.Int("messages", len(msg.Messages))) - fields = append(fields, zap.Int("duty rounds", len(trace.Rounds))) + fields := []zap.Field{ + zap.Uint64("slot", slot), + zap.String("validator", hex.EncodeToString([]byte(validatorPubKey[len(validatorPubKey)-4:]))), + zap.String("type", trace.Role.String()), + } - r := uint64(0) // TODO - round := getRound(trace, r) - round.Lock() - defer round.Unlock() + n.logger.Info("signed", fields...) - // Q: how to map Message to RoundTrace? - for _, pSigMsg := range msg.Messages { - round.Proposer = pSigMsg.Signer - _ = pSigMsg.ValidatorIndex - } + var tr model.PartialSigMessageTrace + tr.Type = msg.Type + tr.BeaconRoot = msg.Messages[0].SigningRoot + tr.Signer = msg.Messages[0].Signer + tr.ReceivedTime = time.Now() - // Q: usage of msg.Type? - switch msg.Type { - case spectypes.PostConsensusPartialSig: - fields = append(fields, zap.String("messageType", "post consensus")) - case spectypes.ContributionProofs: - fields = append(fields, zap.String("messageType", "contribution proofs")) - case spectypes.RandaoPartialSig: - fields = append(fields, zap.String("messageType", "randao")) - case spectypes.SelectionProofPartialSig: - fields = append(fields, zap.String("messageType", "selection proof")) - case spectypes.ValidatorRegistrationPartialSig: - fields = append(fields, zap.String("messageType", "validator registration")) - case spectypes.VoluntaryExitPartialSig: - fields = append(fields, zap.String("messageType", "voluntary exit")) + if msg.Type == spectypes.PostConsensusPartialSig { + trace.Post = append(trace.Post, &tr) + return } - n.logger.Info("signed", fields...) + trace.Pre = append(trace.Pre, &tr) } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { @@ -273,15 +282,39 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { } case spectypes.SSVPartialSignatureMsgType: pSigMessages := new(spectypes.PartialSignatureMessages) - signedMsg := msg.SignedSSVMessage - ssvMsg := signedMsg.SSVMessage + err := pSigMessages.Decode(msg.SignedSSVMessage.SSVMessage.GetData()) + if err != nil { + n.logger.Error("failed to decode partial signature messages", zap.Error(err)) + return + } + if msg.MsgID.GetRoleType() != spectypes.RoleCommittee { + validatorPubKey := msg.MsgID.GetDutyExecutorID() + n.signed(pSigMessages, msg, string(validatorPubKey)) + } else { // to be refined + committeeID := msg.MsgID.GetDutyExecutorID() + n.signed(pSigMessages, msg, string(committeeID)) + } + } +} + +type validatorDutyTrace struct { + sync.Mutex + model.ValidatorDutyTrace +} - _ = signedMsg.OperatorIDs - _ = signedMsg.Signatures +func (t *validatorDutyTrace) addRound() int { + t.Rounds = append(t.Rounds, &model.RoundTrace{}) + return len(t.Rounds) +} - err := pSigMessages.Decode(ssvMsg.GetData()) - if err == nil { - n.signed(pSigMessages) - } +func (t *validatorDutyTrace) getRound(rnd uint64) *round { + r := t.Rounds[rnd] + return &round{ + RoundTrace: *r, } } + +type round struct { + sync.Mutex + model.RoundTrace +} From a4595d406afad4f99c626dfee56977fd93789be1 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 11:04:01 +0000 Subject: [PATCH 12/24] locking fix --- operator/validator/dutytracer.go | 78 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index c36f739627..35d52847d1 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -46,18 +46,6 @@ func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *validatorDutyTrace return trace } -func getRound(trace *validatorDutyTrace, round uint64) *round { - trace.Lock() - defer trace.Unlock() - - var count = len(trace.Rounds) - for round+1 > uint64(count) { //nolint:gosec - count = trace.addRound() - } - - return trace.getRound(round) -} - // HOW TO? id -> validator or committee id func getOperators(id string) []spectypes.OperatorID { return []spectypes.OperatorID{} @@ -77,19 +65,21 @@ func (n *InMemTracer) toMockState(msg *specqbft.Message, operatorIDs []spectypes return mockState } -func decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTrace { +func (n *InMemTracer) decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTrace { var traces = make([]*model.MessageTrace, 0, len(justifications)) for _, rcj := range justifications { var signedMsg = new(spectypes.SignedSSVMessage) err := signedMsg.Decode(rcj) if err != nil { - // n.logger.Error("failed to decode round change justification", zap.Error(err)) + n.logger.Error("failed to decode round change justification", zap.Error(err)) + continue } var qbftMsg = new(specqbft.Message) err = qbftMsg.Decode(signedMsg.SSVMessage.GetData()) if err != nil { - // n.logger.Error("failed to decode round change justification", zap.Error(err)) + n.logger.Error("failed to decode round change justification", zap.Error(err)) + continue } var justificationTrace = new(model.MessageTrace) @@ -102,32 +92,33 @@ func decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTr return traces } -func decodeJustificationWithRoundChanges(justifications [][]byte) []*model.RoundChangeTrace { +func (n *InMemTracer) decodeJustificationWithRoundChanges(justifications [][]byte) []*model.RoundChangeTrace { var traces = make([]*model.RoundChangeTrace, 0, len(justifications)) for _, rcj := range justifications { - var signedMsg = new(spectypes.SignedSSVMessage) err := signedMsg.Decode(rcj) if err != nil { - // n.logger.Error("failed to decode round change justification", zap.Error(err)) + n.logger.Error("failed to decode round change justification", zap.Error(err)) + continue } var qbftMsg = new(specqbft.Message) err = qbftMsg.Decode(signedMsg.SSVMessage.GetData()) if err != nil { - // n.logger.Error("failed to decode round change justification", zap.Error(err)) + n.logger.Error("failed to decode round change justification", zap.Error(err)) + continue } - var receivedTime time.Time // we can't know the time when the sender received the message + var receivedTime time.Time // zero value becausewe can't know the time when the sender received the message - var roundChangeTrace = createRoundChangeTrace(qbftMsg, signedMsg, receivedTime) + var roundChangeTrace = n.createRoundChangeTrace(qbftMsg, signedMsg, receivedTime) traces = append(traces, roundChangeTrace) } return traces } -func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, receivedTime time.Time) *model.RoundChangeTrace { +func (n *InMemTracer) createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, receivedTime time.Time) *model.RoundChangeTrace { var roundChangeTrace = new(model.RoundChangeTrace) roundChangeTrace.PreparedRound = uint64(msg.DataRound) roundChangeTrace.Round = uint64(msg.Round) @@ -135,20 +126,20 @@ func createRoundChangeTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSS roundChangeTrace.Signer = signedMsg.OperatorIDs[0] roundChangeTrace.ReceivedTime = receivedTime - roundChangeTrace.PrepareMessages = decodeJustificationWithPrepares(msg.RoundChangeJustification) + roundChangeTrace.PrepareMessages = n.decodeJustificationWithPrepares(msg.RoundChangeJustification) return roundChangeTrace } -func createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) *model.ProposalTrace { +func (n *InMemTracer) createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) *model.ProposalTrace { var proposalTrace = new(model.ProposalTrace) proposalTrace.Round = uint64(msg.Round) proposalTrace.BeaconRoot = msg.Root proposalTrace.Signer = signedMsg.OperatorIDs[0] proposalTrace.ReceivedTime = time.Now() // correct - proposalTrace.RoundChanges = decodeJustificationWithRoundChanges(msg.RoundChangeJustification) - proposalTrace.PrepareMessages = decodeJustificationWithPrepares(msg.PrepareJustification) + proposalTrace.RoundChanges = n.decodeJustificationWithRoundChanges(msg.RoundChangeJustification) + proposalTrace.PrepareMessages = n.decodeJustificationWithPrepares(msg.PrepareJustification) return proposalTrace } @@ -156,7 +147,6 @@ func createProposalTrace(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMe func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { slot := uint64(msg.Height) - // validator pubkey msgID := spectypes.MessageID(msg.Identifier[:]) // validator + pubkey + role + network validatorPubKey := msgID.GetDutyExecutorID() // validator pubkey or committee id validatorID := string(validatorPubKey) @@ -164,8 +154,7 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV // get or create trace trace := n.getTrace(slot, validatorID) - // first round is 1 - var round = getRound(trace, uint64(msg.Round)) + var round = trace.getRound(uint64(msg.Round)) round.Lock() defer round.Unlock() @@ -187,13 +176,14 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV err := data.Decode(signedMsg.FullData) if err != nil { n.logger.Error("failed to decode proposal data", zap.Error(err)) + return } // beacon vote (for committee duty) trace.Lock() trace.Validator = data.Duty.ValidatorIndex trace.Unlock() - round.ProposalTrace = createProposalTrace(msg, signedMsg) + round.ProposalTrace = n.createProposalTrace(msg, signedMsg) } case specqbft.PrepareMsgType: @@ -218,7 +208,7 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV // optional - only if round change is proposing a value now := time.Now() - roundChangeTrace := createRoundChangeTrace(msg, signedMsg, now) + roundChangeTrace := n.createRoundChangeTrace(msg, signedMsg, now) round.RoundChanges = append(round.RoundChanges, roundChangeTrace) } @@ -297,24 +287,28 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { } } +type round struct { + sync.Mutex + model.RoundTrace +} + type validatorDutyTrace struct { sync.Mutex model.ValidatorDutyTrace } -func (t *validatorDutyTrace) addRound() int { - t.Rounds = append(t.Rounds, &model.RoundTrace{}) - return len(t.Rounds) -} +func (trace *validatorDutyTrace) getRound(rnd uint64) *round { + trace.Lock() + defer trace.Unlock() + + var count = len(trace.Rounds) + for rnd+1 > uint64(count) { //nolint:gosec + trace.Rounds = append(trace.Rounds, &model.RoundTrace{}) + count = len(trace.Rounds) + } -func (t *validatorDutyTrace) getRound(rnd uint64) *round { - r := t.Rounds[rnd] + r := trace.Rounds[rnd] return &round{ RoundTrace: *r, } } - -type round struct { - sync.Mutex - model.RoundTrace -} From d06ab54f6399b87ef4d7353a2bcae1929716282a Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 14:38:42 +0000 Subject: [PATCH 13/24] copy bugfix --- operator/validator/dutytracer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 35d52847d1..447c2da757 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -289,7 +289,7 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { type round struct { sync.Mutex - model.RoundTrace + *model.RoundTrace } type validatorDutyTrace struct { @@ -309,6 +309,6 @@ func (trace *validatorDutyTrace) getRound(rnd uint64) *round { r := trace.Rounds[rnd] return &round{ - RoundTrace: *r, + RoundTrace: r, } } From 5dbed05e41a42efae5dd0538e3d84570a5551ca2 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 15:20:47 +0000 Subject: [PATCH 14/24] breakdown committee validator wip --- exporter/v2/model.go | 11 +---- operator/validator/dutytracer.go | 85 ++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index db2c813b6b..8c0c833c7a 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -82,19 +82,10 @@ type CommitteeDutyTrace struct { SyncCommitteeMessageRoot phase0.Root `ssz-size:"32"` } -// where used? -type CommitteeMessageTrace struct { - BeaconRoot []phase0.Root `ssz-max:"1500" ssz-size:"32"` - Validators []phase0.ValidatorIndex `ssz-max:"1500"` - - Signer spectypes.OperatorID - ReceivedTime time.Time -} - type CommitteePartialSigMessageTrace struct { PartialSigMessageTrace - Messages []*PartialSigMessage `ssz-max:"15"` // TODO confirm + Messages []*PartialSigMessage `ssz-max:"1512"` } type PartialSigMessage struct { diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 447c2da757..ab364e2b92 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -18,12 +18,14 @@ type InMemTracer struct { logger *zap.Logger // consider having the validator pubkey before of the slot validatorTraces map[uint64]map[string]*validatorDutyTrace + committeeTraces map[uint64]map[string]*committeeDutyTrace } func NewTracer(logger *zap.Logger) *InMemTracer { return &InMemTracer{ logger: logger, validatorTraces: make(map[uint64]map[string]*validatorDutyTrace), + committeeTraces: make(map[uint64]map[string]*committeeDutyTrace), } } @@ -46,6 +48,25 @@ func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *validatorDutyTrace return trace } +func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID string) *committeeDutyTrace { + n.Lock() + defer n.Unlock() + + mp, ok := n.committeeTraces[slot] + if !ok { + mp = make(map[string]*committeeDutyTrace) + n.committeeTraces[slot] = mp + } + + trace, ok := mp[committeeID] + if !ok { + trace = new(committeeDutyTrace) + mp[committeeID] = trace + } + + return trace +} + // HOW TO? id -> validator or committee id func getOperators(id string) []spectypes.OperatorID { return []spectypes.OperatorID{} @@ -144,7 +165,7 @@ func (n *InMemTracer) createProposalTrace(msg *specqbft.Message, signedMsg *spec return proposalTrace } -func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { +func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { slot := uint64(msg.Height) msgID := spectypes.MessageID(msg.Identifier[:]) // validator + pubkey + role + network @@ -216,7 +237,7 @@ func (n *InMemTracer) qbft(msg *specqbft.Message, signedMsg *spectypes.SignedSSV // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) } -func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey string) { +func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey string) { slot := uint64(msg.Slot) trace := n.getTrace(slot, validatorPubKey) @@ -229,7 +250,7 @@ func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages, ssvMsg *qu switch ssvMsg.MsgID.GetRoleType() { case spectypes.RoleCommittee: - n.logger.Warn("unexpected committee duty") // we get this every slot + n.logger.Warn("unexpected committee duty") // we get this often case spectypes.RoleProposer: trace.Role = spectypes.BNRoleProposer case spectypes.RoleAggregator: @@ -248,7 +269,7 @@ func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages, ssvMsg *qu zap.String("type", trace.Role.String()), } - n.logger.Info("signed", fields...) + n.logger.Info("signed validator", fields...) var tr model.PartialSigMessageTrace tr.Type = msg.Type @@ -264,11 +285,46 @@ func (n *InMemTracer) signed(msg *spectypes.PartialSignatureMessages, ssvMsg *qu trace.Pre = append(trace.Pre, &tr) } +func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, committeeID string) { + slot := uint64(msg.Slot) + + trace := n.getCommitteeTrace(slot, committeeID) + trace.Lock() + defer trace.Unlock() + + //trace.CommitteeID = [32]byte(committeeID[:]) //fixme + trace.OperatorIDs = getOperators(committeeID) + trace.Slot = msg.Slot + + var cTrace model.CommitteePartialSigMessageTrace + cTrace.Type = msg.Type + cTrace.Signer = msg.Messages[0].Signer + cTrace.ReceivedTime = time.Now() + + for _, partialSigMsg := range msg.Messages { + tr := model.PartialSigMessage{} + tr.BeaconRoot = partialSigMsg.SigningRoot + tr.Signer = partialSigMsg.Signer + tr.ValidatorIndex = partialSigMsg.ValidatorIndex + cTrace.Messages = append(cTrace.Messages, &tr) + } + + trace.Post = append(trace.Post, &cTrace) + + fields := []zap.Field{ + zap.Uint64("slot", slot), + } + + n.logger.Info("signed committee", fields...) + + // tbc +} + func (n *InMemTracer) Trace(msg *queue.SSVMessage) { switch msg.MsgType { case spectypes.SSVConsensusMsgType: if subMsg, ok := msg.Body.(*specqbft.Message); ok { - n.qbft(subMsg, msg.SignedSSVMessage) + n.processConsensus(subMsg, msg.SignedSSVMessage) } case spectypes.SSVPartialSignatureMsgType: pSigMessages := new(spectypes.PartialSignatureMessages) @@ -277,13 +333,15 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { n.logger.Error("failed to decode partial signature messages", zap.Error(err)) return } - if msg.MsgID.GetRoleType() != spectypes.RoleCommittee { - validatorPubKey := msg.MsgID.GetDutyExecutorID() - n.signed(pSigMessages, msg, string(validatorPubKey)) - } else { // to be refined + + if msg.MsgID.GetRoleType() == spectypes.RoleCommittee { committeeID := msg.MsgID.GetDutyExecutorID() - n.signed(pSigMessages, msg, string(committeeID)) + n.processPartialSigCommittee(pSigMessages, msg, string(committeeID)) + return } + + validatorPubKey := msg.MsgID.GetDutyExecutorID() + n.processPartialSigValidator(pSigMessages, msg, string(validatorPubKey)) } } @@ -312,3 +370,10 @@ func (trace *validatorDutyTrace) getRound(rnd uint64) *round { RoundTrace: r, } } + +// committee + +type committeeDutyTrace struct { + sync.Mutex + model.CommitteeDutyTrace +} From 6e9573c21ca71dd041e6796bc86a3dfd67cfad11 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 15:50:02 +0000 Subject: [PATCH 15/24] CHECKPOINT (locking wip) --- exporter/v2/model.go | 7 +- exporter/v2/model_encoding.go | 73 +++++++++----------- operator/validator/dutytracer.go | 110 ++++++++++++++++++------------- 3 files changed, 101 insertions(+), 89 deletions(-) diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 8c0c833c7a..667574fea8 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -83,9 +83,10 @@ type CommitteeDutyTrace struct { } type CommitteePartialSigMessageTrace struct { - PartialSigMessageTrace - - Messages []*PartialSigMessage `ssz-max:"1512"` + Type spectypes.PartialSigMsgType + Signer spectypes.OperatorID + Messages []*PartialSigMessage `ssz-max:"1512"` + ReceivedTime time.Time } type PartialSigMessage struct { diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 5dda947516..043ab7e7a2 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 02e1063c48ea6cbe2c9f3645e11f00a9b7dfea58e6f937545867db8fd98a39dc +// Hash: 242393fc08a3f8ce75ba086caea6ae0776ed76953dbe711dc3d5e6cc664ff59a // Version: 0.1.3 package exporter @@ -1699,27 +1699,24 @@ func (c *CommitteePartialSigMessageTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the CommitteePartialSigMessageTrace object to a target array func (c *CommitteePartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(60) + offset := int(28) // Field (0) 'Type' dst = ssz.MarshalUint64(dst, uint64(c.Type)) - // Field (1) 'BeaconRoot' - dst = append(dst, c.BeaconRoot[:]...) - - // Field (2) 'Signer' + // Field (1) 'Signer' dst = ssz.MarshalUint64(dst, uint64(c.Signer)) - // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, c.ReceivedTime) - - // Offset (4) 'Messages' + // Offset (2) 'Messages' dst = ssz.WriteOffset(dst, offset) offset += len(c.Messages) * 48 - // Field (4) 'Messages' - if size := len(c.Messages); size > 15 { - err = ssz.ErrListTooBigFn("CommitteePartialSigMessageTrace.Messages", size, 15) + // Field (3) 'ReceivedTime' + dst = ssz.MarshalTime(dst, c.ReceivedTime) + + // Field (2) 'Messages' + if size := len(c.Messages); size > 1512 { + err = ssz.ErrListTooBigFn("CommitteePartialSigMessageTrace.Messages", size, 1512) return } for ii := 0; ii < len(c.Messages); ii++ { @@ -1735,38 +1732,35 @@ func (c *CommitteePartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, func (c *CommitteePartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 60 { + if size < 28 { return ssz.ErrSize } tail := buf - var o4 uint64 + var o2 uint64 // Field (0) 'Type' c.Type = spectypes.PartialSigMsgType(ssz.UnmarshallUint64(buf[0:8])) - // Field (1) 'BeaconRoot' - copy(c.BeaconRoot[:], buf[8:40]) - - // Field (2) 'Signer' - c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) - - // Field (3) 'ReceivedTime' - c.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + // Field (1) 'Signer' + c.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[8:16])) - // Offset (4) 'Messages' - if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + // Offset (2) 'Messages' + if o2 = ssz.ReadOffset(buf[16:20]); o2 > size { return ssz.ErrOffset } - if o4 < 60 { + if o2 < 28 { return ssz.ErrInvalidVariableOffset } - // Field (4) 'Messages' + // Field (3) 'ReceivedTime' + c.ReceivedTime = ssz.UnmarshalTime(buf[20:28]) + + // Field (2) 'Messages' { - buf = tail[o4:] - num, err := ssz.DivideInt2(len(buf), 48, 15) + buf = tail[o2:] + num, err := ssz.DivideInt2(len(buf), 48, 1512) if err != nil { return err } @@ -1785,9 +1779,9 @@ func (c *CommitteePartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the CommitteePartialSigMessageTrace object func (c *CommitteePartialSigMessageTrace) SizeSSZ() (size int) { - size = 60 + size = 28 - // Field (4) 'Messages' + // Field (2) 'Messages' size += len(c.Messages) * 48 return @@ -1805,20 +1799,14 @@ func (c *CommitteePartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (e // Field (0) 'Type' hh.PutUint64(uint64(c.Type)) - // Field (1) 'BeaconRoot' - hh.PutBytes(c.BeaconRoot[:]) - - // Field (2) 'Signer' + // Field (1) 'Signer' hh.PutUint64(uint64(c.Signer)) - // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(c.ReceivedTime.Unix())) - - // Field (4) 'Messages' + // Field (2) 'Messages' { subIndx := hh.Index() num := uint64(len(c.Messages)) - if num > 15 { + if num > 1512 { err = ssz.ErrIncorrectListSize return } @@ -1827,9 +1815,12 @@ func (c *CommitteePartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (e return } } - hh.MerkleizeWithMixin(subIndx, num, 15) + hh.MerkleizeWithMixin(subIndx, num, 1512) } + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(c.ReceivedTime.Unix())) + hh.Merkleize(indx) return } diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index ab364e2b92..acfc38b475 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -29,7 +29,7 @@ func NewTracer(logger *zap.Logger) *InMemTracer { } } -func (n *InMemTracer) getTrace(slot uint64, vPubKey string) *validatorDutyTrace { +func (n *InMemTracer) getValidatorTrace(slot uint64, vPubKey string) *validatorDutyTrace { n.Lock() defer n.Unlock() @@ -165,47 +165,10 @@ func (n *InMemTracer) createProposalTrace(msg *specqbft.Message, signedMsg *spec return proposalTrace } -func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage) { - slot := uint64(msg.Height) - - msgID := spectypes.MessageID(msg.Identifier[:]) // validator + pubkey + role + network - validatorPubKey := msgID.GetDutyExecutorID() // validator pubkey or committee id - validatorID := string(validatorPubKey) - - // get or create trace - trace := n.getTrace(slot, validatorID) - - var round = trace.getRound(uint64(msg.Round)) - round.Lock() - defer round.Unlock() - - // proposer - operatorIDs := getOperators(validatorID) - if len(operatorIDs) > 0 { - var mockState = n.toMockState(msg, operatorIDs) - round.Proposer = specqbft.RoundRobinProposer(mockState, msg.Round) - } - +func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, trace *model.ConsensusTrace, round *round) { switch msg.MsgType { case specqbft.ProposalMsgType: - msgID := signedMsg.SSVMessage.GetID() - switch msgID.GetRoleType() { - case spectypes.RoleCommittee: - n.logger.Info("qbft proposal for committee duty: to be implemented") - default: - var data = new(spectypes.ValidatorConsensusData) - err := data.Decode(signedMsg.FullData) - if err != nil { - n.logger.Error("failed to decode proposal data", zap.Error(err)) - return - } - // beacon vote (for committee duty) - trace.Lock() - trace.Validator = data.Duty.ValidatorIndex - trace.Unlock() - - round.ProposalTrace = n.createProposalTrace(msg, signedMsg) - } + round.ProposalTrace = n.createProposalTrace(msg, signedMsg) case specqbft.PrepareMsgType: var m = new(model.MessageTrace) @@ -240,7 +203,7 @@ func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectyp func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey string) { slot := uint64(msg.Slot) - trace := n.getTrace(slot, validatorPubKey) + trace := n.getValidatorTrace(slot, validatorPubKey) trace.Lock() defer trace.Unlock() @@ -250,7 +213,7 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature switch ssvMsg.MsgID.GetRoleType() { case spectypes.RoleCommittee: - n.logger.Warn("unexpected committee duty") // we get this often + n.logger.Error("unexpected committee duty") case spectypes.RoleProposer: trace.Role = spectypes.BNRoleProposer case spectypes.RoleAggregator: @@ -316,15 +279,63 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature } n.logger.Info("signed committee", fields...) - - // tbc } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { switch msg.MsgType { case spectypes.SSVConsensusMsgType: if subMsg, ok := msg.Body.(*specqbft.Message); ok { - n.processConsensus(subMsg, msg.SignedSSVMessage) + slot := uint64(subMsg.Height) + msgID := spectypes.MessageID(subMsg.Identifier[:]) // validator + pubkey + role + network + executorID := msgID.GetDutyExecutorID() // validator pubkey or committee id + + switch msgID.GetRoleType() { + case spectypes.RoleCommittee: + commiteeID := string(executorID) + trace := n.getCommitteeTrace(slot, commiteeID) + trace.Lock() // take a second look here (locking trace vs round etc) + defer trace.Unlock() + + var round = trace.getRound(uint64(subMsg.Round)) + round.Lock() + defer round.Unlock() + + // populate proposer + operatorIDs := getOperators(commiteeID) + if len(operatorIDs) > 0 { + var mockState = n.toMockState(subMsg, operatorIDs) + round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) + } + + n.processConsensus(subMsg, msg.SignedSSVMessage, &trace.ConsensusTrace, round) + default: + validatorPubKey := string(executorID) + trace := n.getValidatorTrace(slot, validatorPubKey) + trace.Lock() // take a second look here + defer trace.Unlock() + + var round = trace.getRound(uint64(subMsg.Round)) + round.Lock() + defer round.Unlock() + + // populate proposer + operatorIDs := getOperators(validatorPubKey) + if len(operatorIDs) > 0 { + var mockState = n.toMockState(subMsg, operatorIDs) + round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) + } + + var data = new(spectypes.ValidatorConsensusData) + err := data.Decode(msg.SignedSSVMessage.FullData) + if err != nil { + n.logger.Error("failed to decode validator proposal data", zap.Error(err)) + return + } + + trace.Validator = data.Duty.ValidatorIndex + + n.processConsensus(subMsg, msg.SignedSSVMessage, &trace.ConsensusTrace, round) + } } case spectypes.SSVPartialSignatureMsgType: pSigMessages := new(spectypes.PartialSignatureMessages) @@ -377,3 +388,12 @@ type committeeDutyTrace struct { sync.Mutex model.CommitteeDutyTrace } + +func (trace *committeeDutyTrace) getRound(rnd uint64) *round { + trace.Lock() + defer trace.Unlock() + + return &round{ + RoundTrace: trace.Rounds[rnd], + } +} From 98c91072008d9a098e99d616de59efad4a316c87 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 16:24:28 +0000 Subject: [PATCH 16/24] wip fixes to locking --- operator/validator/dutytracer.go | 85 ++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index acfc38b475..20182681e0 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -165,7 +165,10 @@ func (n *InMemTracer) createProposalTrace(msg *specqbft.Message, signedMsg *spec return proposalTrace } -func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, trace *model.ConsensusTrace, round *round) { +func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, round *round) { + round.Lock() + defer round.Unlock() + switch msg.MsgType { case specqbft.ProposalMsgType: round.ProposalTrace = n.createProposalTrace(msg, signedMsg) @@ -248,7 +251,7 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature trace.Pre = append(trace.Pre, &tr) } -func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, committeeID string) { +func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignatureMessages, committeeID string) { slot := uint64(msg.Slot) trace := n.getCommitteeTrace(slot, committeeID) @@ -276,6 +279,7 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature fields := []zap.Field{ zap.Uint64("slot", slot), + zap.Int("post len", len(trace.Post)), } n.logger.Info("signed committee", fields...) @@ -293,31 +297,43 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { case spectypes.RoleCommittee: commiteeID := string(executorID) trace := n.getCommitteeTrace(slot, commiteeID) - trace.Lock() // take a second look here (locking trace vs round etc) - defer trace.Unlock() - - var round = trace.getRound(uint64(subMsg.Round)) - round.Lock() - defer round.Unlock() - - // populate proposer - operatorIDs := getOperators(commiteeID) - if len(operatorIDs) > 0 { - var mockState = n.toMockState(subMsg, operatorIDs) - round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) - } - - n.processConsensus(subMsg, msg.SignedSSVMessage, &trace.ConsensusTrace, round) + round := trace.getRound(uint64(subMsg.Round)) + + func() { // populate proposer + operatorIDs := getOperators(commiteeID) + if len(operatorIDs) > 0 { + mockState := n.toMockState(subMsg, operatorIDs) + round.Lock() + defer round.Unlock() + round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) + } + }() + + n.processConsensus(subMsg, msg.SignedSSVMessage, round) default: validatorPubKey := string(executorID) trace := n.getValidatorTrace(slot, validatorPubKey) - trace.Lock() // take a second look here - defer trace.Unlock() - var round = trace.getRound(uint64(subMsg.Round)) - round.Lock() - defer round.Unlock() + stop := func() bool { + trace.Lock() + defer trace.Unlock() + + var data = new(spectypes.ValidatorConsensusData) + err := data.Decode(msg.SignedSSVMessage.FullData) + if err != nil { + n.logger.Error("failed to decode validator proposal data", zap.Error(err)) + return true + } + trace.Validator = data.Duty.ValidatorIndex + return false + }() + + if stop { + return + } + + round := trace.getRound(uint64(subMsg.Round)) // populate proposer operatorIDs := getOperators(validatorPubKey) if len(operatorIDs) > 0 { @@ -325,16 +341,7 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) } - var data = new(spectypes.ValidatorConsensusData) - err := data.Decode(msg.SignedSSVMessage.FullData) - if err != nil { - n.logger.Error("failed to decode validator proposal data", zap.Error(err)) - return - } - - trace.Validator = data.Duty.ValidatorIndex - - n.processConsensus(subMsg, msg.SignedSSVMessage, &trace.ConsensusTrace, round) + n.processConsensus(subMsg, msg.SignedSSVMessage, round) } } case spectypes.SSVPartialSignatureMsgType: @@ -345,14 +352,14 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { return } + executorID := string(msg.MsgID.GetDutyExecutorID()) + if msg.MsgID.GetRoleType() == spectypes.RoleCommittee { - committeeID := msg.MsgID.GetDutyExecutorID() - n.processPartialSigCommittee(pSigMessages, msg, string(committeeID)) + n.processPartialSigCommittee(pSigMessages, executorID) return } - validatorPubKey := msg.MsgID.GetDutyExecutorID() - n.processPartialSigValidator(pSigMessages, msg, string(validatorPubKey)) + n.processPartialSigValidator(pSigMessages, msg, executorID) } } @@ -393,6 +400,12 @@ func (trace *committeeDutyTrace) getRound(rnd uint64) *round { trace.Lock() defer trace.Unlock() + var count = len(trace.Rounds) + for rnd+1 > uint64(count) { //nolint:gosec + trace.Rounds = append(trace.Rounds, &model.RoundTrace{}) + count = len(trace.Rounds) + } + return &round{ RoundTrace: trace.Rounds[rnd], } From ed1b5c617acfdb1391d264127ee32b4ed687255b Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Tue, 4 Feb 2025 17:11:50 +0000 Subject: [PATCH 17/24] fixes --- operator/validator/dutytracer.go | 40 ++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 20182681e0..0fb3cf46ce 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -48,7 +48,7 @@ func (n *InMemTracer) getValidatorTrace(slot uint64, vPubKey string) *validatorD return trace } -func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID string) *committeeDutyTrace { +func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID []byte) *committeeDutyTrace { n.Lock() defer n.Unlock() @@ -58,17 +58,17 @@ func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID string) *commit n.committeeTraces[slot] = mp } - trace, ok := mp[committeeID] + trace, ok := mp[string(committeeID)] if !ok { trace = new(committeeDutyTrace) - mp[committeeID] = trace + mp[string(committeeID)] = trace } return trace } // HOW TO? id -> validator or committee id -func getOperators(id string) []spectypes.OperatorID { +func getOperators([]byte) []spectypes.OperatorID { return []spectypes.OperatorID{} } @@ -192,21 +192,17 @@ func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectyp round.Commits = append(round.Commits, m) case specqbft.RoundChangeMsgType: - // optional - only if round change is proposing a value - now := time.Now() roundChangeTrace := n.createRoundChangeTrace(msg, signedMsg, now) round.RoundChanges = append(round.RoundChanges, roundChangeTrace) } - - // n.validatorTraces = append(n.validatorTraces, model.ValidatorDutyTrace{}) } -func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey string) { +func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey []byte) { slot := uint64(msg.Slot) - trace := n.getValidatorTrace(slot, validatorPubKey) + trace := n.getValidatorTrace(slot, string(validatorPubKey)) trace.Lock() defer trace.Unlock() @@ -231,7 +227,7 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature fields := []zap.Field{ zap.Uint64("slot", slot), - zap.String("validator", hex.EncodeToString([]byte(validatorPubKey[len(validatorPubKey)-4:]))), + zap.String("validator", hex.EncodeToString(validatorPubKey[len(validatorPubKey)-4:])), zap.String("type", trace.Role.String()), } @@ -251,14 +247,14 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature trace.Pre = append(trace.Pre, &tr) } -func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignatureMessages, committeeID string) { +func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignatureMessages, committeeID []byte) { slot := uint64(msg.Slot) trace := n.getCommitteeTrace(slot, committeeID) trace.Lock() defer trace.Unlock() - //trace.CommitteeID = [32]byte(committeeID[:]) //fixme + trace.CommitteeID = [32]byte(committeeID[:]) trace.OperatorIDs = getOperators(committeeID) trace.Slot = msg.Slot @@ -276,13 +272,6 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature } trace.Post = append(trace.Post, &cTrace) - - fields := []zap.Field{ - zap.Uint64("slot", slot), - zap.Int("post len", len(trace.Post)), - } - - n.logger.Info("signed committee", fields...) } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { @@ -295,12 +284,11 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { switch msgID.GetRoleType() { case spectypes.RoleCommittee: - commiteeID := string(executorID) - trace := n.getCommitteeTrace(slot, commiteeID) + trace := n.getCommitteeTrace(slot, executorID) // committe id round := trace.getRound(uint64(subMsg.Round)) func() { // populate proposer - operatorIDs := getOperators(commiteeID) + operatorIDs := getOperators(executorID) if len(operatorIDs) > 0 { mockState := n.toMockState(subMsg, operatorIDs) round.Lock() @@ -335,9 +323,11 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { round := trace.getRound(uint64(subMsg.Round)) // populate proposer - operatorIDs := getOperators(validatorPubKey) + operatorIDs := getOperators(executorID) // validator pubkey if len(operatorIDs) > 0 { var mockState = n.toMockState(subMsg, operatorIDs) + round.Lock() + defer round.Unlock() round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) } @@ -352,7 +342,7 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { return } - executorID := string(msg.MsgID.GetDutyExecutorID()) + executorID := msg.MsgID.GetDutyExecutorID() if msg.MsgID.GetRoleType() == spectypes.RoleCommittee { n.processPartialSigCommittee(pSigMessages, executorID) From ab84d0346000e8ec2e9fcca1eb28ebf408db3f82 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Wed, 5 Feb 2025 17:48:44 +0000 Subject: [PATCH 18/24] adapter hack serves committee --- api/handlers/exporter.go | 21 ++++++++++--- cli/operator/adapter.go | 54 ++++++++++++++++++++++++++++++++ cli/operator/node.go | 4 +++ exporter/v2/model.go | 2 -- operator/validator/controller.go | 5 ++- operator/validator/dutytracer.go | 39 ++++++++++++++++++++++- 6 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 cli/operator/adapter.go diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index 85f3fbdc4c..483c861f05 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -12,14 +12,20 @@ import ( "github.com/ssvlabs/ssv/api" model "github.com/ssvlabs/ssv/exporter/v2" - trace "github.com/ssvlabs/ssv/exporter/v2/store" ibftstorage "github.com/ssvlabs/ssv/ibft/storage" ) type Exporter struct { NetworkConfig networkconfig.NetworkConfig ParticipantStores *ibftstorage.ParticipantStores - TraceStore trace.DutyTraceStore + TraceStore DutyTraceStore +} + +type DutyTraceStore interface { + GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (*model.ValidatorDutyTrace, error) + GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) ([]*model.CommitteeDutyTrace, error) + GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error) + GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) ([]*model.ValidatorDutyTrace, error) } type ParticipantResponse struct { @@ -143,12 +149,17 @@ func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) } - if len(request.CommitteeID) != 32 { - return api.BadRequestError(fmt.Errorf("committee ID is required")) + committeeIDBytes, err := hex.DecodeString(request.CommitteeID) + if err != nil { + return api.BadRequestError(fmt.Errorf("decode committee ID: %w", err)) + } + + if len(committeeIDBytes) != 32 { + return api.BadRequestError(fmt.Errorf("invalid committee ID length")) } var committeeID spectypes.CommitteeID - copy(committeeID[:], []byte(request.CommitteeID)) + copy(committeeID[:], committeeIDBytes) var duties []*model.CommitteeDutyTrace for s := request.From; s <= request.To; s++ { diff --git a/cli/operator/adapter.go b/cli/operator/adapter.go new file mode 100644 index 0000000000..6ddb7210c1 --- /dev/null +++ b/cli/operator/adapter.go @@ -0,0 +1,54 @@ +package operator + +import ( + "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/ssvlabs/ssv/api/handlers" + model "github.com/ssvlabs/ssv/exporter/v2" + "github.com/ssvlabs/ssv/operator/validator" + "go.uber.org/zap" +) + +// tmp hack + +func traceStoreAdapter(tracer *validator.InMemTracer, logger *zap.Logger) handlers.DutyTraceStore { + return &adapter{ + tracer: tracer, + logger: logger, + } +} + +type adapter struct { + tracer *validator.InMemTracer + logger *zap.Logger +} + +func (a *adapter) GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (*model.ValidatorDutyTrace, error) { + panic("not implemented") +} + +func (a *adapter) GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) ([]*model.CommitteeDutyTrace, error) { + panic("not implemented") +} + +func (a *adapter) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error) { + trace, err := a.tracer.GetCommitteeDuty(slot, committeeID) + if err != nil { + return nil, err + } + + return &model.CommitteeDutyTrace{ + Slot: slot, + CommitteeID: committeeID, + ConsensusTrace: model.ConsensusTrace{ + Rounds: trace.Rounds, + Decideds: trace.Decideds, + }, + Post: trace.Post, + OperatorIDs: trace.OperatorIDs, + }, nil +} + +func (a *adapter) GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) ([]*model.ValidatorDutyTrace, error) { + panic("not implemented") +} diff --git a/cli/operator/node.go b/cli/operator/node.go index 26b0f37751..246acf1207 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -342,6 +342,9 @@ var StartNodeCmd = &cobra.Command{ ) cfg.SSVOptions.ValidatorOptions.ValidatorSyncer = metadataSyncer + tracer := validator.NewTracer(logger) + cfg.SSVOptions.ValidatorOptions.DutyTracer = tracer + validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions) cfg.SSVOptions.ValidatorController = validatorCtrl cfg.SSVOptions.ValidatorStore = nodeStorage.ValidatorStore() @@ -449,6 +452,7 @@ var StartNodeCmd = &cobra.Command{ &handlers.Exporter{ NetworkConfig: networkConfig, ParticipantStores: storageMap, + TraceStore: traceStoreAdapter(tracer, logger), }, ) go func() { diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 667574fea8..381d4314af 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -9,7 +9,6 @@ import ( //go:generate sszgen -include ../../vendor/github.com/attestantio/go-eth2-client/spec/phase0,../../vendor/github.com/ssvlabs/ssv-spec/types --path model.go --objs ValidatorDutyTrace,CommitteeDutyTrace type ValidatorDutyTrace struct { - // sync.Mutex ConsensusTrace Slot phase0.Slot @@ -32,7 +31,6 @@ type DecidedTrace struct { } type RoundTrace struct { - // sync.Mutex Proposer spectypes.OperatorID // can be computed or saved // ProposalData ProposalTrace *ProposalTrace diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 67337d4994..11b0c99672 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -77,6 +77,7 @@ type ControllerOptions struct { RecipientsStorage Recipients NewDecidedHandler qbftcontroller.NewDecidedHandler DutyRoles []spectypes.BeaconRole + DutyTracer DutyTracer StorageMap *storage.ParticipantStores ValidatorStore registrystorage.ValidatorStore MessageValidator validation.MessageValidator @@ -235,10 +236,7 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { beaconNetwork := options.NetworkConfig.Beacon cacheTTL := beaconNetwork.SlotDurationSec() * time.Duration(beaconNetwork.SlotsPerEpoch()*2) // #nosec G115 - inMem := NewTracer(logger) - ctrl := controller{ - tracer: inMem, logger: logger.Named(logging.NameController), networkConfig: options.NetworkConfig, sharesStorage: options.RegistryStorage.Shares(), @@ -252,6 +250,7 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { beaconSigner: options.BeaconSigner, operatorSigner: options.OperatorSigner, network: options.Network, + tracer: options.DutyTracer, validatorsMap: options.ValidatorsMap, validatorOptions: validatorOptions, diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 0fb3cf46ce..9e43b255d8 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -2,11 +2,13 @@ package validator import ( "encoding/hex" + "errors" "sync" "time" "go.uber.org/zap" + "github.com/attestantio/go-eth2-client/spec/phase0" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" model "github.com/ssvlabs/ssv/exporter/v2" @@ -61,6 +63,7 @@ func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID []byte) *commit trace, ok := mp[string(committeeID)] if !ok { trace = new(committeeDutyTrace) + n.logger.Info("add new committee", zap.String("id", hex.EncodeToString(committeeID))) mp[string(committeeID)] = trace } @@ -226,8 +229,8 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature } fields := []zap.Field{ - zap.Uint64("slot", slot), zap.String("validator", hex.EncodeToString(validatorPubKey[len(validatorPubKey)-4:])), + zap.Uint64("slot", slot), zap.String("type", trace.Role.String()), } @@ -258,6 +261,13 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature trace.OperatorIDs = getOperators(committeeID) trace.Slot = msg.Slot + fields := []zap.Field{ + zap.String("committeeID", hex.EncodeToString(trace.CommitteeID[:])), + zap.Uint64("slot", slot), + } + + n.logger.Info("signed committee", fields...) + var cTrace model.CommitteePartialSigMessageTrace cTrace.Type = msg.Type cTrace.Signer = msg.Messages[0].Signer @@ -400,3 +410,30 @@ func (trace *committeeDutyTrace) getRound(rnd uint64) *round { RoundTrace: trace.Rounds[rnd], } } + +// tmp hack + +func (n *InMemTracer) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error) { + n.Lock() + defer n.Unlock() + + m, ok := n.committeeTraces[uint64(slot)] + if !ok { + return nil, errors.New("slot not found") + } + + var mapID [48]byte + copy(mapID[16:], committeeID[:]) + + n.logger.Info("committee", zap.String("committeeID", hex.EncodeToString(mapID[:]))) + + trace, ok := m[string(mapID[:])] + if !ok { + return nil, errors.New("committe ID not found: " + hex.EncodeToString(mapID[:])) + } + + trace.Lock() + defer trace.Unlock() + + return &trace.CommitteeDutyTrace, nil +} From c2b626059ef1dbd4d9ed55032925b4abfe808fc7 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Thu, 6 Feb 2025 11:50:19 +0000 Subject: [PATCH 19/24] UI dtos and ttl cache --- api/handlers/model.go | 72 ++++++++++++++++++++----- operator/validator/dutytracer.go | 91 ++++++++++++++++++-------------- 2 files changed, 109 insertions(+), 54 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index 59f9897880..1edb2d5c8a 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -1,6 +1,7 @@ package handlers import ( + "encoding/hex" "time" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -30,12 +31,10 @@ type decided struct { } type round struct { - Proposer spectypes.OperatorID `json:"proposer"` - ProposalRoot phase0.Root `json:"proposalRoot"` - ProposalReceivedTime uint64 `json:"time"` - Prepares []message `json:"prepares"` - Commits []message `json:"commits"` - RoundChanges []roundChange `json:"roundChanges"` + Proposer spectypes.OperatorID `json:"proposer"` + Prepares []message `json:"prepares"` + Commits []message `json:"commits"` + RoundChanges []roundChange `json:"roundChanges"` } type roundChange struct { @@ -45,10 +44,10 @@ type roundChange struct { } type message struct { - Type spectypes.PartialSigMsgType `json:"type"` - BeaconRoot phase0.Root `json:"beaconRoot"` - Signer spectypes.OperatorID `json:"signer"` - ReceivedTime time.Time `json:"time"` + Round uint64 `json:"round"` + BeaconRoot phase0.Root `json:"beaconRoot"` + Signer spectypes.OperatorID `json:"signer"` + ReceivedTime time.Time `json:"time"` } func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { @@ -65,7 +64,6 @@ func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { func toMessageTrace(m []*model.PartialSigMessageTrace) (out []message) { for _, mt := range m { out = append(out, message{ - Type: mt.Type, BeaconRoot: mt.BeaconRoot, Signer: mt.Signer, ReceivedTime: mt.ReceivedTime, @@ -77,7 +75,39 @@ func toMessageTrace(m []*model.PartialSigMessageTrace) (out []message) { func toRoundTrace(r []*model.RoundTrace) (out []round) { for _, rt := range r { out = append(out, round{ - Proposer: rt.Proposer, + Proposer: rt.Proposer, + Prepares: toUIMessageTrace(rt.Prepares), + Commits: toUIMessageTrace(rt.Commits), + RoundChanges: toUIRoundChangeTrace(rt.RoundChanges), + }) + } + return +} + +func toUIMessageTrace(m []*model.MessageTrace) (out []message) { + for _, mt := range m { + out = append(out, message{ + Round: mt.Round, + BeaconRoot: mt.BeaconRoot, + Signer: mt.Signer, + ReceivedTime: mt.ReceivedTime, + }) + } + + return +} + +func toUIRoundChangeTrace(m []*model.RoundChangeTrace) (out []roundChange) { + for _, mt := range m { + out = append(out, roundChange{ + message: message{ + Round: mt.Round, + BeaconRoot: mt.BeaconRoot, + Signer: mt.Signer, + ReceivedTime: mt.ReceivedTime, + }, + PreparedRound: mt.PreparedRound, + PrepareMessages: toUIMessageTrace(mt.PrepareMessages), }) } return @@ -95,7 +125,7 @@ type committeeTrace struct { Decideds []decided `json:"decideds"` Post []committeeMessage `json:"post"` - CommitteeID spectypes.CommitteeID `json:"committeeID"` + CommitteeID string `json:"committeeID"` OperatorIDs []spectypes.OperatorID `json:"operatorIDs"` } @@ -112,11 +142,25 @@ func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { Slot: t.Slot, Rounds: toRoundTrace(t.Rounds), Post: toCommitteeMessageTrace(t.Post), - CommitteeID: t.CommitteeID, + CommitteeID: hex.EncodeToString(t.CommitteeID[:]), OperatorIDs: t.OperatorIDs, + Decideds: toDecidedTrace(t.Decideds), } } +func toDecidedTrace(d []*model.DecidedTrace) (out []decided) { + for _, dt := range d { + out = append(out, decided{ + Round: dt.Round, + BeaconRoot: dt.BeaconRoot, + Signer: dt.Signer, + ReceivedTime: dt.ReceivedTime, + }) + } + + return +} + func toCommitteeMessageTrace(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) { for _, mt := range m { out = append(out, committeeMessage{ diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 9e43b255d8..87213ba80c 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -1,6 +1,7 @@ package validator import ( + "context" "encoding/hex" "errors" "sync" @@ -9,6 +10,7 @@ import ( "go.uber.org/zap" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/jellydator/ttlcache/v3" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" model "github.com/ssvlabs/ssv/exporter/v2" @@ -19,32 +21,52 @@ type InMemTracer struct { sync.Mutex logger *zap.Logger // consider having the validator pubkey before of the slot - validatorTraces map[uint64]map[string]*validatorDutyTrace - committeeTraces map[uint64]map[string]*committeeDutyTrace + validatorTraces *ttlcache.Cache[uint64, map[string]*validatorDutyTrace] + committeeTraces *ttlcache.Cache[uint64, map[string]*committeeDutyTrace] } func NewTracer(logger *zap.Logger) *InMemTracer { - return &InMemTracer{ - logger: logger, - validatorTraces: make(map[uint64]map[string]*validatorDutyTrace), - committeeTraces: make(map[uint64]map[string]*committeeDutyTrace), + tracer := &InMemTracer{ + logger: logger, + validatorTraces: ttlcache.New( + ttlcache.WithTTL[uint64, map[string]*validatorDutyTrace](3 * time.Minute), + ), + committeeTraces: ttlcache.New( + ttlcache.WithTTL[uint64, map[string]*committeeDutyTrace](3 * time.Minute), + ), } + + tracer.committeeTraces.OnEviction(func(_ context.Context, _ ttlcache.EvictionReason, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { + logger.Info("eviction", zap.Uint64("slot", item.Key()), zap.Int("len", len(item.Value()))) + }) + + tracer.committeeTraces.OnInsertion(func(_ context.Context, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { + logger.Info("insertion", zap.Uint64("slot", item.Key())) + for k := range item.Value() { + logger.Info("committee", zap.String("id", hex.EncodeToString([]byte(k)))) + } + }) + + go func() { + for { + <-time.After(time.Minute) + tracer.committeeTraces.DeleteExpired() + } + }() + + return tracer } func (n *InMemTracer) getValidatorTrace(slot uint64, vPubKey string) *validatorDutyTrace { n.Lock() defer n.Unlock() - mp, ok := n.validatorTraces[slot] - if !ok { - mp = make(map[string]*validatorDutyTrace) - n.validatorTraces[slot] = mp - } + mp, _ := n.validatorTraces.GetOrSet(slot, make(map[string]*validatorDutyTrace)) - trace, ok := mp[vPubKey] + trace, ok := mp.Value()[vPubKey] if !ok { trace = new(validatorDutyTrace) - mp[vPubKey] = trace + mp.Value()[vPubKey] = trace } return trace @@ -54,17 +76,12 @@ func (n *InMemTracer) getCommitteeTrace(slot uint64, committeeID []byte) *commit n.Lock() defer n.Unlock() - mp, ok := n.committeeTraces[slot] - if !ok { - mp = make(map[string]*committeeDutyTrace) - n.committeeTraces[slot] = mp - } + mp, _ := n.committeeTraces.GetOrSet(slot, make(map[string]*committeeDutyTrace)) - trace, ok := mp[string(committeeID)] + trace, ok := mp.Value()[string(committeeID)] if !ok { trace = new(committeeDutyTrace) - n.logger.Info("add new committee", zap.String("id", hex.EncodeToString(committeeID))) - mp[string(committeeID)] = trace + mp.Value()[string(committeeID)] = trace } return trace @@ -133,7 +150,7 @@ func (n *InMemTracer) decodeJustificationWithRoundChanges(justifications [][]byt continue } - var receivedTime time.Time // zero value becausewe can't know the time when the sender received the message + var receivedTime time.Time // zero value because we can't know the time when the sender received the message var roundChangeTrace = n.createRoundChangeTrace(qbftMsg, signedMsg, receivedTime) traces = append(traces, roundChangeTrace) @@ -261,27 +278,20 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature trace.OperatorIDs = getOperators(committeeID) trace.Slot = msg.Slot - fields := []zap.Field{ - zap.String("committeeID", hex.EncodeToString(trace.CommitteeID[:])), - zap.Uint64("slot", slot), - } - - n.logger.Info("signed committee", fields...) - - var cTrace model.CommitteePartialSigMessageTrace + var cTrace = new(model.CommitteePartialSigMessageTrace) cTrace.Type = msg.Type cTrace.Signer = msg.Messages[0].Signer cTrace.ReceivedTime = time.Now() for _, partialSigMsg := range msg.Messages { - tr := model.PartialSigMessage{} + tr := new(model.PartialSigMessage) tr.BeaconRoot = partialSigMsg.SigningRoot tr.Signer = partialSigMsg.Signer tr.ValidatorIndex = partialSigMsg.ValidatorIndex - cTrace.Messages = append(cTrace.Messages, &tr) + cTrace.Messages = append(cTrace.Messages, tr) } - trace.Post = append(trace.Post, &cTrace) + trace.Post = append(trace.Post, cTrace) } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { @@ -378,12 +388,12 @@ func (trace *validatorDutyTrace) getRound(rnd uint64) *round { defer trace.Unlock() var count = len(trace.Rounds) - for rnd+1 > uint64(count) { //nolint:gosec + for rnd > uint64(count) { //nolint:gosec trace.Rounds = append(trace.Rounds, &model.RoundTrace{}) count = len(trace.Rounds) } - r := trace.Rounds[rnd] + r := trace.Rounds[rnd-1] return &round{ RoundTrace: r, } @@ -401,13 +411,13 @@ func (trace *committeeDutyTrace) getRound(rnd uint64) *round { defer trace.Unlock() var count = len(trace.Rounds) - for rnd+1 > uint64(count) { //nolint:gosec + for rnd > uint64(count) { //nolint:gosec trace.Rounds = append(trace.Rounds, &model.RoundTrace{}) count = len(trace.Rounds) } return &round{ - RoundTrace: trace.Rounds[rnd], + RoundTrace: trace.Rounds[rnd-1], } } @@ -417,17 +427,18 @@ func (n *InMemTracer) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.C n.Lock() defer n.Unlock() - m, ok := n.committeeTraces[uint64(slot)] - if !ok { + if !n.committeeTraces.Has(uint64(slot)) { return nil, errors.New("slot not found") } + m := n.committeeTraces.Get(uint64(slot)) + var mapID [48]byte copy(mapID[16:], committeeID[:]) n.logger.Info("committee", zap.String("committeeID", hex.EncodeToString(mapID[:]))) - trace, ok := m[string(mapID[:])] + trace, ok := m.Value()[string(mapID[:])] if !ok { return nil, errors.New("committe ID not found: " + hex.EncodeToString(mapID[:])) } From f1f906536a61d3c28e68900b680b8d873336a72d Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Thu, 6 Feb 2025 12:18:12 +0000 Subject: [PATCH 20/24] missing mappings --- api/handlers/model.go | 86 ++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index 1edb2d5c8a..9359d4a239 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -31,10 +31,20 @@ type decided struct { } type round struct { - Proposer spectypes.OperatorID `json:"proposer"` - Prepares []message `json:"prepares"` - Commits []message `json:"commits"` - RoundChanges []roundChange `json:"roundChanges"` + ProposalTrace *proposalTrace `json:"proposal"` + Proposer spectypes.OperatorID `json:"proposer"` + Prepares []message `json:"prepares"` + Commits []message `json:"commits"` + RoundChanges []roundChange `json:"roundChanges"` +} + +type proposalTrace struct { + Round uint64 `json:"round"` + BeaconRoot phase0.Root `json:"beaconRoot"` + Signer spectypes.OperatorID `json:"signer"` + RoundChanges []roundChange `json:"roundChanges"` + PrepareMessages []message `json:"prepareMessages"` + ReceivedTime time.Time `json:"time"` } type roundChange struct { @@ -50,6 +60,12 @@ type message struct { ReceivedTime time.Time `json:"time"` } +type partialSigMessage struct { + BeaconRoot phase0.Root `json:"beaconRoot"` + Signer spectypes.OperatorID `json:"signer"` + Validator phase0.ValidatorIndex `json:"validator"` +} + func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { return validatorTrace{ Slot: t.Slot, @@ -57,7 +73,7 @@ func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { Validator: t.Validator, Pre: toMessageTrace(t.Pre), Post: toMessageTrace(t.Post), - Rounds: toRoundTrace(t.Rounds), + Rounds: toRounds(t.Rounds), } } @@ -72,18 +88,33 @@ func toMessageTrace(m []*model.PartialSigMessageTrace) (out []message) { return } -func toRoundTrace(r []*model.RoundTrace) (out []round) { +func toRounds(r []*model.RoundTrace) (out []round) { for _, rt := range r { out = append(out, round{ - Proposer: rt.Proposer, - Prepares: toUIMessageTrace(rt.Prepares), - Commits: toUIMessageTrace(rt.Commits), - RoundChanges: toUIRoundChangeTrace(rt.RoundChanges), + Proposer: rt.Proposer, + ProposalTrace: toProposalTrace(rt.ProposalTrace), + Prepares: toUIMessageTrace(rt.Prepares), + Commits: toUIMessageTrace(rt.Commits), + RoundChanges: toUIRoundChangeTrace(rt.RoundChanges), }) } return } +func toProposalTrace(rt *model.ProposalTrace) *proposalTrace { + if rt == nil { + return nil + } + return &proposalTrace{ + Round: rt.Round, + BeaconRoot: rt.BeaconRoot, + Signer: rt.Signer, + ReceivedTime: rt.ReceivedTime, + RoundChanges: toUIRoundChangeTrace(rt.RoundChanges), + PrepareMessages: toUIMessageTrace(rt.PrepareMessages), + } +} + func toUIMessageTrace(m []*model.MessageTrace) (out []message) { for _, mt := range m { out = append(out, message{ @@ -130,21 +161,21 @@ type committeeTrace struct { } type committeeMessage struct { - message - BeaconRoot []phase0.Root `json:"beaconRoot"` - Validators []phase0.ValidatorIndex `json:"validators"` - Signer spectypes.OperatorID `json:"signer"` - ReceivedTime time.Time `json:"time"` + Type spectypes.PartialSigMsgType `json:"type"` + Signer spectypes.OperatorID `json:"signer"` + Messages []partialSigMessage `json:"messages"` + ReceivedTime time.Time `json:"time"` } func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { return committeeTrace{ + // consensus trace + Rounds: toRounds(t.Rounds), + Decideds: toDecidedTrace(t.Decideds), Slot: t.Slot, - Rounds: toRoundTrace(t.Rounds), - Post: toCommitteeMessageTrace(t.Post), + Post: toCommitteePost(t.Post), CommitteeID: hex.EncodeToString(t.CommitteeID[:]), OperatorIDs: t.OperatorIDs, - Decideds: toDecidedTrace(t.Decideds), } } @@ -161,15 +192,26 @@ func toDecidedTrace(d []*model.DecidedTrace) (out []decided) { return } -func toCommitteeMessageTrace(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) { +func toCommitteePost(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) { for _, mt := range m { out = append(out, committeeMessage{ - // Type: mt.Type, - // BeaconRoot: mt.BeaconRoot, - // Validators: mt.Validators, + Type: mt.Type, Signer: mt.Signer, + Messages: toCommitteePartSigMessage(mt.Messages), ReceivedTime: mt.ReceivedTime, }) } return } + +func toCommitteePartSigMessage(m []*model.PartialSigMessage) (out []partialSigMessage) { + for _, mt := range m { + out = append(out, partialSigMessage{ + BeaconRoot: mt.BeaconRoot, + Signer: mt.Signer, + Validator: mt.ValidatorIndex, + }) + } + + return +} From 9184a0ecf19b56244bdc817bfc25ea4bb2fe1858 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Thu, 6 Feb 2025 13:15:34 +0000 Subject: [PATCH 21/24] decideds --- operator/validator/dutytracer.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 87213ba80c..b85d55d6b2 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -41,9 +41,8 @@ func NewTracer(logger *zap.Logger) *InMemTracer { }) tracer.committeeTraces.OnInsertion(func(_ context.Context, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { - logger.Info("insertion", zap.Uint64("slot", item.Key())) for k := range item.Value() { - logger.Info("committee", zap.String("id", hex.EncodeToString([]byte(k)))) + logger.Info("insertion", zap.Uint64("slot", item.Key()), zap.String("id", hex.EncodeToString([]byte(k[16:])))) } }) @@ -292,6 +291,16 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature } trace.Post = append(trace.Post, cTrace) + + trace.Decideds = append(trace.Decideds, &model.DecidedTrace{ + MessageTrace: model.MessageTrace{ + Round: 0, + BeaconRoot: msg.Messages[0].SigningRoot, // Matheus:TODO: check if this is correct + Signer: msg.Messages[0].Signer, + ReceivedTime: time.Now(), + }, + Signers: []spectypes.OperatorID{msg.Messages[0].Signer}, // Matheus: WIP + }) } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { @@ -436,8 +445,6 @@ func (n *InMemTracer) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.C var mapID [48]byte copy(mapID[16:], committeeID[:]) - n.logger.Info("committee", zap.String("committeeID", hex.EncodeToString(mapID[:]))) - trace, ok := m.Value()[string(mapID[:])] if !ok { return nil, errors.New("committe ID not found: " + hex.EncodeToString(mapID[:])) From d22d8c8e9d5d9378460203fa284ff3dc158bc7fb Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Thu, 6 Feb 2025 17:05:48 +0000 Subject: [PATCH 22/24] string type --- api/handlers/model.go | 11 ++++---- operator/validator/dutytracer.go | 45 ++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index 9359d4a239..743589c037 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -7,6 +7,7 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/ssvlabs/ssv-spec/types" model "github.com/ssvlabs/ssv/exporter/v2" + qbftmsg "github.com/ssvlabs/ssv/protocol/v2/message" ) type validatorTraceResponse struct { @@ -161,10 +162,10 @@ type committeeTrace struct { } type committeeMessage struct { - Type spectypes.PartialSigMsgType `json:"type"` - Signer spectypes.OperatorID `json:"signer"` - Messages []partialSigMessage `json:"messages"` - ReceivedTime time.Time `json:"time"` + Type string `json:"type"` + Signer spectypes.OperatorID `json:"signer"` + Messages []partialSigMessage `json:"messages"` + ReceivedTime time.Time `json:"time"` } func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace { @@ -195,7 +196,7 @@ func toDecidedTrace(d []*model.DecidedTrace) (out []decided) { func toCommitteePost(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) { for _, mt := range m { out = append(out, committeeMessage{ - Type: mt.Type, + Type: qbftmsg.PartialMsgTypeToString(mt.Type), Signer: mt.Signer, Messages: toCommitteePartSigMessage(mt.Messages), ReceivedTime: mt.ReceivedTime, diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index b85d55d6b2..2fde230968 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -40,12 +40,6 @@ func NewTracer(logger *zap.Logger) *InMemTracer { logger.Info("eviction", zap.Uint64("slot", item.Key()), zap.Int("len", len(item.Value()))) }) - tracer.committeeTraces.OnInsertion(func(_ context.Context, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { - for k := range item.Value() { - logger.Info("insertion", zap.Uint64("slot", item.Key()), zap.String("id", hex.EncodeToString([]byte(k[16:])))) - } - }) - go func() { for { <-time.After(time.Minute) @@ -331,23 +325,34 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { validatorPubKey := string(executorID) trace := n.getValidatorTrace(slot, validatorPubKey) - stop := func() bool { - trace.Lock() - defer trace.Unlock() - - var data = new(spectypes.ValidatorConsensusData) - err := data.Decode(msg.SignedSSVMessage.FullData) + if msg.MsgType == spectypes.SSVConsensusMsgType { + var qbftMsg specqbft.Message + err := qbftMsg.Decode(msg.Data) if err != nil { - n.logger.Error("failed to decode validator proposal data", zap.Error(err)) - return true + n.logger.Error("failed to decode validator consensus data", zap.Error(err)) + return } - trace.Validator = data.Duty.ValidatorIndex - return false - }() - - if stop { - return + if qbftMsg.MsgType == specqbft.ProposalMsgType { + stop := func() bool { + trace.Lock() + defer trace.Unlock() + + var data = new(spectypes.ValidatorConsensusData) + err := data.Decode(msg.SignedSSVMessage.FullData) + if err != nil { + n.logger.Error("failed to decode validator proposal data", zap.Error(err)) + return true + } + + trace.Validator = data.Duty.ValidatorIndex + return false + }() + + if stop { + return + } + } } round := trace.getRound(uint64(subMsg.Round)) From 9d37461fbc704da955aaac1fed7e2262bffc9300 Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Fri, 7 Feb 2025 15:38:40 +0000 Subject: [PATCH 23/24] decidedds --- api/handlers/model.go | 8 +- exporter/v2/model.go | 27 ++--- exporter/v2/model_encoding.go | 163 +++++++++++++++---------------- operator/validator/dutytracer.go | 45 +++++---- 4 files changed, 121 insertions(+), 122 deletions(-) diff --git a/api/handlers/model.go b/api/handlers/model.go index 743589c037..5902cf4a6f 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -27,7 +27,7 @@ type validatorTrace struct { type decided struct { Round uint64 BeaconRoot phase0.Root - Signer spectypes.OperatorID + Signers []spectypes.OperatorID ReceivedTime time.Time } @@ -78,7 +78,7 @@ func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace { } } -func toMessageTrace(m []*model.PartialSigMessageTrace) (out []message) { +func toMessageTrace(m []*model.PartialSigTrace) (out []message) { for _, mt := range m { out = append(out, message{ BeaconRoot: mt.BeaconRoot, @@ -116,7 +116,7 @@ func toProposalTrace(rt *model.ProposalTrace) *proposalTrace { } } -func toUIMessageTrace(m []*model.MessageTrace) (out []message) { +func toUIMessageTrace(m []*model.QBFTTrace) (out []message) { for _, mt := range m { out = append(out, message{ Round: mt.Round, @@ -185,7 +185,7 @@ func toDecidedTrace(d []*model.DecidedTrace) (out []decided) { out = append(out, decided{ Round: dt.Round, BeaconRoot: dt.BeaconRoot, - Signer: dt.Signer, + Signers: dt.Signers, ReceivedTime: dt.ReceivedTime, }) } diff --git a/exporter/v2/model.go b/exporter/v2/model.go index 381d4314af..40a866efee 100644 --- a/exporter/v2/model.go +++ b/exporter/v2/model.go @@ -12,8 +12,8 @@ type ValidatorDutyTrace struct { ConsensusTrace Slot phase0.Slot - Pre []*PartialSigMessageTrace `ssz-max:"13"` - Post []*PartialSigMessageTrace `ssz-max:"13"` + Pre []*PartialSigTrace `ssz-max:"13"` + Post []*PartialSigTrace `ssz-max:"13"` Role spectypes.BeaconRole // this could be a pubkey Validator phase0.ValidatorIndex @@ -25,40 +25,41 @@ type ConsensusTrace struct { } type DecidedTrace struct { - MessageTrace - // Value []byte // full data needed? - Signers []spectypes.OperatorID `ssz-max:"13"` + Round uint64 // same for + BeaconRoot phase0.Root `ssz-size:"32"` + Signers []spectypes.OperatorID `ssz-max:"13"` + ReceivedTime time.Time } type RoundTrace struct { Proposer spectypes.OperatorID // can be computed or saved // ProposalData ProposalTrace *ProposalTrace - Prepares []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. - Commits []*MessageTrace `ssz-max:"13"` // Only recorded if root matches proposal. + Prepares []*QBFTTrace `ssz-max:"13"` // Only recorded if root matches proposal. + Commits []*QBFTTrace `ssz-max:"13"` // Only recorded if root matches proposal. RoundChanges []*RoundChangeTrace `ssz-max:"13"` } type RoundChangeTrace struct { - MessageTrace + QBFTTrace PreparedRound uint64 - PrepareMessages []*MessageTrace `ssz-max:"13"` + PrepareMessages []*QBFTTrace `ssz-max:"13"` } type ProposalTrace struct { - MessageTrace + QBFTTrace RoundChanges []*RoundChangeTrace `ssz-max:"13"` - PrepareMessages []*MessageTrace `ssz-max:"13"` + PrepareMessages []*QBFTTrace `ssz-max:"13"` } -type MessageTrace struct { +type QBFTTrace struct { Round uint64 // same for BeaconRoot phase0.Root `ssz-size:"32"` Signer spectypes.OperatorID ReceivedTime time.Time } -type PartialSigMessageTrace struct { +type PartialSigTrace struct { Type spectypes.PartialSigMsgType BeaconRoot phase0.Root `ssz-size:"32"` Signer spectypes.OperatorID diff --git a/exporter/v2/model_encoding.go b/exporter/v2/model_encoding.go index 043ab7e7a2..067702a2dd 100644 --- a/exporter/v2/model_encoding.go +++ b/exporter/v2/model_encoding.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 242393fc08a3f8ce75ba086caea6ae0776ed76953dbe711dc3d5e6cc664ff59a +// Hash: 5909191be4b4882ef7d8241a17f34caee6dff722f6bde5d54d6f8037b9f7de68 // Version: 0.1.3 package exporter @@ -206,10 +206,10 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - v.Pre = make([]*PartialSigMessageTrace, num) + v.Pre = make([]*PartialSigTrace, num) for ii := 0; ii < num; ii++ { if v.Pre[ii] == nil { - v.Pre[ii] = new(PartialSigMessageTrace) + v.Pre[ii] = new(PartialSigTrace) } if err = v.Pre[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -224,10 +224,10 @@ func (v *ValidatorDutyTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - v.Post = make([]*PartialSigMessageTrace, num) + v.Post = make([]*PartialSigTrace, num) for ii := 0; ii < num; ii++ { if v.Post[ii] == nil { - v.Post[ii] = new(PartialSigMessageTrace) + v.Post[ii] = new(PartialSigTrace) } if err = v.Post[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -361,7 +361,7 @@ func (d *DecidedTrace) MarshalSSZ() ([]byte, error) { // MarshalSSZTo ssz marshals the DecidedTrace object to a target array func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf - offset := int(60) + offset := int(52) // Field (0) 'Round' dst = ssz.MarshalUint64(dst, d.Round) @@ -369,17 +369,14 @@ func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { // Field (1) 'BeaconRoot' dst = append(dst, d.BeaconRoot[:]...) - // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(d.Signer)) + // Offset (2) 'Signers' + dst = ssz.WriteOffset(dst, offset) + offset += len(d.Signers) * 8 // Field (3) 'ReceivedTime' dst = ssz.MarshalTime(dst, d.ReceivedTime) - // Offset (4) 'Signers' - dst = ssz.WriteOffset(dst, offset) - offset += len(d.Signers) * 8 - - // Field (4) 'Signers' + // Field (2) 'Signers' if size := len(d.Signers); size > 13 { err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) return @@ -395,12 +392,12 @@ func (d *DecidedTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) - if size < 60 { + if size < 52 { return ssz.ErrSize } tail := buf - var o4 uint64 + var o2 uint64 // Field (0) 'Round' d.Round = ssz.UnmarshallUint64(buf[0:8]) @@ -408,24 +405,21 @@ func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { // Field (1) 'BeaconRoot' copy(d.BeaconRoot[:], buf[8:40]) - // Field (2) 'Signer' - d.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) - - // Field (3) 'ReceivedTime' - d.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) - - // Offset (4) 'Signers' - if o4 = ssz.ReadOffset(buf[56:60]); o4 > size { + // Offset (2) 'Signers' + if o2 = ssz.ReadOffset(buf[40:44]); o2 > size { return ssz.ErrOffset } - if o4 < 60 { + if o2 < 52 { return ssz.ErrInvalidVariableOffset } - // Field (4) 'Signers' + // Field (3) 'ReceivedTime' + d.ReceivedTime = ssz.UnmarshalTime(buf[44:52]) + + // Field (2) 'Signers' { - buf = tail[o4:] + buf = tail[o2:] num, err := ssz.DivideInt2(len(buf), 8, 13) if err != nil { return err @@ -440,9 +434,9 @@ func (d *DecidedTrace) UnmarshalSSZ(buf []byte) error { // SizeSSZ returns the ssz encoded size in bytes for the DecidedTrace object func (d *DecidedTrace) SizeSSZ() (size int) { - size = 60 + size = 52 - // Field (4) 'Signers' + // Field (2) 'Signers' size += len(d.Signers) * 8 return @@ -463,13 +457,7 @@ func (d *DecidedTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { // Field (1) 'BeaconRoot' hh.PutBytes(d.BeaconRoot[:]) - // Field (2) 'Signer' - hh.PutUint64(uint64(d.Signer)) - - // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(d.ReceivedTime.Unix())) - - // Field (4) 'Signers' + // Field (2) 'Signers' { if size := len(d.Signers); size > 13 { err = ssz.ErrListTooBigFn("DecidedTrace.Signers", size, 13) @@ -484,6 +472,9 @@ func (d *DecidedTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { hh.MerkleizeWithMixin(subIndx, numItems, ssz.CalculateLimit(13, numItems, 8)) } + // Field (3) 'ReceivedTime' + hh.PutUint64(uint64(d.ReceivedTime.Unix())) + hh.Merkleize(indx) return } @@ -632,10 +623,10 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - r.Prepares = make([]*MessageTrace, num) + r.Prepares = make([]*QBFTTrace, num) for ii := 0; ii < num; ii++ { if r.Prepares[ii] == nil { - r.Prepares[ii] = new(MessageTrace) + r.Prepares[ii] = new(QBFTTrace) } if err = r.Prepares[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -650,10 +641,10 @@ func (r *RoundTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - r.Commits = make([]*MessageTrace, num) + r.Commits = make([]*QBFTTrace, num) for ii := 0; ii < num; ii++ { if r.Commits[ii] == nil { - r.Commits[ii] = new(MessageTrace) + r.Commits[ii] = new(QBFTTrace) } if err = r.Commits[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -869,10 +860,10 @@ func (r *RoundChangeTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - r.PrepareMessages = make([]*MessageTrace, num) + r.PrepareMessages = make([]*QBFTTrace, num) for ii := 0; ii < num; ii++ { if r.PrepareMessages[ii] == nil { - r.PrepareMessages[ii] = new(MessageTrace) + r.PrepareMessages[ii] = new(QBFTTrace) } if err = r.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -1072,10 +1063,10 @@ func (p *ProposalTrace) UnmarshalSSZ(buf []byte) error { if err != nil { return err } - p.PrepareMessages = make([]*MessageTrace, num) + p.PrepareMessages = make([]*QBFTTrace, num) for ii := 0; ii < num; ii++ { if p.PrepareMessages[ii] == nil { - p.PrepareMessages[ii] = new(MessageTrace) + p.PrepareMessages[ii] = new(QBFTTrace) } if err = p.PrepareMessages[ii].UnmarshalSSZ(buf[ii*56 : (ii+1)*56]); err != nil { return err @@ -1163,32 +1154,32 @@ func (p *ProposalTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(p) } -// MarshalSSZ ssz marshals the MessageTrace object -func (m *MessageTrace) MarshalSSZ() ([]byte, error) { - return ssz.MarshalSSZ(m) +// MarshalSSZ ssz marshals the QBFTTrace object +func (q *QBFTTrace) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(q) } -// MarshalSSZTo ssz marshals the MessageTrace object to a target array -func (m *MessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the QBFTTrace object to a target array +func (q *QBFTTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf // Field (0) 'Round' - dst = ssz.MarshalUint64(dst, m.Round) + dst = ssz.MarshalUint64(dst, q.Round) // Field (1) 'BeaconRoot' - dst = append(dst, m.BeaconRoot[:]...) + dst = append(dst, q.BeaconRoot[:]...) // Field (2) 'Signer' - dst = ssz.MarshalUint64(dst, uint64(m.Signer)) + dst = ssz.MarshalUint64(dst, uint64(q.Signer)) // Field (3) 'ReceivedTime' - dst = ssz.MarshalTime(dst, m.ReceivedTime) + dst = ssz.MarshalTime(dst, q.ReceivedTime) return } -// UnmarshalSSZ ssz unmarshals the MessageTrace object -func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the QBFTTrace object +func (q *QBFTTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) if size != 56 { @@ -1196,63 +1187,63 @@ func (m *MessageTrace) UnmarshalSSZ(buf []byte) error { } // Field (0) 'Round' - m.Round = ssz.UnmarshallUint64(buf[0:8]) + q.Round = ssz.UnmarshallUint64(buf[0:8]) // Field (1) 'BeaconRoot' - copy(m.BeaconRoot[:], buf[8:40]) + copy(q.BeaconRoot[:], buf[8:40]) // Field (2) 'Signer' - m.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) + q.Signer = spectypes.OperatorID(ssz.UnmarshallUint64(buf[40:48])) // Field (3) 'ReceivedTime' - m.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) + q.ReceivedTime = ssz.UnmarshalTime(buf[48:56]) return err } -// SizeSSZ returns the ssz encoded size in bytes for the MessageTrace object -func (m *MessageTrace) SizeSSZ() (size int) { +// SizeSSZ returns the ssz encoded size in bytes for the QBFTTrace object +func (q *QBFTTrace) SizeSSZ() (size int) { size = 56 return } -// HashTreeRoot ssz hashes the MessageTrace object -func (m *MessageTrace) HashTreeRoot() ([32]byte, error) { - return ssz.HashWithDefaultHasher(m) +// HashTreeRoot ssz hashes the QBFTTrace object +func (q *QBFTTrace) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(q) } -// HashTreeRootWith ssz hashes the MessageTrace object with a hasher -func (m *MessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the QBFTTrace object with a hasher +func (q *QBFTTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Round' - hh.PutUint64(m.Round) + hh.PutUint64(q.Round) // Field (1) 'BeaconRoot' - hh.PutBytes(m.BeaconRoot[:]) + hh.PutBytes(q.BeaconRoot[:]) // Field (2) 'Signer' - hh.PutUint64(uint64(m.Signer)) + hh.PutUint64(uint64(q.Signer)) // Field (3) 'ReceivedTime' - hh.PutUint64(uint64(m.ReceivedTime.Unix())) + hh.PutUint64(uint64(q.ReceivedTime.Unix())) hh.Merkleize(indx) return } -// GetTree ssz hashes the MessageTrace object -func (m *MessageTrace) GetTree() (*ssz.Node, error) { - return ssz.ProofTree(m) +// GetTree ssz hashes the QBFTTrace object +func (q *QBFTTrace) GetTree() (*ssz.Node, error) { + return ssz.ProofTree(q) } -// MarshalSSZ ssz marshals the PartialSigMessageTrace object -func (p *PartialSigMessageTrace) MarshalSSZ() ([]byte, error) { +// MarshalSSZ ssz marshals the PartialSigTrace object +func (p *PartialSigTrace) MarshalSSZ() ([]byte, error) { return ssz.MarshalSSZ(p) } -// MarshalSSZTo ssz marshals the PartialSigMessageTrace object to a target array -func (p *PartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { +// MarshalSSZTo ssz marshals the PartialSigTrace object to a target array +func (p *PartialSigTrace) MarshalSSZTo(buf []byte) (dst []byte, err error) { dst = buf // Field (0) 'Type' @@ -1270,8 +1261,8 @@ func (p *PartialSigMessageTrace) MarshalSSZTo(buf []byte) (dst []byte, err error return } -// UnmarshalSSZ ssz unmarshals the PartialSigMessageTrace object -func (p *PartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { +// UnmarshalSSZ ssz unmarshals the PartialSigTrace object +func (p *PartialSigTrace) UnmarshalSSZ(buf []byte) error { var err error size := uint64(len(buf)) if size != 56 { @@ -1293,19 +1284,19 @@ func (p *PartialSigMessageTrace) UnmarshalSSZ(buf []byte) error { return err } -// SizeSSZ returns the ssz encoded size in bytes for the PartialSigMessageTrace object -func (p *PartialSigMessageTrace) SizeSSZ() (size int) { +// SizeSSZ returns the ssz encoded size in bytes for the PartialSigTrace object +func (p *PartialSigTrace) SizeSSZ() (size int) { size = 56 return } -// HashTreeRoot ssz hashes the PartialSigMessageTrace object -func (p *PartialSigMessageTrace) HashTreeRoot() ([32]byte, error) { +// HashTreeRoot ssz hashes the PartialSigTrace object +func (p *PartialSigTrace) HashTreeRoot() ([32]byte, error) { return ssz.HashWithDefaultHasher(p) } -// HashTreeRootWith ssz hashes the PartialSigMessageTrace object with a hasher -func (p *PartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { +// HashTreeRootWith ssz hashes the PartialSigTrace object with a hasher +func (p *PartialSigTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) { indx := hh.Index() // Field (0) 'Type' @@ -1324,8 +1315,8 @@ func (p *PartialSigMessageTrace) HashTreeRootWith(hh ssz.HashWalker) (err error) return } -// GetTree ssz hashes the PartialSigMessageTrace object -func (p *PartialSigMessageTrace) GetTree() (*ssz.Node, error) { +// GetTree ssz hashes the PartialSigTrace object +func (p *PartialSigTrace) GetTree() (*ssz.Node, error) { return ssz.ProofTree(p) } diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 2fde230968..0fcc9f510d 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -99,8 +99,8 @@ func (n *InMemTracer) toMockState(msg *specqbft.Message, operatorIDs []spectypes return mockState } -func (n *InMemTracer) decodeJustificationWithPrepares(justifications [][]byte) []*model.MessageTrace { - var traces = make([]*model.MessageTrace, 0, len(justifications)) +func (n *InMemTracer) decodeJustificationWithPrepares(justifications [][]byte) []*model.QBFTTrace { + var traces = make([]*model.QBFTTrace, 0, len(justifications)) for _, rcj := range justifications { var signedMsg = new(spectypes.SignedSSVMessage) err := signedMsg.Decode(rcj) @@ -116,7 +116,7 @@ func (n *InMemTracer) decodeJustificationWithPrepares(justifications [][]byte) [ continue } - var justificationTrace = new(model.MessageTrace) + var justificationTrace = new(model.QBFTTrace) justificationTrace.Round = uint64(qbftMsg.Round) justificationTrace.BeaconRoot = qbftMsg.Root justificationTrace.Signer = signedMsg.OperatorIDs[0] @@ -178,7 +178,7 @@ func (n *InMemTracer) createProposalTrace(msg *specqbft.Message, signedMsg *spec return proposalTrace } -func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, round *round) { +func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectypes.SignedSSVMessage, round *round) *model.DecidedTrace { round.Lock() defer round.Unlock() @@ -187,7 +187,7 @@ func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectyp round.ProposalTrace = n.createProposalTrace(msg, signedMsg) case specqbft.PrepareMsgType: - var m = new(model.MessageTrace) + var m = new(model.QBFTTrace) m.Round = uint64(msg.Round) m.BeaconRoot = msg.Root m.Signer = signedMsg.OperatorIDs[0] @@ -196,7 +196,16 @@ func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectyp round.Prepares = append(round.Prepares, m) case specqbft.CommitMsgType: - var m = new(model.MessageTrace) + if len(signedMsg.OperatorIDs) > 1 { + return &model.DecidedTrace{ + Round: uint64(msg.Round), + BeaconRoot: msg.Root, + Signers: signedMsg.OperatorIDs, + ReceivedTime: time.Now(), + } + } + + var m = new(model.QBFTTrace) m.Round = uint64(msg.Round) m.BeaconRoot = msg.Root m.Signer = signedMsg.OperatorIDs[0] @@ -210,6 +219,8 @@ func (n *InMemTracer) processConsensus(msg *specqbft.Message, signedMsg *spectyp round.RoundChanges = append(round.RoundChanges, roundChangeTrace) } + + return nil } func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignatureMessages, ssvMsg *queue.SSVMessage, validatorPubKey []byte) { @@ -246,7 +257,7 @@ func (n *InMemTracer) processPartialSigValidator(msg *spectypes.PartialSignature n.logger.Info("signed validator", fields...) - var tr model.PartialSigMessageTrace + var tr model.PartialSigTrace tr.Type = msg.Type tr.BeaconRoot = msg.Messages[0].SigningRoot tr.Signer = msg.Messages[0].Signer @@ -285,16 +296,6 @@ func (n *InMemTracer) processPartialSigCommittee(msg *spectypes.PartialSignature } trace.Post = append(trace.Post, cTrace) - - trace.Decideds = append(trace.Decideds, &model.DecidedTrace{ - MessageTrace: model.MessageTrace{ - Round: 0, - BeaconRoot: msg.Messages[0].SigningRoot, // Matheus:TODO: check if this is correct - Signer: msg.Messages[0].Signer, - ReceivedTime: time.Now(), - }, - Signers: []spectypes.OperatorID{msg.Messages[0].Signer}, // Matheus: WIP - }) } func (n *InMemTracer) Trace(msg *queue.SSVMessage) { @@ -320,7 +321,10 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { } }() - n.processConsensus(subMsg, msg.SignedSSVMessage, round) + decided := n.processConsensus(subMsg, msg.SignedSSVMessage, round) + if decided != nil { + trace.Decideds = append(trace.Decideds, decided) + } default: validatorPubKey := string(executorID) trace := n.getValidatorTrace(slot, validatorPubKey) @@ -365,7 +369,10 @@ func (n *InMemTracer) Trace(msg *queue.SSVMessage) { round.Proposer = specqbft.RoundRobinProposer(mockState, subMsg.Round) } - n.processConsensus(subMsg, msg.SignedSSVMessage, round) + decided := n.processConsensus(subMsg, msg.SignedSSVMessage, round) + if decided != nil { + trace.Decideds = append(trace.Decideds, decided) + } } } case spectypes.SSVPartialSignatureMsgType: From 9158d38f8ceed642a8bb59def356c1f46730721b Mon Sep 17 00:00:00 2001 From: Anatolie Lupacescu Date: Mon, 10 Feb 2025 08:48:10 +0000 Subject: [PATCH 24/24] validator adapter --- api/handlers/exporter.go | 38 +++++++++++++------------ api/handlers/model.go | 12 +++++--- cli/operator/adapter.go | 16 +++++++++-- operator/validator/dutytracer.go | 49 +++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/api/handlers/exporter.go b/api/handlers/exporter.go index 483c861f05..6b5cebf08b 100644 --- a/api/handlers/exporter.go +++ b/api/handlers/exporter.go @@ -6,13 +6,13 @@ import ( "net/http" "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/networkconfig" - qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage" + spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/api" model "github.com/ssvlabs/ssv/exporter/v2" ibftstorage "github.com/ssvlabs/ssv/ibft/storage" + "github.com/ssvlabs/ssv/networkconfig" + qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage" ) type Exporter struct { @@ -22,7 +22,7 @@ type Exporter struct { } type DutyTraceStore interface { - GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (*model.ValidatorDutyTrace, error) + GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, pubkey spectypes.ValidatorPK) (*model.ValidatorDutyTrace, error) GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) ([]*model.CommitteeDutyTrace, error) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error) GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) ([]*model.ValidatorDutyTrace, error) @@ -136,9 +136,9 @@ func (e *Exporter) OperatorTraces(w http.ResponseWriter, r *http.Request) error func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error { var request struct { - From uint64 `json:"from"` - To uint64 `json:"to"` - CommitteeID string `json:"committeeID"` + From uint64 `json:"from"` + To uint64 `json:"to"` + CommitteeID api.Hex `json:"committeeID"` } if err := api.Bind(r, &request); err != nil { @@ -149,17 +149,13 @@ func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'")) } - committeeIDBytes, err := hex.DecodeString(request.CommitteeID) - if err != nil { - return api.BadRequestError(fmt.Errorf("decode committee ID: %w", err)) - } + var committeeID spectypes.CommitteeID - if len(committeeIDBytes) != 32 { + if len(request.CommitteeID) != len(committeeID) { return api.BadRequestError(fmt.Errorf("invalid committee ID length")) } - var committeeID spectypes.CommitteeID - copy(committeeID[:], committeeIDBytes) + copy(committeeID[:], request.CommitteeID) var duties []*model.CommitteeDutyTrace for s := request.From; s <= request.To; s++ { @@ -187,7 +183,7 @@ func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error From uint64 `json:"from"` To uint64 `json:"to"` Roles api.RoleSlice `json:"roles"` - VIndex uint64 `json:"index"` + PubKey api.Hex `json:"pubkey"` // optional } if err := api.Bind(r, &request); err != nil { @@ -204,7 +200,7 @@ func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error var results []*model.ValidatorDutyTrace - if request.VIndex == 0 { + if len(request.PubKey) == 0 { for s := request.From; s <= request.To; s++ { slot := phase0.Slot(s) for _, r := range request.Roles { @@ -219,13 +215,19 @@ func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error return api.Render(w, r, toValidatorTraceResponse(results)) } - vIndex := phase0.ValidatorIndex(request.VIndex) + var pubkey spectypes.ValidatorPK + + if len(request.PubKey) != len(pubkey) { + return api.BadRequestError(fmt.Errorf("invalid pubkey length")) + } + + copy(pubkey[:], request.PubKey) for s := request.From; s <= request.To; s++ { slot := phase0.Slot(s) for _, r := range request.Roles { role := spectypes.BeaconRole(r) - duty, err := e.TraceStore.GetValidatorDuty(role, slot, vIndex) + duty, err := e.TraceStore.GetValidatorDuty(role, slot, pubkey) if err != nil { return api.Error(fmt.Errorf("error getting duties: %w", err)) } diff --git a/api/handlers/model.go b/api/handlers/model.go index 5902cf4a6f..00b3d9e68a 100644 --- a/api/handlers/model.go +++ b/api/handlers/model.go @@ -25,10 +25,10 @@ type validatorTrace struct { } type decided struct { - Round uint64 - BeaconRoot phase0.Root - Signers []spectypes.OperatorID - ReceivedTime time.Time + Round uint64 `json:"round"` + BeaconRoot phase0.Root `json:"beaconRoot"` + Signers []spectypes.OperatorID `json:"signers"` + ReceivedTime time.Time `json:"time"` } type round struct { @@ -86,6 +86,7 @@ func toMessageTrace(m []*model.PartialSigTrace) (out []message) { ReceivedTime: mt.ReceivedTime, }) } + return } @@ -99,6 +100,7 @@ func toRounds(r []*model.RoundTrace) (out []round) { RoundChanges: toUIRoundChangeTrace(rt.RoundChanges), }) } + return } @@ -142,6 +144,7 @@ func toUIRoundChangeTrace(m []*model.RoundChangeTrace) (out []roundChange) { PrepareMessages: toUIMessageTrace(mt.PrepareMessages), }) } + return } @@ -202,6 +205,7 @@ func toCommitteePost(m []*model.CommitteePartialSigMessageTrace) (out []committe ReceivedTime: mt.ReceivedTime, }) } + return } diff --git a/cli/operator/adapter.go b/cli/operator/adapter.go index 6ddb7210c1..43a7c597cf 100644 --- a/cli/operator/adapter.go +++ b/cli/operator/adapter.go @@ -23,8 +23,20 @@ type adapter struct { logger *zap.Logger } -func (a *adapter) GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, index phase0.ValidatorIndex) (*model.ValidatorDutyTrace, error) { - panic("not implemented") +func (a *adapter) GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, pubkey spectypes.ValidatorPK) (*model.ValidatorDutyTrace, error) { + trace, err := a.tracer.GetValidatorDuty(role, slot, pubkey) + if err != nil { + return nil, err + } + + return &model.ValidatorDutyTrace{ + Slot: slot, + ConsensusTrace: model.ConsensusTrace{ + Rounds: trace.Rounds, + Decideds: trace.Decideds, + }, + Post: trace.Post, + }, nil } func (a *adapter) GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) ([]*model.CommitteeDutyTrace, error) { diff --git a/operator/validator/dutytracer.go b/operator/validator/dutytracer.go index 0fcc9f510d..baa423cb9c 100644 --- a/operator/validator/dutytracer.go +++ b/operator/validator/dutytracer.go @@ -1,7 +1,6 @@ package validator import ( - "context" "encoding/hex" "errors" "sync" @@ -36,14 +35,35 @@ func NewTracer(logger *zap.Logger) *InMemTracer { ), } - tracer.committeeTraces.OnEviction(func(_ context.Context, _ ttlcache.EvictionReason, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { - logger.Info("eviction", zap.Uint64("slot", item.Key()), zap.Int("len", len(item.Value()))) - }) + // tracer.committeeTraces.OnEviction(func(_ context.Context, _ ttlcache.EvictionReason, item *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { + // logger.Info("eviction", zap.Uint64("slot", item.Key()), zap.Int("len", len(item.Value()))) + // }) + + // tracer.committeeTraces.OnInsertion(func(ctx context.Context, i *ttlcache.Item[uint64, map[string]*committeeDutyTrace]) { + // tracer.Lock() + // defer tracer.Unlock() + // for id := range i.Value() { + // logger.Info("insertion", zap.Uint64("slot", i.Key()), zap.String("id", hex.EncodeToString([]byte(id[16:])))) + // } + // }) + + // tracer.validatorTraces.OnEviction(func(_ context.Context, _ ttlcache.EvictionReason, item *ttlcache.Item[uint64, map[string]*validatorDutyTrace]) { + // logger.Info("eviction", zap.Uint64("slot", item.Key()), zap.Int("len", len(item.Value()))) + // }) + + // tracer.validatorTraces.OnInsertion(func(ctx context.Context, i *ttlcache.Item[uint64, map[string]*validatorDutyTrace]) { + // tracer.Lock() + // defer tracer.Unlock() + // for id := range i.Value() { + // logger.Info("insertion", zap.Uint64("slot", i.Key()), zap.String("id", hex.EncodeToString([]byte(id[16:])))) + // } + // }) go func() { for { <-time.After(time.Minute) tracer.committeeTraces.DeleteExpired() + tracer.validatorTraces.DeleteExpired() } }() @@ -444,6 +464,27 @@ func (trace *committeeDutyTrace) getRound(rnd uint64) *round { // tmp hack +func (n *InMemTracer) GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, pubkey spectypes.ValidatorPK) (*model.ValidatorDutyTrace, error) { + n.Lock() + defer n.Unlock() + + if !n.validatorTraces.Has(uint64(slot)) { + return nil, errors.New("slot not found") + } + + m := n.validatorTraces.Get(uint64(slot)) + + trace, ok := m.Value()[string(pubkey[:])] + if !ok { + return nil, errors.New("validator not found") + } + + trace.Lock() + defer trace.Unlock() + + return &trace.ValidatorDutyTrace, nil +} + func (n *InMemTracer) GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error) { n.Lock() defer n.Unlock()