Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions handlers/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,19 @@ func buildIndexPageRecentEpochsData(ctx context.Context, pageData *models.IndexP
pageData.RecentEpochCount = uint64(len(pageData.RecentEpochs))
}

// resolveBuildSource maps a db builder index (-1 = self-built) to the model fields
// driving the proposer build-source icon (house / hard-hat), matching the slots page.
func resolveBuildSource(dbBuilderIndex int64) (hasBuilder bool, builderIndex uint64, builderURL string) {
if dbBuilderIndex == -1 {
return true, math.MaxUint64, ""
}
if dbBuilderIndex < 0 {
return false, 0, ""
}
builderIndex = uint64(dbBuilderIndex)
return true, builderIndex, services.GlobalBeaconService.GetBuilderURL(builderIndex)
}

func buildIndexPageRecentBlocksData(ctx context.Context, pageData *models.IndexPageData, recentBlockCount int) {
pageData.RecentBlocks = make([]*models.IndexPageDataBlocks, 0)

Expand Down Expand Up @@ -478,10 +491,11 @@ func buildIndexPageRecentBlocksData(ctx context.Context, pageData *models.IndexP
Ts: chainState.SlotToTime(phase0.Slot(blockData.Slot)),
Proposer: blockData.Proposer,
ProposerName: services.GlobalBeaconService.GetValidatorNameAt(blockData.Proposer, phase0.Slot(blockData.Slot)),
Status: uint64(blockData.Status),
Status: uint8(blockData.Status),
PayloadStatus: uint8(payloadStatus),
BlockRoot: blockData.Root,
}
blockModel.HasBuilder, blockModel.BuilderIndex, blockModel.BuilderURL = resolveBuildSource(blockData.BuilderIndex)
if blockData.EthBlockNumber != nil {
blockModel.WithEthBlock = true
blockModel.EthBlock = *blockData.EthBlockNumber
Expand Down Expand Up @@ -531,7 +545,7 @@ func buildIndexPageRecentSlotsData(ctx context.Context, pageData *models.IndexPa
Slot: slot,
Epoch: uint64(epoch),
Ts: chainState.SlotToTime(phase0.Slot(slot)),
Status: uint64(dbSlot.Status),
Status: uint8(dbSlot.Status),
PayloadStatus: uint8(payloadStatus),
Safe: fcrEnabled && dbSlot.Status == dbtypes.Canonical && slot <= uint64(safeSlot),
Proposer: dbSlot.Proposer,
Expand All @@ -540,6 +554,9 @@ func buildIndexPageRecentSlotsData(ctx context.Context, pageData *models.IndexPa
ParentRoot: dbSlot.ParentRoot,
ForkGraph: make([]*models.IndexPageDataForkGraph, 0),
}
if dbSlot.Status > 0 {
slotData.HasBuilder, slotData.BuilderIndex, slotData.BuilderURL = resolveBuildSource(dbSlot.BuilderIndex)
}
pageData.RecentSlots = append(pageData.RecentSlots, slotData)
blockCount++
buildIndexPageSlotGraph(slotData, &maxOpenFork, openForks)
Expand Down
41 changes: 41 additions & 0 deletions static/js/page-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
formatEth: function(x) { return formatFloat(x / 1000000000, 4); },
formatFloat: function(x) { return formatFloat(x, 2); },
formatValidator: function(idx, name) { return formatValidator(idx, name); },
formatProposerWithBuildSource: function(status, idx, name, hasBuilder, builderIdx, builderUrl) { return formatProposerWithBuildSource(status, idx, name, hasBuilder, builderIdx, builderUrl); },
hexstr: function(x) { return "0x" + base64ToHex(x); },
slotStatusTooltip: function(status, payloadStatus) {
var bs = ["Missed", "Canonical", "Orphaned"][status] || "Unknown";
Expand Down Expand Up @@ -210,6 +211,8 @@

function formatValidator(idx, name) {
var icon = "fa-male mr-2";
// MaxInt64 = "unknown proposer" sentinel. It exceeds 2^53, so JSON.parse rounds it;
// >= against the exact BigInt matches the rounded value (== would never match).
if(idx >= 9223372036854775807n) {
return `<span class="validator-label validator-index"><i class="fas ` + icon + `"></i> unknown</span>`;
}
Expand All @@ -219,6 +222,44 @@
return `<span class="validator-label validator-index"><i class="fas ` + icon + `"></i> <a href="/validator/` + idx + `">` + idx + `</a></span>`
}

// mirrors utils.FormatProposerWithBuildSource: house = self-built payload,
// hard-hat (linking to the builder) = builder-built payload
function formatProposerWithBuildSource(status, idx, name, hasBuilder, builderIdx, builderUrl) {
if(status == 0 || idx >= 9223372036854775807n) {
if(idx >= 9223372036854775807n) {
return `<span class="validator-label validator-index">unknown</span>`;
}
if(name != "") {
return `<span class="validator-label validator-name"><a href="/validator/` + idx + `">` + escapeHtml(name) + `</a></span>`;
}
return `<span class="validator-label validator-index"><a href="/validator/` + idx + `">` + idx + `</a></span>`;
}

if(!hasBuilder) {
return formatValidator(idx, name);
}

var iconHtml;
// MaxUint64 = "self-built payload" sentinel (db builder_index -1); rounded by JSON.parse
// above 2^53 like the proposer sentinel, hence >= against the exact BigInt.
if(builderIdx >= 18446744073709551615n) {
iconHtml = `<i class="fas fa-house mr-2" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Self-built payload"></i>`;
} else {
var builderLink = "/builder/" + builderIdx;
var external = "";
if(builderUrl) {
builderLink = escapeHtml(builderUrl);
external = ` target="_blank" rel="noopener noreferrer"`;
}
iconHtml = `<a href="` + builderLink + `"` + external + ` class="builder-source-link" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Builder-built payload (builder ` + builderIdx + `)"><i class="fas fa-hard-hat mr-2"></i></a>`;
}

if(name != "") {
return `<span class="validator-label validator-name">` + iconHtml + ` <a href="/validator/` + idx + `">` + escapeHtml(name) + `</a></span>`;
}
return `<span class="validator-label validator-index">` + iconHtml + ` <a href="/validator/` + idx + `">` + idx + `</a></span>`;
}

function base64ToHex(str) {
const raw = atob(str);
let result = '';
Expand Down
4 changes: 2 additions & 2 deletions templates/index/recentBlocks.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ <h5 class="card-title d-flex justify-content-between align-items-center" style="
<td data-bind="attr: {'data-timer': $root.unixtime(ts)}">
<span data-bs-toggle="tooltip" data-bs-placement="top" data-bind="attr: {'data-bs-title': $root.timestamp(ts)}, text: $root.formatRecentTimeShort(ts)"></span>
</td>
<td data-bind="html: $root.formatValidator(proposer, proposer_name)"></td>
<td data-bind="html: $root.formatProposerWithBuildSource(status, proposer, proposer_name, has_builder, builder_index, builder_url)"></td>
</tr>
{{ html "<!-- /ko -->" }}
{{ html "<!-- ko if: blocks().length == 0 -->" }}
Expand Down Expand Up @@ -82,7 +82,7 @@ <h5 class="card-title d-flex justify-content-between align-items-center" style="
{{ end }}
</td>
<td data-timer="{{ $block.Ts.Unix }}"><span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="{{ $block.Ts }}">{{ formatRecentTimeShort $block.Ts }}</span></td>
<td>{{ formatValidator $block.Proposer $block.ProposerName }}</td>
<td>{{ formatProposerWithBuildSource $block.Status $block.Proposer $block.ProposerName $block.HasBuilder $block.BuilderIndex $block.BuilderURL }}</td>
</tr>
{{ end }}
{{ else }}
Expand Down
4 changes: 2 additions & 2 deletions templates/index/recentSlots.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ <h5 class="card-title d-flex justify-content-between align-items-center" style="
</td>
<td>
<span data-bind="if: slot > 0">
<span data-bind="html: $root.formatValidator(proposer, proposer_name)"></span>
<span data-bind="html: $root.formatProposerWithBuildSource(status, proposer, proposer_name, has_builder, builder_index, builder_url)"></span>
</span>
</td>
</tr>
Expand Down Expand Up @@ -109,7 +109,7 @@ <h5 class="card-title d-flex justify-content-between align-items-center" style="
{{ end }}
</td>
<td data-timer="{{ $slot.Ts.Unix }}"><span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="{{ $slot.Ts }}">{{ formatRecentTimeShort $slot.Ts }}</span></td>
<td>{{ if gt $slot.Slot 0 }}{{ formatValidator $slot.Proposer $slot.ProposerName }}{{ end }}</td>
<td>{{ if gt $slot.Slot 0 }}{{ formatProposerWithBuildSource $slot.Status $slot.Proposer $slot.ProposerName $slot.HasBuilder $slot.BuilderIndex $slot.BuilderURL }}{{ end }}</td>
</tr>
{{ end }}
{{ else }}
Expand Down
10 changes: 8 additions & 2 deletions types/models/indexPage.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ type IndexPageDataBlocks struct {
Ts time.Time `json:"ts"`
Proposer uint64 `json:"proposer"`
ProposerName string `json:"proposer_name"`
Status uint64 `json:"status"`
Status uint8 `json:"status"`
PayloadStatus uint8 `json:"payload_status"`
HasBuilder bool `json:"has_builder"`
BuilderIndex uint64 `json:"builder_index"`
BuilderURL string `json:"builder_url"`
BlockRoot []byte `json:"block_root" ssz-size:"32"`
}

Expand All @@ -93,8 +96,11 @@ type IndexPageDataSlots struct {
Ts time.Time `json:"ts"`
Proposer uint64 `json:"proposer"`
ProposerName string `json:"proposer_name"`
Status uint64 `json:"status"`
Status uint8 `json:"status"`
PayloadStatus uint8 `json:"payload_status"`
HasBuilder bool `json:"has_builder"`
BuilderIndex uint64 `json:"builder_index"`
BuilderURL string `json:"builder_url"`
Safe bool `json:"safe"`
BlockRoot []byte `json:"block_root" ssz-size:"32"`
ParentRoot []byte `json:"parent_root" ssz-size:"32"`
Expand Down