Skip to content

Commit 05ecc71

Browse files
authored
Merge pull request #748 from ethpandaops/bbusa/builder-blocks-filter
feat(blocks/slots): builder-block filter + gloas-aware MEV/Builder columns
2 parents ce5ef9f + 09635b2 commit 05ecc71

14 files changed

Lines changed: 289 additions & 80 deletions

File tree

.hack/devnet/run.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ BUILDOOR_CONTAINER=$(docker ps -aq -f "label=kurtosis_enclave_uuid=$ENCLAVE_UUID
4848
-f "label=com.kurtosistech.id=buildoor" | head -1)
4949
if [ -n "$BUILDOOR_CONTAINER" ]; then
5050
BUILDOOR_PORT=$(docker inspect --format='{{ (index (index .NetworkSettings.Ports "8080/tcp") 0).HostPort }}' "$BUILDOOR_CONTAINER" 2>/dev/null)
51+
# kurtosis service name (e.g. "buildoor") so dora shows it instead of the forwarded IP:port
52+
BUILDOOR_NAME=$(docker inspect --format='{{ index .Config.Labels "kurtosis_service_name" }}' "$BUILDOOR_CONTAINER" 2>/dev/null)
53+
if [ -z "$BUILDOOR_NAME" ]; then BUILDOOR_NAME="buildoor"; fi
5154
if [ -n "$BUILDOOR_PORT" ]; then
5255
BUILDOOR_CONFIG="buildoorUrls:
53-
- \"http://127.0.0.1:${BUILDOOR_PORT}\""
56+
- \"${BUILDOOR_NAME}|http://127.0.0.1:${BUILDOOR_PORT}\""
5457
fi
5558
fi
5659

db/slots.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,20 @@ func GetFilteredSlots(ctx context.Context, filter *dbtypes.BlockFilter, firstSlo
479479
}
480480
if filter.BuilderIndex != nil {
481481
argIdx++
482-
fmt.Fprintf(&sql, ` AND slots.builder_index = $%v `, argIdx)
482+
if filter.InvertBuilder {
483+
fmt.Fprintf(&sql, ` AND slots.builder_index != $%v `, argIdx)
484+
} else {
485+
fmt.Fprintf(&sql, ` AND slots.builder_index = $%v `, argIdx)
486+
}
483487
args = append(args, *filter.BuilderIndex)
484488
}
489+
if filter.WithBuilderBlock == 1 {
490+
// builder-built blocks only (self-built blocks use builder_index = -1)
491+
fmt.Fprint(&sql, ` AND slots.builder_index >= 0 `)
492+
} else if filter.WithBuilderBlock == 2 {
493+
// self-built blocks only; exclude missing slots (status 0), which also default to builder_index = -1
494+
fmt.Fprint(&sql, ` AND slots.builder_index = -1 AND slots.status != 0 `)
495+
}
485496

486497
if filter.WithPayloadMask != dbtypes.PayloadStatusMaskAll {
487498
allowedPayloadStatuses := []dbtypes.PayloadStatus{}

dbtypes/other.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ type BlockFilter struct {
8080
EthBlockHash []byte // Filter by EL block hash
8181
EthBlockParentHash []byte // Filter by EL block parent hash
8282
BuilderIndex *int64 // Filter by builder index (-1 for self-built blocks)
83+
InvertBuilder bool // Invert the builder index filter (exclude matches)
8384
MinGasUsed *uint64 // Filter by minimum gas used
8485
MaxGasUsed *uint64 // Filter by maximum gas used
8586
MinGasLimit *uint64 // Filter by minimum gas limit
8687
MaxGasLimit *uint64 // Filter by maximum gas limit
8788
MinBlockSize *uint64 // Filter by minimum block size (bytes)
8889
MaxBlockSize *uint64 // Filter by maximum block size (bytes)
8990
WithMevBlock uint8 // 0=hide mev, 1=show all, 2=mev only
91+
WithBuilderBlock uint8 // 0=all (default), 1=builder-built only, 2=self-built only
9092
MinEpoch *uint64 // Filter by minimum epoch
9193
MaxEpoch *uint64 // Filter by maximum epoch
9294
MinSlot *uint64 // Filter by minimum slot (derived from MinEpoch)

handlers/blocks.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ func buildBlocksPageData(ctx context.Context, firstSlot uint64, pageSize uint64,
9494
}
9595
}
9696
if len(displayMap) == 0 {
97+
cs := services.GlobalBeaconService.GetChainState()
98+
gloasActive := cs.IsEip7732Enabled(cs.EpochOfSlot(cs.CurrentSlot()))
9799
displayMap = map[uint64]bool{
98100
1: true,
99101
2: true,
@@ -110,11 +112,11 @@ func buildBlocksPageData(ctx context.Context, firstSlot uint64, pageSize uint64,
110112
13: true,
111113
14: true,
112114
15: true,
113-
16: true,
115+
16: !gloasActive, // MEV Block (replaced by Builder once gloas is active)
114116
17: true,
115117
18: false,
116118
19: false,
117-
20: false, // Builder (hidden by default)
119+
20: gloasActive, // Builder (shown once gloas is active)
118120
}
119121
}
120122

handlers/blocks_filtered.go

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package handlers
33
import (
44
"context"
55
"fmt"
6+
"math"
67
"net/http"
78
"net/url"
89
"strconv"
@@ -51,6 +52,9 @@ func BlocksFiltered(w http.ResponseWriter, r *http.Request) {
5152
var minBlockSize string
5253
var maxBlockSize string
5354
var withMevBlock uint64
55+
var withBuilderBlock uint64
56+
var builder string
57+
var invertBuilder bool
5458
var minTxCount string
5559
var maxTxCount string
5660
var minBlobCount string
@@ -86,6 +90,15 @@ func BlocksFiltered(w http.ResponseWriter, r *http.Request) {
8690
if urlArgs.Has("f.mev") {
8791
withMevBlock, _ = strconv.ParseUint(urlArgs.Get("f.mev"), 10, 64)
8892
}
93+
if urlArgs.Has("f.builderblock") {
94+
withBuilderBlock, _ = strconv.ParseUint(urlArgs.Get("f.builderblock"), 10, 64)
95+
}
96+
if urlArgs.Has("f.builder") {
97+
builder = urlArgs.Get("f.builder")
98+
}
99+
if urlArgs.Has("f.binvert") {
100+
invertBuilder = urlArgs.Get("f.binvert") == "on"
101+
}
89102
if urlArgs.Has("f.mintx") {
90103
minTxCount = urlArgs.Get("f.mintx")
91104
}
@@ -111,7 +124,7 @@ func BlocksFiltered(w http.ResponseWriter, r *http.Request) {
111124
var pageError error
112125
pageError = services.GlobalCallRateLimiter.CheckCallLimit(r, 2)
113126
if pageError == nil {
114-
data.Data, pageError = getFilteredBlocksPageData(pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, uint8(withMevBlock), minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
127+
data.Data, pageError = getFilteredBlocksPageData(pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, uint8(withMevBlock), uint8(withBuilderBlock), builder, invertBuilder, minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
115128
}
116129
if pageError != nil {
117130
handlePageError(w, r, pageError)
@@ -123,11 +136,11 @@ func BlocksFiltered(w http.ResponseWriter, r *http.Request) {
123136
}
124137
}
125138

126-
func getFilteredBlocksPageData(pageIdx uint64, pageSize uint64, extradata string, invertextradata bool, minGasUsed string, maxGasUsed string, minGasLimit string, maxGasLimit string, minBlockSize string, maxBlockSize string, withMevBlock uint8, minTxCount string, maxTxCount string, minBlobCount string, maxBlobCount string, minEpoch string, maxEpoch string, displayColumns uint64) (*models.BlocksFilteredPageData, error) {
139+
func getFilteredBlocksPageData(pageIdx uint64, pageSize uint64, extradata string, invertextradata bool, minGasUsed string, maxGasUsed string, minGasLimit string, maxGasLimit string, minBlockSize string, maxBlockSize string, withMevBlock uint8, withBuilderBlock uint8, builder string, invertBuilder bool, minTxCount string, maxTxCount string, minBlobCount string, maxBlobCount string, minEpoch string, maxEpoch string, displayColumns uint64) (*models.BlocksFilteredPageData, error) {
127140
pageData := &models.BlocksFilteredPageData{}
128-
pageCacheKey := fmt.Sprintf("blocks_filtered:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v", pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, withMevBlock, minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
141+
pageCacheKey := fmt.Sprintf("blocks_filtered:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v", pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, withMevBlock, withBuilderBlock, builder, invertBuilder, minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
129142
pageRes, pageErr := services.GlobalFrontendCache.ProcessCachedPage(pageCacheKey, true, pageData, func(pageCall *services.FrontendCacheProcessingPage) interface{} {
130-
return buildFilteredBlocksPageData(pageCall.CallCtx, pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, withMevBlock, minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
143+
return buildFilteredBlocksPageData(pageCall.CallCtx, pageIdx, pageSize, extradata, invertextradata, minGasUsed, maxGasUsed, minGasLimit, maxGasLimit, minBlockSize, maxBlockSize, withMevBlock, withBuilderBlock, builder, invertBuilder, minTxCount, maxTxCount, minBlobCount, maxBlobCount, minEpoch, maxEpoch, displayColumns)
131144
})
132145
if pageErr == nil && pageRes != nil {
133146
resData, resOk := pageRes.(*models.BlocksFilteredPageData)
@@ -139,8 +152,9 @@ func getFilteredBlocksPageData(pageIdx uint64, pageSize uint64, extradata string
139152
return pageData, pageErr
140153
}
141154

142-
func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize uint64, extradata string, invertextradata bool, minGasUsed string, maxGasUsed string, minGasLimit string, maxGasLimit string, minBlockSize string, maxBlockSize string, withMevBlock uint8, minTxCount string, maxTxCount string, minBlobCount string, maxBlobCount string, minEpoch string, maxEpoch string, displayColumns uint64) *models.BlocksFilteredPageData {
155+
func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize uint64, extradata string, invertextradata bool, minGasUsed string, maxGasUsed string, minGasLimit string, maxGasLimit string, minBlockSize string, maxBlockSize string, withMevBlock uint8, withBuilderBlock uint8, builder string, invertBuilder bool, minTxCount string, maxTxCount string, minBlobCount string, maxBlobCount string, minEpoch string, maxEpoch string, displayColumns uint64) *models.BlocksFilteredPageData {
143156
chainState := services.GlobalBeaconService.GetChainState()
157+
gloasActive := chainState.IsEip7732Enabled(chainState.EpochOfSlot(chainState.CurrentSlot()))
144158
filterArgs := url.Values{}
145159

146160
if extradata != "" {
@@ -170,6 +184,15 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
170184
if withMevBlock != 0 {
171185
filterArgs.Add("f.mev", fmt.Sprintf("%v", withMevBlock))
172186
}
187+
if withBuilderBlock != 0 {
188+
filterArgs.Add("f.builderblock", fmt.Sprintf("%v", withBuilderBlock))
189+
}
190+
if builder != "" {
191+
filterArgs.Add("f.builder", builder)
192+
}
193+
if invertBuilder {
194+
filterArgs.Add("f.binvert", "on")
195+
}
173196
if minTxCount != "" {
174197
filterArgs.Add("f.mintx", minTxCount)
175198
}
@@ -199,19 +222,20 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
199222
}
200223
if len(displayMap) == 0 {
201224
displayMap = map[uint64]bool{
202-
1: true, // Epoch
203-
2: true, // Block Number
204-
3: true, // Slot
205-
4: true, // Status
206-
5: true, // Time
207-
6: true, // Tx Count
208-
7: true, // Blob Count
209-
8: true, // Gas Usage
210-
9: true, // Gas Limit
211-
10: true, // MEV Block
212-
11: true, // Block Size
213-
12: true, // Extra Data
214-
13: false, // Fee Recipient
225+
1: true, // Epoch
226+
2: true, // Block Number
227+
3: true, // Slot
228+
4: true, // Status
229+
5: true, // Time
230+
6: true, // Tx Count
231+
7: true, // Blob Count
232+
8: true, // Gas Usage
233+
9: true, // Gas Limit
234+
10: !gloasActive, // MEV Block (replaced by Builder once gloas is active)
235+
11: true, // Block Size
236+
12: true, // Extra Data
237+
13: false, // Fee Recipient
238+
14: gloasActive, // Builder (shown once gloas is active)
215239
}
216240
} else {
217241
displayMask := uint64(0)
@@ -234,6 +258,9 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
234258
FilterMinBlockSize: minBlockSize,
235259
FilterMaxBlockSize: maxBlockSize,
236260
FilterWithMevBlock: withMevBlock,
261+
FilterWithBuilder: withBuilderBlock,
262+
FilterBuilder: builder,
263+
FilterInvertBuilder: invertBuilder,
237264
FilterMinTxCount: minTxCount,
238265
FilterMaxTxCount: maxTxCount,
239266
FilterMinBlobCount: minBlobCount,
@@ -251,10 +278,13 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
251278
DisplayGasUsage: displayMap[8],
252279
DisplayGasLimit: displayMap[9],
253280
DisplayMevBlock: displayMap[10],
281+
DisplayBuilder: displayMap[14],
254282
DisplayBlockSize: displayMap[11],
255283
DisplayElExtraData: displayMap[12],
256284
DisplayFeeRecipient: displayMap[13],
257285
DisplayColCount: uint64(len(displayMap)),
286+
287+
IsGloasActive: gloasActive,
258288
}
259289
logrus.Debugf("blocks_filtered page called: %v:%v [%v]", pageIdx, pageSize, extradata)
260290
if pageIdx == 0 {
@@ -278,11 +308,20 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
278308

279309
pageData.Blocks = make([]*models.BlocksFilteredPageDataBlock, 0)
280310
blockFilter := &dbtypes.BlockFilter{
281-
ExtraData: extradata,
282-
InvertExtraData: invertextradata,
283-
WithOrphaned: 0,
284-
WithMissing: 0,
285-
WithMevBlock: withMevBlock,
311+
ExtraData: extradata,
312+
InvertExtraData: invertextradata,
313+
WithOrphaned: 0,
314+
WithMissing: 0,
315+
WithMevBlock: withMevBlock,
316+
WithBuilderBlock: withBuilderBlock,
317+
}
318+
319+
if builder != "" {
320+
builderIdx, err := strconv.ParseInt(builder, 10, 64)
321+
if err == nil {
322+
blockFilter.BuilderIndex = &builderIdx
323+
blockFilter.InvertBuilder = invertBuilder
324+
}
286325
}
287326

288327
if minGasUsed != "" {
@@ -438,6 +477,19 @@ func buildFilteredBlocksPageData(ctx context.Context, pageIdx uint64, pageSize u
438477
blockData.Status = 1
439478
}
440479

480+
// Add builder info (build source for the Builder column)
481+
if pageData.DisplayBuilder && dbBlock.Block.Status > 0 {
482+
if dbBlock.Block.BuilderIndex == -1 {
483+
blockData.HasBuilder = true
484+
blockData.BuilderIndex = math.MaxUint64 // self-built sentinel
485+
} else if dbBlock.Block.BuilderIndex >= 0 {
486+
blockData.HasBuilder = true
487+
blockData.BuilderIndex = uint64(dbBlock.Block.BuilderIndex)
488+
blockData.BuilderName = services.GlobalBeaconService.GetValidatorName(uint64(dbBlock.Block.BuilderIndex) | services.BuilderIndexFlag)
489+
blockData.BuilderURL = services.GlobalBeaconService.GetBuilderURL(uint64(dbBlock.Block.BuilderIndex))
490+
}
491+
}
492+
441493
pageData.Blocks = append(pageData.Blocks, blockData)
442494
}
443495
pageData.BlockCount = uint64(len(pageData.Blocks))

handlers/slots.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func buildSlotsPageData(ctx context.Context, firstSlot uint64, pageSize uint64,
119119
17: false,
120120
18: !hasSnooperClients, // Disable receive delay if snooper clients exist
121121
19: hasSnooperClients, // Enable exec time if snooper clients exist
122-
20: false, // Builder (hidden by default)
122+
20: false, // Builder (opt-in; proposer column already shows build source)
123123
}
124124
}
125125

0 commit comments

Comments
 (0)