Skip to content

Commit fb68716

Browse files
authored
Merge pull request #715 from ethpandaops/bbusa/included-deposits-cred-type-filter
feat(included_deposits): filter by withdrawal credential type
2 parents a5fc5fa + b103415 commit fb68716

20 files changed

Lines changed: 330 additions & 69 deletions

db/deposits.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ func InsertDeposits(ctx context.Context, tx *sqlx.Tx, deposits []*dbtypes.Deposi
1717
dbtypes.DBEnginePgsql: "INSERT INTO deposits ",
1818
dbtypes.DBEngineSqlite: "INSERT OR REPLACE INTO deposits ",
1919
}),
20-
"(deposit_index, slot_number, slot_index, slot_root, orphaned, publickey, withdrawalcredentials, amount, fork_id)",
20+
"(deposit_index, slot_number, slot_index, slot_root, orphaned, publickey, withdrawalcredentials, amount, fork_id, cred_type)",
2121
" VALUES ",
2222
)
2323
argIdx := 0
24-
fieldCount := 9
24+
fieldCount := 10
2525

2626
args := make([]any, len(deposits)*fieldCount)
2727
for i, deposit := range deposits {
@@ -47,6 +47,7 @@ func InsertDeposits(ctx context.Context, tx *sqlx.Tx, deposits []*dbtypes.Deposi
4747
args[argIdx+6] = deposit.WithdrawalCredentials[:]
4848
args[argIdx+7] = deposit.Amount
4949
args[argIdx+8] = deposit.ForkId
50+
args[argIdx+9] = deposit.CredType
5051
argIdx += fieldCount
5152
}
5253
fmt.Fprint(&sql, EngineQuery(map[dbtypes.DBEngineType]string{
@@ -139,6 +140,19 @@ func GetDepositsFiltered(ctx context.Context, offset uint64, limit uint32, canon
139140
filterOp = "AND"
140141
}
141142

143+
if len(txFilter.WithdrawalCredTypes) > 0 {
144+
fmt.Fprintf(&sql, " %v deposits.cred_type IN (", filterOp)
145+
for i, credType := range txFilter.WithdrawalCredTypes {
146+
if i > 0 {
147+
fmt.Fprintf(&sql, ", ")
148+
}
149+
args = append(args, uint16(credType))
150+
fmt.Fprintf(&sql, "$%v", len(args))
151+
}
152+
fmt.Fprintf(&sql, ")")
153+
filterOp = "AND"
154+
}
155+
142156
if len(txFilter.Address) > 0 {
143157
args = append(args, txFilter.Address)
144158
fmt.Fprintf(&sql, " %v deposit_txs.tx_sender = $%v", filterOp, len(args))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
-- +goose Up
2+
-- +goose StatementBegin
3+
4+
ALTER TABLE public."deposits"
5+
ADD "cred_type" SMALLINT NOT NULL DEFAULT 0;
6+
7+
UPDATE public."deposits"
8+
SET "cred_type" = get_byte("withdrawalcredentials", 0)
9+
WHERE length("withdrawalcredentials") > 0;
10+
11+
CREATE INDEX IF NOT EXISTS "deposits_cred_type_slot_number_idx"
12+
ON public."deposits"
13+
("cred_type" ASC, "slot_number" ASC);
14+
15+
ALTER TABLE public."validators"
16+
ADD "cred_type" SMALLINT NOT NULL DEFAULT 0;
17+
18+
UPDATE public."validators"
19+
SET "cred_type" = get_byte("withdrawal_credentials", 0)
20+
WHERE length("withdrawal_credentials") > 0;
21+
22+
CREATE INDEX IF NOT EXISTS "validators_cred_type_validator_index_idx"
23+
ON public."validators"
24+
("cred_type" ASC, "validator_index" ASC);
25+
26+
-- +goose StatementEnd
27+
-- +goose Down
28+
-- +goose StatementBegin
29+
SELECT 'NOT SUPPORTED';
30+
-- +goose StatementEnd
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- +goose Up
2+
-- +goose StatementBegin
3+
4+
ALTER TABLE "deposits"
5+
ADD "cred_type" INTEGER NOT NULL DEFAULT 0;
6+
7+
UPDATE "deposits"
8+
SET "cred_type" = (instr('123456789ABCDEF', substr(hex("withdrawalcredentials"), 1, 1)) * 16)
9+
+ instr('123456789ABCDEF', substr(hex("withdrawalcredentials"), 2, 1))
10+
WHERE length("withdrawalcredentials") > 0;
11+
12+
CREATE INDEX IF NOT EXISTS "deposits_cred_type_slot_number_idx"
13+
ON "deposits"
14+
("cred_type" ASC, "slot_number" ASC);
15+
16+
ALTER TABLE "validators"
17+
ADD "cred_type" INTEGER NOT NULL DEFAULT 0;
18+
19+
UPDATE "validators"
20+
SET "cred_type" = (instr('123456789ABCDEF', substr(hex("withdrawal_credentials"), 1, 1)) * 16)
21+
+ instr('123456789ABCDEF', substr(hex("withdrawal_credentials"), 2, 1))
22+
WHERE length("withdrawal_credentials") > 0;
23+
24+
CREATE INDEX IF NOT EXISTS "validators_cred_type_validator_index_idx"
25+
ON "validators"
26+
("cred_type" ASC, "validator_index" ASC);
27+
28+
-- +goose StatementEnd
29+
-- +goose Down
30+
-- +goose StatementBegin
31+
SELECT 'NOT SUPPORTED';
32+
-- +goose StatementEnd

db/validators.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,23 @@ func InsertValidator(ctx context.Context, tx *sqlx.Tx, validator *dbtypes.Valida
1818
INSERT INTO validators (
1919
validator_index, pubkey, withdrawal_credentials, effective_balance,
2020
slashed, activation_eligibility_epoch, activation_epoch,
21-
exit_epoch, withdrawable_epoch
22-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
21+
exit_epoch, withdrawable_epoch, cred_type
22+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
2323
ON CONFLICT (validator_index) DO UPDATE SET
2424
withdrawal_credentials = excluded.withdrawal_credentials,
2525
effective_balance = excluded.effective_balance,
2626
slashed = excluded.slashed,
2727
activation_eligibility_epoch = excluded.activation_eligibility_epoch,
2828
activation_epoch = excluded.activation_epoch,
2929
exit_epoch = excluded.exit_epoch,
30-
withdrawable_epoch = excluded.withdrawable_epoch`,
30+
withdrawable_epoch = excluded.withdrawable_epoch,
31+
cred_type = excluded.cred_type`,
3132
dbtypes.DBEngineSqlite: `
3233
INSERT OR REPLACE INTO validators (
3334
validator_index, pubkey, withdrawal_credentials, effective_balance,
3435
slashed, activation_eligibility_epoch, activation_epoch,
35-
exit_epoch, withdrawable_epoch
36-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
36+
exit_epoch, withdrawable_epoch, cred_type
37+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
3738
}),
3839
validator.ValidatorIndex,
3940
validator.Pubkey,
@@ -43,7 +44,8 @@ func InsertValidator(ctx context.Context, tx *sqlx.Tx, validator *dbtypes.Valida
4344
validator.ActivationEligibilityEpoch,
4445
validator.ActivationEpoch,
4546
validator.ExitEpoch,
46-
validator.WithdrawableEpoch)
47+
validator.WithdrawableEpoch,
48+
validator.CredType)
4749

4850
if err != nil {
4951
return fmt.Errorf("error inserting validator: %v", err)
@@ -57,14 +59,14 @@ func InsertValidatorBatch(ctx context.Context, tx *sqlx.Tx, validators []*dbtype
5759
return nil
5860
}
5961

60-
valueArgs := make([]interface{}, 0, len(validators)*9)
62+
valueArgs := make([]interface{}, 0, len(validators)*10)
6163
var values strings.Builder
6264
for i, validator := range validators {
6365
if i > 0 {
6466
values.WriteByte(',')
6567
}
6668
values.WriteByte('(')
67-
appendDollarPlaceholders(&values, i*9+1, 9, ", ")
69+
appendDollarPlaceholders(&values, i*10+1, 10, ", ")
6870
values.WriteByte(')')
6971
valueArgs = append(valueArgs,
7072
validator.ValidatorIndex,
@@ -75,15 +77,16 @@ func InsertValidatorBatch(ctx context.Context, tx *sqlx.Tx, validators []*dbtype
7577
validator.ActivationEligibilityEpoch,
7678
validator.ActivationEpoch,
7779
validator.ExitEpoch,
78-
validator.WithdrawableEpoch)
80+
validator.WithdrawableEpoch,
81+
validator.CredType)
7982
}
8083

8184
stmt := fmt.Sprintf(EngineQuery(map[dbtypes.DBEngineType]string{
8285
dbtypes.DBEnginePgsql: `
8386
INSERT INTO validators (
8487
validator_index, pubkey, withdrawal_credentials, effective_balance,
8588
slashed, activation_eligibility_epoch, activation_epoch,
86-
exit_epoch, withdrawable_epoch
89+
exit_epoch, withdrawable_epoch, cred_type
8790
) VALUES %s
8891
ON CONFLICT (validator_index) DO UPDATE SET
8992
withdrawal_credentials = excluded.withdrawal_credentials,
@@ -92,12 +95,13 @@ func InsertValidatorBatch(ctx context.Context, tx *sqlx.Tx, validators []*dbtype
9295
activation_eligibility_epoch = excluded.activation_eligibility_epoch,
9396
activation_epoch = excluded.activation_epoch,
9497
exit_epoch = excluded.exit_epoch,
95-
withdrawable_epoch = excluded.withdrawable_epoch`,
98+
withdrawable_epoch = excluded.withdrawable_epoch,
99+
cred_type = excluded.cred_type`,
96100
dbtypes.DBEngineSqlite: `
97101
INSERT OR REPLACE INTO validators (
98102
validator_index, pubkey, withdrawal_credentials, effective_balance,
99103
slashed, activation_eligibility_epoch, activation_epoch,
100-
exit_epoch, withdrawable_epoch
104+
exit_epoch, withdrawable_epoch, cred_type
101105
) VALUES %s`,
102106
}), values.String())
103107

@@ -264,6 +268,18 @@ func buildValidatorFilterSql(filter dbtypes.ValidatorFilter, currentEpoch uint64
264268
fmt.Fprintf(sql, " %v withdrawal_credentials = $%v", filterOp, len(args))
265269
filterOp = "AND"
266270
}
271+
if len(filter.WithdrawalCredTypes) > 0 {
272+
fmt.Fprintf(sql, " %v cred_type IN (", filterOp)
273+
for i, credType := range filter.WithdrawalCredTypes {
274+
if i > 0 {
275+
fmt.Fprintf(sql, ", ")
276+
}
277+
args = append(args, uint16(credType))
278+
fmt.Fprintf(sql, "$%v", len(args))
279+
}
280+
fmt.Fprintf(sql, ")")
281+
filterOp = "AND"
282+
}
267283
if filter.ValidatorName != "" {
268284
args = append(args, "%"+filter.ValidatorName+"%")
269285
fmt.Fprintf(sql, EngineQuery(map[dbtypes.DBEngineType]string{

dbtypes/dbtypes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ type Deposit struct {
290290
WithdrawalCredentials []byte `db:"withdrawalcredentials"`
291291
Amount uint64 `db:"amount"`
292292
ForkId uint64 `db:"fork_id"`
293+
CredType uint8 `db:"cred_type"`
293294
}
294295

295296
type DepositWithTx struct {
@@ -446,6 +447,7 @@ type Validator struct {
446447
ActivationEpoch int64 `db:"activation_epoch"`
447448
ExitEpoch int64 `db:"exit_epoch"`
448449
WithdrawableEpoch int64 `db:"withdrawable_epoch"`
450+
CredType uint8 `db:"cred_type"`
449451
}
450452

451453
// EL Explorer types

dbtypes/other.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,18 +105,19 @@ type MevBlockFilter struct {
105105
}
106106

107107
type DepositTxFilter struct {
108-
MinIndex uint64
109-
MaxIndex uint64
110-
Address []byte
111-
TargetAddress []byte
112-
PublicKey []byte
113-
PublicKeys [][]byte
114-
WithdrawalAddress []byte
115-
ValidatorName string
116-
MinAmount uint64
117-
MaxAmount uint64
118-
WithOrphaned uint8
119-
WithValid uint8
108+
MinIndex uint64
109+
MaxIndex uint64
110+
Address []byte
111+
TargetAddress []byte
112+
PublicKey []byte
113+
PublicKeys [][]byte
114+
WithdrawalAddress []byte
115+
WithdrawalCredTypes []uint8
116+
ValidatorName string
117+
MinAmount uint64
118+
MaxAmount uint64
119+
WithOrphaned uint8
120+
WithValid uint8
120121
}
121122

122123
type DepositFilter struct {
@@ -221,14 +222,15 @@ const (
221222
)
222223

223224
type ValidatorFilter struct {
224-
MinIndex *uint64
225-
MaxIndex *uint64
226-
Indices []phase0.ValidatorIndex
227-
PubKey []byte
228-
WithdrawalAddress []byte
229-
WithdrawalCreds []byte
230-
ValidatorName string
231-
Status []v1.ValidatorState
225+
MinIndex *uint64
226+
MaxIndex *uint64
227+
Indices []phase0.ValidatorIndex
228+
PubKey []byte
229+
WithdrawalAddress []byte
230+
WithdrawalCreds []byte
231+
WithdrawalCredTypes []uint8
232+
ValidatorName string
233+
Status []v1.ValidatorState
232234

233235
OrderBy ValidatorOrder
234236
Limit uint64

docs/docs.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,16 @@ const docTemplate = `{
425425
"description": "Filter by signature validity (0=invalid only, 1=valid only, 2=all)",
426426
"name": "with_valid",
427427
"in": "query"
428+
},
429+
{
430+
"type": "array",
431+
"items": {
432+
"type": "integer"
433+
},
434+
"collectionFormat": "multi",
435+
"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.",
436+
"name": "cred_type",
437+
"in": "query"
428438
}
429439
],
430440
"responses": {

docs/swagger.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,16 @@
422422
"description": "Filter by signature validity (0=invalid only, 1=valid only, 2=all)",
423423
"name": "with_valid",
424424
"in": "query"
425+
},
426+
{
427+
"type": "array",
428+
"items": {
429+
"type": "integer"
430+
},
431+
"collectionFormat": "multi",
432+
"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.",
433+
"name": "cred_type",
434+
"in": "query"
425435
}
426436
],
427437
"responses": {

docs/swagger.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,14 @@ paths:
21812181
in: query
21822182
name: with_valid
21832183
type: integer
2184+
- collectionFormat: multi
2185+
description: Filter by withdrawal credential type prefix byte (0-3). Repeat
2186+
the parameter to include multiple types, e.g. cred_type=1&cred_type=2.
2187+
in: query
2188+
items:
2189+
type: integer
2190+
name: cred_type
2191+
type: array
21842192
produces:
21852193
- application/json
21862194
responses:

handlers/api/deposits_included_v1.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type APIDepositIncludedInfo struct {
6464
// @Param with_orphaned query int false "Include orphaned deposits (0=canonical only, 1=include all, 2=orphaned only)"
6565
// @Param address query string false "Filter by depositor address"
6666
// @Param with_valid query int false "Filter by signature validity (0=invalid only, 1=valid only, 2=all)"
67+
// @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)
6768
// @Success 200 {object} APIDepositsIncludedResponse
6869
// @Failure 400 {object} map[string]string "Invalid parameters"
6970
// @Failure 500 {object} map[string]string "Internal server error"
@@ -170,6 +171,19 @@ func APIDepositsIncludedV1(w http.ResponseWriter, r *http.Request) {
170171
}
171172
}
172173

174+
// Withdrawal credential type filter (repeatable: cred_type=0&cred_type=1)
175+
if credVals, ok := query["cred_type"]; ok {
176+
seen := map[uint8]bool{}
177+
for _, v := range credVals {
178+
t, err := strconv.ParseUint(v, 10, 8)
179+
if err != nil || t > 3 || seen[uint8(t)] {
180+
continue
181+
}
182+
seen[uint8(t)] = true
183+
depositFilter.WithdrawalCredTypes = append(depositFilter.WithdrawalCredTypes, uint8(t))
184+
}
185+
}
186+
173187
// Get deposits included in blocks using the proper service method
174188
combinedFilter := &services.CombinedDepositRequestFilter{
175189
Filter: depositFilter,

0 commit comments

Comments
 (0)