Skip to content
Open
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
13 changes: 13 additions & 0 deletions db/deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ func GetDepositsFiltered(ctx context.Context, offset uint64, limit uint32, canon
filterOp = "AND"
}

if len(txFilter.WithdrawalCredTypes) > 0 {
fmt.Fprintf(&sql, " %v (", filterOp)
for i, credType := range txFilter.WithdrawalCredTypes {
if i > 0 {
fmt.Fprintf(&sql, " OR ")
}
args = append(args, []byte{credType})
fmt.Fprintf(&sql, " SUBSTRING(deposits.withdrawalcredentials, 1, 1) = $%v", len(args))
}
fmt.Fprintf(&sql, " )")
filterOp = "AND"
}

if len(txFilter.Address) > 0 {
args = append(args, txFilter.Address)
fmt.Fprintf(&sql, " %v deposit_txs.tx_sender = $%v", filterOp, len(args))
Expand Down
25 changes: 13 additions & 12 deletions dbtypes/other.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,19 @@ type MevBlockFilter struct {
}

type DepositTxFilter struct {
MinIndex uint64
MaxIndex uint64
Address []byte
TargetAddress []byte
PublicKey []byte
PublicKeys [][]byte
WithdrawalAddress []byte
ValidatorName string
MinAmount uint64
MaxAmount uint64
WithOrphaned uint8
WithValid uint8
MinIndex uint64
MaxIndex uint64
Address []byte
TargetAddress []byte
PublicKey []byte
PublicKeys [][]byte
WithdrawalAddress []byte
WithdrawalCredTypes []uint8
ValidatorName string
MinAmount uint64
MaxAmount uint64
WithOrphaned uint8
WithValid uint8
}

type DepositFilter struct {
Expand Down
10 changes: 10 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,16 @@ const docTemplate = `{
"description": "Filter by signature validity (0=invalid only, 1=valid only, 2=all)",
"name": "with_valid",
"in": "query"
},
{
"type": "array",
"items": {
"type": "integer"
},
"collectionFormat": "multi",
"description": "Filter by withdrawal credential type prefix byte (0-3). Repeat the parameter to include multiple types, e.g. cred_type=1\u0026cred_type=2.",
"name": "cred_type",
"in": "query"
}
],
"responses": {
Expand Down
10 changes: 10 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,16 @@
"description": "Filter by signature validity (0=invalid only, 1=valid only, 2=all)",
"name": "with_valid",
"in": "query"
},
{
"type": "array",
"items": {
"type": "integer"
},
"collectionFormat": "multi",
"description": "Filter by withdrawal credential type prefix byte (0-3). Repeat the parameter to include multiple types, e.g. cred_type=1\u0026cred_type=2.",
"name": "cred_type",
"in": "query"
}
],
"responses": {
Expand Down
8 changes: 8 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2116,6 +2116,14 @@ paths:
in: query
name: with_valid
type: integer
- collectionFormat: multi
description: Filter by withdrawal credential type prefix byte (0-3). Repeat
the parameter to include multiple types, e.g. cred_type=1&cred_type=2.
in: query
items:
type: integer
name: cred_type
type: array
produces:
- application/json
responses:
Expand Down
14 changes: 14 additions & 0 deletions handlers/api/deposits_included_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type APIDepositIncludedInfo struct {
// @Param with_orphaned query int false "Include orphaned deposits (0=canonical only, 1=include all, 2=orphaned only)"
// @Param address query string false "Filter by depositor address"
// @Param with_valid query int false "Filter by signature validity (0=invalid only, 1=valid only, 2=all)"
// @Param cred_type query []int false "Filter by withdrawal credential type prefix byte (0-3). Repeat the parameter to include multiple types, e.g. cred_type=1&cred_type=2." collectionFormat(multi)
// @Success 200 {object} APIDepositsIncludedResponse
// @Failure 400 {object} map[string]string "Invalid parameters"
// @Failure 500 {object} map[string]string "Internal server error"
Expand Down Expand Up @@ -170,6 +171,19 @@ func APIDepositsIncludedV1(w http.ResponseWriter, r *http.Request) {
}
}

// Withdrawal credential type filter (repeatable: cred_type=0&cred_type=1)
if credVals, ok := query["cred_type"]; ok {
seen := map[uint8]bool{}
for _, v := range credVals {
t, err := strconv.ParseUint(v, 10, 8)
if err != nil || t > 3 || seen[uint8(t)] {
continue
}
seen[uint8(t)] = true
depositFilter.WithdrawalCredTypes = append(depositFilter.WithdrawalCredTypes, uint8(t))
}
}

// Get deposits included in blocks using the proper service method
combinedFilter := &services.CombinedDepositRequestFilter{
Filter: depositFilter,
Expand Down
49 changes: 34 additions & 15 deletions handlers/included_deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func IncludedDeposits(w http.ResponseWriter, r *http.Request) {
var withOrphaned uint64
var address string
var withValid uint64
var credTypes []uint8

if urlArgs.Has("f") {
if urlArgs.Has("f.mini") {
Expand Down Expand Up @@ -81,14 +82,25 @@ func IncludedDeposits(w http.ResponseWriter, r *http.Request) {
if urlArgs.Has("f.valid") {
withValid, _ = strconv.ParseUint(urlArgs.Get("f.valid"), 10, 64)
}
if vals, ok := urlArgs["f.cred"]; ok {
seen := map[uint8]bool{}
for _, v := range vals {
t, err := strconv.ParseUint(v, 10, 8)
if err != nil || t > 3 || seen[uint8(t)] {
continue
}
seen[uint8(t)] = true
credTypes = append(credTypes, uint8(t))
}
}
} else {
withOrphaned = 1
withValid = 1
}
var pageError error
pageError = services.GlobalCallRateLimiter.CheckCallLimit(r, 2)
if pageError == nil {
data.Data, pageError = getFilteredIncludedDepositsPageData(pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, uint8(withOrphaned), address, uint8(withValid))
data.Data, pageError = getFilteredIncludedDepositsPageData(pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, uint8(withOrphaned), address, uint8(withValid), credTypes)
}
if pageError != nil {
handlePageError(w, r, pageError)
Expand All @@ -100,11 +112,11 @@ func IncludedDeposits(w http.ResponseWriter, r *http.Request) {
}
}

func getFilteredIncludedDepositsPageData(pageIdx uint64, pageSize uint64, minIndex uint64, maxIndex uint64, publickey string, vname string, minAmount uint64, maxAmount uint64, withOrphaned uint8, address string, withValid uint8) (*models.IncludedDepositsPageData, error) {
func getFilteredIncludedDepositsPageData(pageIdx uint64, pageSize uint64, minIndex uint64, maxIndex uint64, publickey string, vname string, minAmount uint64, maxAmount uint64, withOrphaned uint8, address string, withValid uint8, credTypes []uint8) (*models.IncludedDepositsPageData, error) {
pageData := &models.IncludedDepositsPageData{}
pageCacheKey := fmt.Sprintf("included_deposits:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v", pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, withOrphaned, address, withValid)
pageCacheKey := fmt.Sprintf("included_deposits:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v", pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, withOrphaned, address, withValid, credTypes)
pageRes, pageErr := services.GlobalFrontendCache.ProcessCachedPage(pageCacheKey, true, pageData, func(pageCall *services.FrontendCacheProcessingPage) interface{} {
pageData, cacheTimeout := buildFilteredIncludedDepositsPageData(pageCall.CallCtx, pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, withOrphaned, address, withValid)
pageData, cacheTimeout := buildFilteredIncludedDepositsPageData(pageCall.CallCtx, pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, withOrphaned, address, withValid, credTypes)
pageCall.CacheTimeout = cacheTimeout
return pageData
})
Expand All @@ -118,7 +130,7 @@ func getFilteredIncludedDepositsPageData(pageIdx uint64, pageSize uint64, minInd
return pageData, pageErr
}

func buildFilteredIncludedDepositsPageData(ctx context.Context, pageIdx uint64, pageSize uint64, minIndex uint64, maxIndex uint64, publickey string, vname string, minAmount uint64, maxAmount uint64, withOrphaned uint8, address string, withValid uint8) (*models.IncludedDepositsPageData, time.Duration) {
func buildFilteredIncludedDepositsPageData(ctx context.Context, pageIdx uint64, pageSize uint64, minIndex uint64, maxIndex uint64, publickey string, vname string, minAmount uint64, maxAmount uint64, withOrphaned uint8, address string, withValid uint8, credTypes []uint8) (*models.IncludedDepositsPageData, time.Duration) {
filterArgs := url.Values{}
if minIndex != 0 {
filterArgs.Add("f.mini", fmt.Sprintf("%v", minIndex))
Expand Down Expand Up @@ -147,6 +159,11 @@ func buildFilteredIncludedDepositsPageData(ctx context.Context, pageIdx uint64,
if withValid != 0 {
filterArgs.Add("f.valid", fmt.Sprintf("%v", withValid))
}
credTypeSet := make(map[uint8]bool, len(credTypes))
for _, t := range credTypes {
credTypeSet[t] = true
filterArgs.Add("f.cred", fmt.Sprintf("%v", t))
}

pageData := &models.IncludedDepositsPageData{
FilterMinIndex: minIndex,
Expand All @@ -158,9 +175,10 @@ func buildFilteredIncludedDepositsPageData(ctx context.Context, pageIdx uint64,
FilterWithOrphaned: withOrphaned,
FilterAddress: address,
FilterWithValid: withValid,
FilterCredTypes: credTypeSet,
}
cacheTimeout := 5 * time.Minute
logrus.Debugf("included_deposits page called: %v:%v [%v,%v,%v,%v,%v,%v]", pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount)
logrus.Debugf("included_deposits page called: %v:%v [%v,%v,%v,%v,%v,%v,%v]", pageIdx, pageSize, minIndex, maxIndex, publickey, vname, minAmount, maxAmount, credTypes)
if pageIdx == 1 {
pageData.IsDefaultPage = true
} else {
Expand All @@ -183,15 +201,16 @@ func buildFilteredIncludedDepositsPageData(ctx context.Context, pageIdx uint64,
// Update to use new filter structure
depositFilter := &services.CombinedDepositRequestFilter{
Filter: &dbtypes.DepositTxFilter{
MinIndex: minIndex,
MaxIndex: maxIndex,
PublicKey: common.FromHex(publickey),
ValidatorName: vname,
MinAmount: minAmount,
MaxAmount: maxAmount,
WithOrphaned: withOrphaned,
Address: common.FromHex(address),
WithValid: withValid,
MinIndex: minIndex,
MaxIndex: maxIndex,
PublicKey: common.FromHex(publickey),
ValidatorName: vname,
MinAmount: minAmount,
MaxAmount: maxAmount,
WithOrphaned: withOrphaned,
Address: common.FromHex(address),
WithValid: withValid,
WithdrawalCredTypes: credTypes,
},
}

Expand Down
14 changes: 11 additions & 3 deletions services/chainservice_deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ func (bs *ChainService) GetDepositRequestsByFilter(ctx context.Context, filter *
}

txFilter := &dbtypes.DepositTxFilter{
Address: filter.Filter.Address,
TargetAddress: filter.Filter.TargetAddress,
WithValid: filter.Filter.WithValid,
Address: filter.Filter.Address,
TargetAddress: filter.Filter.TargetAddress,
WithValid: filter.Filter.WithValid,
WithdrawalCredTypes: filter.Filter.WithdrawalCredTypes,
}

dbOperations, totalReqResults := bs.GetDepositOperationsByFilter(ctx, operationFilter, txFilter, pageOffset, pageSize)
Expand Down Expand Up @@ -316,6 +317,13 @@ func (bs *ChainService) GetDepositOperationsByFilter(ctx context.Context, filter
}
}

if len(txFilter.WithdrawalCredTypes) > 0 {
wdcreds := depositWithTx.WithdrawalCredentials
if len(wdcreds) == 0 || !slices.Contains(txFilter.WithdrawalCredTypes, wdcreds[0]) {
continue
}
}

filteredMatches = append(filteredMatches, depositWithTx)
}

Expand Down
23 changes: 23 additions & 0 deletions templates/included_deposits/included_deposits.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,29 @@ <h1 class="h4 mb-1 mb-md-0">
</select>
</div>
</div>
<div class="row mt-1">
<div class="col-sm-12 col-md-6 col-lg-4">
<nobr>Credential Type</nobr>
</div>
<div class="col-sm-12 col-md-6 col-lg-8 d-flex flex-wrap gap-3 cred-type-filter">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="f.cred" id="credType0" value="0" {{ if index .FilterCredTypes 0 }}checked{{ end }}>
<label class="form-check-label text-monospace text-warning" for="credType0">0x00</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="f.cred" id="credType1" value="1" {{ if index .FilterCredTypes 1 }}checked{{ end }}>
<label class="form-check-label text-monospace text-success" for="credType1">0x01</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="f.cred" id="credType2" value="2" {{ if index .FilterCredTypes 2 }}checked{{ end }}>
<label class="form-check-label text-monospace text-info" for="credType2">0x02</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="f.cred" id="credType3" value="3" {{ if index .FilterCredTypes 3 }}checked{{ end }}>
<label class="form-check-label text-monospace text-primary" for="credType3">0x03</label>
</div>
</div>
</div>
</div>
</div>

Expand Down
19 changes: 10 additions & 9 deletions types/models/included_deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import (

// IncludedDepositsPageData is a struct to hold info for the included_deposits page
type IncludedDepositsPageData struct {
FilterMinIndex uint64 `json:"filter_mini"`
FilterMaxIndex uint64 `json:"filter_maxi"`
FilterPubKey string `json:"filter_publickey"`
FilterValidatorName string `json:"filter_vname"`
FilterMinAmount uint64 `json:"filter_mina"`
FilterMaxAmount uint64 `json:"filter_maxa"`
FilterWithOrphaned uint8 `json:"filter_orphaned"`
FilterWithValid uint8 `json:"filter_valid"`
FilterAddress string `json:"filter_address"`
FilterMinIndex uint64 `json:"filter_mini"`
FilterMaxIndex uint64 `json:"filter_maxi"`
FilterPubKey string `json:"filter_publickey"`
FilterValidatorName string `json:"filter_vname"`
FilterMinAmount uint64 `json:"filter_mina"`
FilterMaxAmount uint64 `json:"filter_maxa"`
FilterWithOrphaned uint8 `json:"filter_orphaned"`
FilterWithValid uint8 `json:"filter_valid"`
FilterAddress string `json:"filter_address"`
FilterCredTypes map[uint8]bool `json:"filter_cred_types"`

Deposits []*IncludedDepositsPageDataDeposit `json:"deposits"`
DepositCount uint64 `json:"deposit_count"`
Expand Down