diff --git a/handlers/index.go b/handlers/index.go index d08078d5..14af5df3 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -1,6 +1,7 @@ package handlers import ( + "bytes" "fmt" "math" "net/http" @@ -23,6 +24,7 @@ func Index(w http.ResponseWriter, r *http.Request) { "index/networkOverview.html", "index/recentBlocks.html", "index/recentEpochs.html", + "index/recentSlots.html", "_svg/professor.html", "_svg/timeline.html", ) @@ -52,8 +54,9 @@ func getIndexPageData() *models.IndexPageData { func buildIndexPageData() (*models.IndexPageData, time.Duration) { logrus.Printf("index page called") - recentEpochCount := 15 - recentBlockCount := 15 + recentEpochCount := 7 + recentBlockCount := 7 + recentSlotsCount := 16 // network overview now := time.Now() @@ -160,8 +163,20 @@ func buildIndexPageData() (*models.IndexPageData, time.Duration) { } // load recent epochs + buildIndexPageRecentEpochsData(pageData, uint64(currentEpoch), finalizedEpoch, recentEpochCount) + + // load recent blocks + buildIndexPageRecentBlocksData(pageData, currentSlot, recentBlockCount) + + // load recent slots + buildIndexPageRecentSlotsData(pageData, currentSlot, recentSlotsCount) + + return pageData, 12 * time.Second +} + +func buildIndexPageRecentEpochsData(pageData *models.IndexPageData, currentEpoch uint64, finalizedEpoch int64, recentEpochCount int) { pageData.RecentEpochs = make([]*models.IndexPageDataEpochs, 0) - epochsData := services.GlobalBeaconService.GetDbEpochs(uint64(currentEpoch), uint32(recentEpochCount)) + epochsData := services.GlobalBeaconService.GetDbEpochs(currentEpoch, uint32(recentEpochCount)) for i := 0; i < len(epochsData); i++ { epochData := epochsData[i] if epochData == nil { @@ -183,8 +198,9 @@ func buildIndexPageData() (*models.IndexPageData, time.Duration) { }) } pageData.RecentEpochCount = uint64(len(pageData.RecentEpochs)) +} - // load recent blocks +func buildIndexPageRecentBlocksData(pageData *models.IndexPageData, currentSlot uint64, recentBlockCount int) { pageData.RecentBlocks = make([]*models.IndexPageDataBlocks, 0) blocksData := services.GlobalBeaconService.GetDbBlocks(uint64(currentSlot), int32(recentBlockCount), false) for i := 0; i < len(blocksData); i++ { @@ -204,10 +220,149 @@ func buildIndexPageData() (*models.IndexPageData, time.Duration) { Proposer: blockData.Proposer, ProposerName: services.GlobalBeaconService.GetValidatorName(blockData.Proposer), Status: uint64(blockStatus), - BlockRoot: fmt.Sprintf("0x%x", blockData.Root), + BlockRoot: blockData.Root, }) } pageData.RecentBlockCount = uint64(len(pageData.RecentBlocks)) +} - return pageData, 12 * time.Second +func buildIndexPageRecentSlotsData(pageData *models.IndexPageData, firstSlot uint64, slotLimit int) { + var lastSlot uint64 + if firstSlot >= uint64(slotLimit) { + lastSlot = firstSlot - uint64(slotLimit) + } else { + lastSlot = 0 + } + + // get slot assignments + firstEpoch := utils.EpochOfSlot(firstSlot) + lastEpoch := utils.EpochOfSlot(lastSlot) + slotAssignments, _ := services.GlobalBeaconService.GetProposerAssignments(firstEpoch, lastEpoch) + + // load slots + pageData.RecentSlots = make([]*models.IndexPageDataSlots, 0) + dbSlots := services.GlobalBeaconService.GetDbBlocksForSlots(uint64(firstSlot), uint32(slotLimit), true) + dbIdx := 0 + dbCnt := len(dbSlots) + blockCount := uint64(0) + openForks := map[int][]byte{} + maxOpenFork := 0 + for slotIdx := int64(firstSlot); slotIdx >= int64(lastSlot); slotIdx-- { + slot := uint64(slotIdx) + haveBlock := false + for dbIdx < dbCnt && dbSlots[dbIdx] != nil && dbSlots[dbIdx].Slot == slot { + dbSlot := dbSlots[dbIdx] + dbIdx++ + blockStatus := uint64(1) + if dbSlot.Orphaned { + blockStatus = 2 + } + + slotData := &models.IndexPageDataSlots{ + Slot: slot, + Epoch: utils.EpochOfSlot(slot), + Ts: utils.SlotToTime(slot), + Status: blockStatus, + Proposer: dbSlot.Proposer, + ProposerName: services.GlobalBeaconService.GetValidatorName(dbSlot.Proposer), + BlockRoot: dbSlot.Root, + ParentRoot: dbSlot.ParentRoot, + ForkGraph: make([]*models.IndexPageDataForkGraph, 0), + } + pageData.RecentSlots = append(pageData.RecentSlots, slotData) + blockCount++ + haveBlock = true + buildIndexPageSlotGraph(pageData, slotData, &maxOpenFork, openForks) + } + + if !haveBlock { + epoch := utils.EpochOfSlot(slot) + slotData := &models.IndexPageDataSlots{ + Slot: slot, + Epoch: epoch, + Ts: utils.SlotToTime(slot), + Status: 0, + Proposer: slotAssignments[slot], + ProposerName: services.GlobalBeaconService.GetValidatorName(slotAssignments[slot]), + ForkGraph: make([]*models.IndexPageDataForkGraph, 0), + } + pageData.RecentSlots = append(pageData.RecentSlots, slotData) + blockCount++ + buildIndexPageSlotGraph(pageData, slotData, &maxOpenFork, openForks) + } + } + pageData.RecentSlotCount = uint64(blockCount) + +} + +func buildIndexPageSlotGraph(pageData *models.IndexPageData, slotData *models.IndexPageDataSlots, maxOpenFork *int, openForks map[int][]byte) { + // fork tree + var forkGraphIdx int = -1 + var freeForkIdx int = -1 + getForkGraph := func(slotData *models.IndexPageDataSlots, forkIdx int) *models.IndexPageDataForkGraph { + forkGraph := &models.IndexPageDataForkGraph{} + graphCount := len(slotData.ForkGraph) + if graphCount > forkIdx { + forkGraph = slotData.ForkGraph[forkIdx] + } else { + for graphCount <= forkIdx { + forkGraph = &models.IndexPageDataForkGraph{ + Index: graphCount, + Left: 10 + (graphCount * 20), + Tiles: map[string]bool{}, + } + slotData.ForkGraph = append(slotData.ForkGraph, forkGraph) + graphCount++ + } + } + return forkGraph + } + + for forkIdx := 0; forkIdx < *maxOpenFork; forkIdx++ { + forkGraph := getForkGraph(slotData, forkIdx) + if openForks[forkIdx] == nil { + if freeForkIdx == -1 { + freeForkIdx = forkIdx + } + continue + } else { + forkGraph.Tiles["vline"] = true + if bytes.Equal(openForks[forkIdx], slotData.BlockRoot) { + if forkGraphIdx != -1 { + continue + } + forkGraphIdx = forkIdx + openForks[forkIdx] = slotData.ParentRoot + forkGraph.Block = true + for targetIdx := forkIdx + 1; targetIdx < *maxOpenFork; targetIdx++ { + if openForks[targetIdx] == nil || !bytes.Equal(openForks[targetIdx], slotData.BlockRoot) { + continue + } + for idx := forkIdx + 1; idx <= targetIdx; idx++ { + splitGraph := getForkGraph(slotData, idx) + if idx == targetIdx { + splitGraph.Tiles["tline"] = true + splitGraph.Tiles["lline"] = true + splitGraph.Tiles["fork"] = true + } else { + splitGraph.Tiles["hline"] = true + } + } + forkGraph.Tiles["rline"] = true + openForks[targetIdx] = nil + } + } + } + } + if forkGraphIdx == -1 && slotData.Status > 0 { + // fork head + if freeForkIdx == -1 { + freeForkIdx = *maxOpenFork + *maxOpenFork++ + } + openForks[freeForkIdx] = slotData.ParentRoot + forkGraph := getForkGraph(slotData, freeForkIdx) + forkGraph.Block = true + forkGraph.Tiles["bline"] = true + } } diff --git a/templates/index/index.html b/templates/index/index.html index 69a20d9f..daa501f6 100644 --- a/templates/index/index.html +++ b/templates/index/index.html @@ -3,10 +3,17 @@ {{ template "networkOverview" . }}
- {{ template "recentEpochs" . }} +
+ {{ template "recentEpochs" . }} +
+
+ {{ template "recentBlocks" . }} +
- {{ template "recentBlocks" . }} +
+ {{ template "recentSlots" . }} +
@@ -14,4 +21,20 @@ {{ define "js" }} {{ end }} {{ define "css" }} + + {{ end }} \ No newline at end of file diff --git a/templates/index/recentBlocks.html b/templates/index/recentBlocks.html index 019fcf6d..569eacdc 100644 --- a/templates/index/recentBlocks.html +++ b/templates/index/recentBlocks.html @@ -1,10 +1,10 @@ {{ define "recentBlocks" }}
-

+

Most recent blocks View more -
+
@@ -25,7 +25,7 @@

{{ formatAddCommas $block.Epoch }} {{ if eq .Status 2 }} - {{ formatAddCommas $block.Slot }} + {{ formatAddCommas $block.Slot }} {{ else }} {{ formatAddCommas $block.Slot }} {{ end }} diff --git a/templates/index/recentEpochs.html b/templates/index/recentEpochs.html index 0173e883..cfa0e9f9 100644 --- a/templates/index/recentEpochs.html +++ b/templates/index/recentEpochs.html @@ -1,10 +1,10 @@ {{ define "recentEpochs" }}
-

+

Most recent epochs View more -
+

diff --git a/templates/index/recentSlots.html b/templates/index/recentSlots.html new file mode 100644 index 00000000..3511da2e --- /dev/null +++ b/templates/index/recentSlots.html @@ -0,0 +1,82 @@ +{{ define "recentSlots" }} +
+
+
+ Most recent slots + View more +
+
+
+
+ + + + + + + + + + + + {{ if gt .RecentSlotCount 0 }} + + {{ $treeWidth := .ForkTreeWidth }} + {{ range $i, $slot := .RecentSlots }} + + + + {{ if eq .Status 2 }} + + {{ else }} + + {{ end }} + + + + + {{ end }} + + {{ else }} + + + + + + + + {{ end }} +
ChainEpochSlotStatusTimeProposer
+ {{ range $j, $graph := $slot.ForkGraph }} +
+ {{- range $tile, $val := $graph.Tiles -}} +
+ {{- end -}} + {{- if $graph.Block }} +
+ +
+ {{ end -}} +
+ {{ end }} +
{{ formatAddCommas $slot.Epoch }}{{ formatAddCommas $slot.Slot }}{{ formatAddCommas $slot.Slot }} + {{ if eq $slot.Slot 0 }} + Genesis + {{ else if eq .Status 0 }} + Missed + {{ else if eq .Status 1 }} + Proposed + {{ else if eq .Status 2 }} + Missed (Orphaned) + {{ else }} + Unknown + {{ end }} + {{ formatRecentTimeShort $slot.Ts }}{{ formatValidator $slot.Proposer $slot.ProposerName }}
+
+ {{ template "timeline_svg" }} +
+
+
+
+
+{{ end }} diff --git a/templates/slots/slots.html b/templates/slots/slots.html index f31f9cc4..508babba 100644 --- a/templates/slots/slots.html +++ b/templates/slots/slots.html @@ -51,7 +51,7 @@

Slots

{{ if .ShowForkTree -}} - Graph + Chain {{- end }} Epoch Slot diff --git a/types/models/indexPage.go b/types/models/indexPage.go index 757cc298..52246f41 100644 --- a/types/models/indexPage.go +++ b/types/models/indexPage.go @@ -32,6 +32,9 @@ type IndexPageData struct { RecentBlockCount uint64 `json:"recent_block_count"` RecentEpochs []*IndexPageDataEpochs `json:"recent_epochs"` RecentEpochCount uint64 `json:"recent_epoch_count"` + RecentSlots []*IndexPageDataSlots `json:"recent_slots"` + RecentSlotCount uint64 `json:"recent_slot_count"` + ForkTreeWidth int `json:"forktree_width"` } type IndexPageDataForks struct { @@ -60,5 +63,25 @@ type IndexPageDataBlocks struct { Proposer uint64 `json:"proposer"` ProposerName string `json:"proposer_name"` Status uint64 `json:"status"` - BlockRoot string `json:"block_root"` + BlockRoot []byte `json:"block_root"` +} + +type IndexPageDataSlots struct { + Epoch uint64 `json:"epoch"` + Slot uint64 `json:"slot"` + EthBlock uint64 `json:"eth_block"` + Ts time.Time `json:"ts"` + Proposer uint64 `json:"proposer"` + ProposerName string `json:"proposer_name"` + Status uint64 `json:"status"` + BlockRoot []byte `json:"block_root"` + ParentRoot []byte `json:"parent_root"` + ForkGraph []*IndexPageDataForkGraph `json:"fork_graph"` +} + +type IndexPageDataForkGraph struct { + Index int `json:"index"` + Left int `json:"left"` + Tiles map[string]bool `json:"tiles"` + Block bool `json:"block"` }