Skip to content

Commit

Permalink
show validator names as popover in aggregation bitsets
Browse files Browse the repository at this point in the history
  • Loading branch information
pk910 committed Aug 5, 2023
1 parent b55bd0d commit 7b3332a
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 61 deletions.
19 changes: 13 additions & 6 deletions handlers/slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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),
Expand All @@ -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]),
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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),
})
Expand Down Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion templates/slot/attestations.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</div>
<div class="row border-bottom p-1 mx-0">
<div class="col-md-2"><span data-bs-toggle="tooltip" data-bs-placement="top" title="Represents the aggregated attestation of all participating validators in this attestation">Aggregation Bits:</span></div>
<div class="col-md-10">{{ formatBitlist $attestation.AggregationBits }}</div>
<div class="col-md-10">{{ formatBitlist $attestation.AggregationBits $attestation.Validators }}</div>
</div>
<div class="row border-bottom p-1 mx-0">
<div class="col-md-2"><span data-bs-toggle="tooltip" data-bs-placement="top" title="Validators who have submitted their attestation and have been included by the block proposer">Validators:</span></div>
Expand Down
5 changes: 5 additions & 0 deletions types/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ type Meta struct {

type Empty struct {
}

type NamedValidator struct {
Index uint64 `json:"index"`
Name string `json:"name"`
}
93 changes: 46 additions & 47 deletions types/models/slot.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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"`

Expand Down Expand Up @@ -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"`

Expand All @@ -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"`
Expand All @@ -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
Expand Down
32 changes: 25 additions & 7 deletions utils/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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("<div class=\"text-bitfield text-monospace\">")
perLine := 8
Expand All @@ -88,20 +89,31 @@ func formatBits(b []byte, length int) template.HTML {
}
buf.WriteString("<span>")
}
if v != nil {
val := v[x]
if val.Name != "" {
buf.WriteString(fmt.Sprintf(`<span data-bs-toggle="tooltip" data-bs-placement="top" title="%v (%v)">`, val.Name, val.Index))
} else {
buf.WriteString(fmt.Sprintf(`<span data-bs-toggle="tooltip" data-bs-placement="top" title="%v">`, val.Index))
}
}
bit := BitAtVector(b, x)
if bit {
buf.WriteString("1")
} else {
buf.WriteString("0")
}
if v != nil {
buf.WriteString("</span>")
}
}
buf.WriteString("</span><br/>")
}
buf.WriteString("</div>")
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
Expand All @@ -117,11 +129,17 @@ func formatBitvectorValidators(bits []byte, validators []uint64) template.HTML {
}
} else {
val := validators[i]
if val.Name != "" {
buf.WriteString(fmt.Sprintf(`<span data-bs-toggle="tooltip" data-bs-placement="top" title="%v (%v)">`, val.Name, val.Index))
} else {
buf.WriteString(fmt.Sprintf(`<span data-bs-toggle="tooltip" data-bs-placement="top" title="%v">`, val.Index))
}
if BitAtVector(bits, i) {
buf.WriteString(fmt.Sprintf("<span title=\"Validator %[1]d\" data-validx=\"%[1]d\">1</span>", val))
buf.WriteString("1")
} else {
buf.WriteString(fmt.Sprintf("<span title=\"Validator %[1]d\" data-validx=\"%[1]d\">0</span>", val))
buf.WriteString("0")
}
buf.WriteString("</span>")
}

if (i+1)%64 == 0 {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 7b3332a

Please sign in to comment.