From 7b3332a929e95303e61e51e81aeeb61df4535e1a Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 5 Aug 2023 14:48:32 +0200 Subject: [PATCH] show validator names as popover in aggregation bitsets --- handlers/slot.go | 19 ++++--- templates/slot/attestations.html | 2 +- types/models.go | 5 ++ types/models/slot.go | 93 ++++++++++++++++---------------- utils/format.go | 32 ++++++++--- 5 files changed, 90 insertions(+), 61 deletions(-) diff --git a/handlers/slot.go b/handlers/slot.go index f5841379..10aec4f4 100644 --- a/handlers/slot.go +++ b/handlers/slot.go @@ -16,6 +16,7 @@ import ( "github.com/pk910/light-beaconchain-explorer/rpctypes" "github.com/pk910/light-beaconchain-explorer/services" "github.com/pk910/light-beaconchain-explorer/templates" + "github.com/pk910/light-beaconchain-explorer/types" "github.com/pk910/light-beaconchain-explorer/types/models" "github.com/pk910/light-beaconchain-explorer/utils" ) @@ -259,7 +260,7 @@ func getSlotPageBlockData(blockData *rpctypes.CombinedBlockResponse, assignments Slot: uint64(attestation.Data.Slot), CommitteeIndex: uint64(attestation.Data.Index), AggregationBits: attestation.AggregationBits, - Validators: make([]models.SlotPageValidator, len(attAssignments)), + Validators: make([]types.NamedValidator, len(attAssignments)), Signature: attestation.Signature, BeaconBlockRoot: attestation.Data.BeaconBlockRoot, SourceEpoch: uint64(attestation.Data.Source.Epoch), @@ -268,7 +269,7 @@ func getSlotPageBlockData(blockData *rpctypes.CombinedBlockResponse, assignments TargetRoot: attestation.Data.Target.Root, } for j := 0; j < len(attAssignments); j++ { - attPageData.Validators[j] = models.SlotPageValidator{ + attPageData.Validators[j] = types.NamedValidator{ Index: attAssignments[j], Name: services.GlobalBeaconService.GetValidatorName(attAssignments[j]), } @@ -320,7 +321,7 @@ func getSlotPageBlockData(blockData *rpctypes.CombinedBlockResponse, assignments Attestation2SourceRoot: slashing.Attestation2.Data.Source.Root, Attestation2TargetEpoch: uint64(slashing.Attestation2.Data.Target.Epoch), Attestation2TargetRoot: slashing.Attestation2.Data.Target.Root, - SlashedValidators: make([]models.SlotPageValidator, 0), + SlashedValidators: make([]types.NamedValidator, 0), } pageData.AttesterSlashings[i] = slashingData for j := range slashing.Attestation1.AttestingIndices { @@ -332,7 +333,7 @@ func getSlotPageBlockData(blockData *rpctypes.CombinedBlockResponse, assignments inter := intersect.Simple(slashing.Attestation1.AttestingIndices, slashing.Attestation2.AttestingIndices) for _, j := range inter { valIdx := uint64(j.(rpctypes.Uint64Str)) - slashingData.SlashedValidators = append(slashingData.SlashedValidators, models.SlotPageValidator{ + slashingData.SlashedValidators = append(slashingData.SlashedValidators, types.NamedValidator{ Index: valIdx, Name: services.GlobalBeaconService.GetValidatorName(valIdx), }) @@ -364,9 +365,15 @@ func getSlotPageBlockData(blockData *rpctypes.CombinedBlockResponse, assignments pageData.SyncAggregateBits = syncAggregate.SyncCommitteeBits pageData.SyncAggregateSignature = syncAggregate.SyncCommitteeSignature if assignments != nil { - pageData.SyncAggCommittee = assignments.SyncAssignments + pageData.SyncAggCommittee = make([]types.NamedValidator, len(assignments.SyncAssignments)) + for idx, vidx := range assignments.SyncAssignments { + pageData.SyncAggCommittee[idx] = types.NamedValidator{ + Index: vidx, + Name: services.GlobalBeaconService.GetValidatorName(vidx), + } + } } else { - pageData.SyncAggCommittee = []uint64{} + pageData.SyncAggCommittee = []types.NamedValidator{} } pageData.SyncAggParticipation = utils.SyncCommitteeParticipation(pageData.SyncAggregateBits) } diff --git a/templates/slot/attestations.html b/templates/slot/attestations.html index 06f61866..fae9c5d8 100644 --- a/templates/slot/attestations.html +++ b/templates/slot/attestations.html @@ -15,7 +15,7 @@
Aggregation Bits:
-
{{ formatBitlist $attestation.AggregationBits }}
+
{{ formatBitlist $attestation.AggregationBits $attestation.Validators }}
Validators:
diff --git a/types/models.go b/types/models.go index 41423078..1a73f20e 100644 --- a/types/models.go +++ b/types/models.go @@ -68,3 +68,8 @@ type Meta struct { type Empty struct { } + +type NamedValidator struct { + Index uint64 `json:"index"` + Name string `json:"name"` +} diff --git a/types/models/slot.go b/types/models/slot.go index 5a85db52..9ddb8b29 100644 --- a/types/models/slot.go +++ b/types/models/slot.go @@ -1,6 +1,10 @@ package models -import "time" +import ( + "time" + + "github.com/pk910/light-beaconchain-explorer/types" +) // SlotPageData is a struct to hold info for the slot details page type SlotPageData struct { @@ -27,26 +31,26 @@ const ( ) type SlotPageBlockData struct { - BlockRoot []byte `json:"blockroot"` - ParentRoot []byte `json:"parentroot"` - StateRoot []byte `json:"stateroot"` - Signature []byte `json:"signature"` - RandaoReveal []byte `json:"randaoreveal"` - Graffiti []byte `json:"graffiti"` - Eth1dataDepositroot []byte `json:"eth1data_depositroot"` - Eth1dataDepositcount uint64 `json:"eth1data_depositcount"` - Eth1dataBlockhash []byte `json:"eth1data_blockhash"` - SyncAggregateBits []byte `json:"syncaggregate_bits"` - SyncAggregateSignature []byte `json:"syncaggregate_signature"` - SyncAggParticipation float64 `json:"syncaggregate_participation"` - SyncAggCommittee []uint64 `json:"syncaggregate_committee"` - ProposerSlashingsCount uint64 `json:"proposer_slashings_count"` - AttesterSlashingsCount uint64 `json:"attester_slashings_count"` - AttestationsCount uint64 `json:"attestations_count"` - DepositsCount uint64 `json:"deposits_count"` - WithdrawalsCount uint64 `json:"withdrawals_count"` - BLSChangesCount uint64 `json:"bls_changes_count"` - VoluntaryExitsCount uint64 `json:"voluntaryexits_count"` + BlockRoot []byte `json:"blockroot"` + ParentRoot []byte `json:"parentroot"` + StateRoot []byte `json:"stateroot"` + Signature []byte `json:"signature"` + RandaoReveal []byte `json:"randaoreveal"` + Graffiti []byte `json:"graffiti"` + Eth1dataDepositroot []byte `json:"eth1data_depositroot"` + Eth1dataDepositcount uint64 `json:"eth1data_depositcount"` + Eth1dataBlockhash []byte `json:"eth1data_blockhash"` + SyncAggregateBits []byte `json:"syncaggregate_bits"` + SyncAggregateSignature []byte `json:"syncaggregate_signature"` + SyncAggParticipation float64 `json:"syncaggregate_participation"` + SyncAggCommittee []types.NamedValidator `json:"syncaggregate_committee"` + ProposerSlashingsCount uint64 `json:"proposer_slashings_count"` + AttesterSlashingsCount uint64 `json:"attester_slashings_count"` + AttestationsCount uint64 `json:"attestations_count"` + DepositsCount uint64 `json:"deposits_count"` + WithdrawalsCount uint64 `json:"withdrawals_count"` + BLSChangesCount uint64 `json:"bls_changes_count"` + VoluntaryExitsCount uint64 `json:"voluntaryexits_count"` SlashingsCount uint64 BlobsCount uint64 `json:"blobs_count"` @@ -83,8 +87,8 @@ type SlotPageAttestation struct { Slot uint64 `json:"slot"` CommitteeIndex uint64 `json:"committeeindex"` - AggregationBits []byte `json:"aggregationbits"` - Validators []SlotPageValidator `json:"validators"` + AggregationBits []byte `json:"aggregationbits"` + Validators []types.NamedValidator `json:"validators"` Signature []byte `json:"signature"` @@ -95,11 +99,6 @@ type SlotPageAttestation struct { TargetRoot []byte `json:"target_root"` } -type SlotPageValidator struct { - Index uint64 `json:"index"` - Name string `json:"name"` -} - type SlotPageDeposit struct { PublicKey []byte `json:"publickey"` Withdrawalcredentials []byte `json:"withdrawalcredentials"` @@ -116,25 +115,25 @@ type SlotPageVoluntaryExit struct { // BlockPageAttesterSlashing is a struct to hold data for attester slashings on the block page type SlotPageAttesterSlashing struct { - Attestation1Indices []uint64 `json:"attestation1_indices"` - Attestation1Signature []byte `json:"attestation1_signature"` - Attestation1Slot uint64 `json:"attestation1_slot"` - Attestation1Index uint64 `json:"attestation1_index"` - Attestation1BeaconBlockRoot []byte `json:"attestation1_beaconblockroot"` - Attestation1SourceEpoch uint64 `json:"attestation1_source_epoch"` - Attestation1SourceRoot []byte `json:"attestation1_source_root"` - Attestation1TargetEpoch uint64 `json:"attestation1_target_epoch"` - Attestation1TargetRoot []byte `json:"attestation1_target_root"` - Attestation2Indices []uint64 `json:"attestation2_indices"` - Attestation2Signature []byte `json:"attestation2_signature"` - Attestation2Slot uint64 `json:"attestation2_slot"` - Attestation2Index uint64 `json:"attestation2_index"` - Attestation2BeaconBlockRoot []byte `json:"attestation2_beaconblockroot"` - Attestation2SourceEpoch uint64 `json:"attestation2_source_epoch"` - Attestation2SourceRoot []byte `json:"attestation2_source_root"` - Attestation2TargetEpoch uint64 `json:"attestation2_target_epoch"` - Attestation2TargetRoot []byte `json:"attestation2_target_root"` - SlashedValidators []SlotPageValidator `json:"validators"` + Attestation1Indices []uint64 `json:"attestation1_indices"` + Attestation1Signature []byte `json:"attestation1_signature"` + Attestation1Slot uint64 `json:"attestation1_slot"` + Attestation1Index uint64 `json:"attestation1_index"` + Attestation1BeaconBlockRoot []byte `json:"attestation1_beaconblockroot"` + Attestation1SourceEpoch uint64 `json:"attestation1_source_epoch"` + Attestation1SourceRoot []byte `json:"attestation1_source_root"` + Attestation1TargetEpoch uint64 `json:"attestation1_target_epoch"` + Attestation1TargetRoot []byte `json:"attestation1_target_root"` + Attestation2Indices []uint64 `json:"attestation2_indices"` + Attestation2Signature []byte `json:"attestation2_signature"` + Attestation2Slot uint64 `json:"attestation2_slot"` + Attestation2Index uint64 `json:"attestation2_index"` + Attestation2BeaconBlockRoot []byte `json:"attestation2_beaconblockroot"` + Attestation2SourceEpoch uint64 `json:"attestation2_source_epoch"` + Attestation2SourceRoot []byte `json:"attestation2_source_root"` + Attestation2TargetEpoch uint64 `json:"attestation2_target_epoch"` + Attestation2TargetRoot []byte `json:"attestation2_target_root"` + SlashedValidators []types.NamedValidator `json:"validators"` } // BlockPageProposerSlashing is a struct to hold data for proposer slashings on the block page diff --git a/utils/format.go b/utils/format.go index 798e5125..bedf4aae 100644 --- a/utils/format.go +++ b/utils/format.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pk910/light-beaconchain-explorer/types" "github.com/prysmaticlabs/go-bitfield" "golang.org/x/text/language" "golang.org/x/text/message" @@ -67,12 +68,12 @@ func FormatAddCommas(n uint64) template.HTML { return template.HTML(number) } -func FormatBitlist(b []byte) template.HTML { +func FormatBitlist(b []byte, v []types.NamedValidator) template.HTML { p := bitfield.Bitlist(b) - return formatBits(p.BytesNoTrim(), int(p.Len())) + return formatBits(p.BytesNoTrim(), int(p.Len()), v) } -func formatBits(b []byte, length int) template.HTML { +func formatBits(b []byte, length int, v []types.NamedValidator) template.HTML { var buf strings.Builder buf.WriteString("
") perLine := 8 @@ -88,12 +89,23 @@ func formatBits(b []byte, length int) template.HTML { } buf.WriteString("") } + if v != nil { + val := v[x] + if val.Name != "" { + buf.WriteString(fmt.Sprintf(``, val.Name, val.Index)) + } else { + buf.WriteString(fmt.Sprintf(``, val.Index)) + } + } bit := BitAtVector(b, x) if bit { buf.WriteString("1") } else { buf.WriteString("0") } + if v != nil { + buf.WriteString("") + } } buf.WriteString("
") } @@ -101,7 +113,7 @@ func formatBits(b []byte, length int) template.HTML { return template.HTML(buf.String()) } -func formatBitvectorValidators(bits []byte, validators []uint64) template.HTML { +func formatBitvectorValidators(bits []byte, validators []types.NamedValidator) template.HTML { invalidLen := false if len(bits)*8 != len(validators) { invalidLen = true @@ -117,11 +129,17 @@ func formatBitvectorValidators(bits []byte, validators []uint64) template.HTML { } } else { val := validators[i] + if val.Name != "" { + buf.WriteString(fmt.Sprintf(``, val.Name, val.Index)) + } else { + buf.WriteString(fmt.Sprintf(``, val.Index)) + } if BitAtVector(bits, i) { - buf.WriteString(fmt.Sprintf("1", val)) + buf.WriteString("1") } else { - buf.WriteString(fmt.Sprintf("0", val)) + buf.WriteString("0") } + buf.WriteString("") } if (i+1)%64 == 0 { @@ -185,7 +203,7 @@ func formatAmount(amount *big.Int, unit string, digits int, maxPreCommaDigitsBef trimmedAmount, fullAmount := trimAmount(amount, unitDigits, maxPreCommaDigitsBeforeTrim, digits, false) tooltip := "" if fullAmountTooltip { - tooltip = fmt.Sprintf(` data-toggle="tooltip" data-placement="top" title="%s"`, fullAmount) + tooltip = fmt.Sprintf(` data-bs-toggle="tooltip" data-bs-placement="top" title="%s"`, fullAmount) } // done, convert to HTML & return