Skip to content

Commit 672d573

Browse files
committed
feat(vdb): added validator search by hex graffiti
1 parent 4bb9d8f commit 672d573

File tree

7 files changed

+70
-25
lines changed

7 files changed

+70
-25
lines changed

backend/pkg/api/api_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ func TestInternalSearchHandler(t *testing.T) {
293293
"validators_by_withdrawal_credential",
294294
"validator_by_index",
295295
"validator_by_public_key",
296-
"validators_by_graffiti"
296+
"validators_by_graffiti",
297+
"validators_by_graffiti_hex"
297298
]
298299
}`)).Expect().Status(http.StatusOK).JSON().Decode(&resp)
299300

@@ -320,7 +321,8 @@ func TestInternalSearchHandler(t *testing.T) {
320321
"validators_by_withdrawal_credential",
321322
"validator_by_index",
322323
"validator_by_public_key",
323-
"validators_by_graffiti"
324+
"validators_by_graffiti",
325+
"validators_by_graffiti_hex"
324326
]
325327
}`)).Expect().Status(http.StatusOK).JSON().Decode(&resp)
326328

@@ -346,12 +348,13 @@ func TestInternalSearchHandler(t *testing.T) {
346348
"validators_by_withdrawal_credential",
347349
"validator_by_index",
348350
"validator_by_public_key",
349-
"validators_by_graffiti"
351+
"validators_by_graffiti",
352+
"validators_by_graffiti_hex"
350353
]
351354
}`)).Expect().Status(http.StatusOK).JSON().Decode(&resp)
352355

353356
assert.NotEqual(t, 0, len(resp.Data), "response data should not be empty")
354-
validatorsByWithdrawalAddress, ok := resp.Data[0].Value.(api_types.SearchValidatorsByWithdrwalCredential)
357+
validatorsByWithdrawalAddress, ok := resp.Data[0].Value.(api_types.SearchValidatorsByWithdrawalCredential)
355358
assert.True(t, ok, "response data should be of type SearchValidator")
356359
assert.Greater(t, validatorsByWithdrawalAddress.Count, uint64(0), "returned number of validators should be greater than 0")
357360
}

backend/pkg/api/data_access/dummy.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,15 +518,19 @@ func (d *DummyService) GetSearchValidatorsByDepositEnsName(ctx context.Context,
518518
return getDummyStruct[t.SearchValidatorsByDepositAddress](ctx)
519519
}
520520

521-
func (d *DummyService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error) {
522-
return getDummyStruct[t.SearchValidatorsByWithdrwalCredential](ctx)
521+
func (d *DummyService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrawalCredential, error) {
522+
return getDummyStruct[t.SearchValidatorsByWithdrawalCredential](ctx)
523523
}
524524

525-
func (d *DummyService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error) {
526-
return getDummyStruct[t.SearchValidatorsByWithdrwalCredential](ctx)
525+
func (d *DummyService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrawalCredential, error) {
526+
return getDummyStruct[t.SearchValidatorsByWithdrawalCredential](ctx)
527527
}
528528

529-
func (d *DummyService) GetSearchValidatorsByGraffiti(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) {
529+
func (d *DummyService) GetSearchValidatorsByGraffitiText(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) {
530+
return getDummyStruct[t.SearchValidatorsByGraffiti](ctx)
531+
}
532+
533+
func (d *DummyService) GetSearchValidatorsByGraffitiHex(ctx context.Context, chainId uint64, graffiti []byte) (*t.SearchValidatorsByGraffiti, error) {
530534
return getDummyStruct[t.SearchValidatorsByGraffiti](ctx)
531535
}
532536

backend/pkg/api/data_access/search.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dataaccess
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/ethereum/go-ethereum/common/hexutil"
78
t "github.com/gobitfly/beaconchain/pkg/api/types"
@@ -13,9 +14,10 @@ type SearchRepository interface {
1314
GetSearchValidatorByPublicKey(ctx context.Context, chainId uint64, publicKey []byte) (*t.SearchValidator, error)
1415
GetSearchValidatorsByDepositAddress(ctx context.Context, chainId uint64, address []byte) (*t.SearchValidatorsByDepositAddress, error)
1516
GetSearchValidatorsByDepositEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByDepositAddress, error)
16-
GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error)
17-
GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error)
18-
GetSearchValidatorsByGraffiti(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error)
17+
GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrawalCredential, error)
18+
GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrawalCredential, error)
19+
GetSearchValidatorsByGraffitiText(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error)
20+
GetSearchValidatorsByGraffitiHex(ctx context.Context, chainId uint64, graffiti []byte) (*t.SearchValidatorsByGraffiti, error)
1921
}
2022

2123
func (d *DataAccessService) GetSearchValidatorByIndex(ctx context.Context, chainId, index uint64) (*t.SearchValidator, error) {
@@ -75,9 +77,9 @@ func (d *DataAccessService) GetSearchValidatorsByDepositEnsName(ctx context.Cont
7577
return nil, ErrNotFound
7678
}
7779

78-
func (d *DataAccessService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrwalCredential, error) {
80+
func (d *DataAccessService) GetSearchValidatorsByWithdrawalCredential(ctx context.Context, chainId uint64, credential []byte) (*t.SearchValidatorsByWithdrawalCredential, error) {
7981
// TODO: implement handling of chainid
80-
ret := &t.SearchValidatorsByWithdrwalCredential{
82+
ret := &t.SearchValidatorsByWithdrawalCredential{
8183
WithdrawalCredential: hexutil.Encode(credential),
8284
}
8385
err := db.ReaderDb.GetContext(ctx, &ret.Count, "select count(validatorindex) from validators where withdrawalcredentials = $1;", credential)
@@ -90,16 +92,17 @@ func (d *DataAccessService) GetSearchValidatorsByWithdrawalCredential(ctx contex
9092
return ret, nil
9193
}
9294

93-
func (d *DataAccessService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrwalCredential, error) {
95+
func (d *DataAccessService) GetSearchValidatorsByWithdrawalEnsName(ctx context.Context, chainId uint64, ensName string) (*t.SearchValidatorsByWithdrawalCredential, error) {
9496
// TODO: implement handling of chainid
9597
// TODO: finalize ens implementation first
9698
return nil, ErrNotFound
9799
}
98100

99-
func (d *DataAccessService) GetSearchValidatorsByGraffiti(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) {
101+
func (d *DataAccessService) GetSearchValidatorsByGraffitiText(ctx context.Context, chainId uint64, graffiti string) (*t.SearchValidatorsByGraffiti, error) {
100102
// TODO: implement handling of chainid
101103
ret := &t.SearchValidatorsByGraffiti{
102104
Graffiti: graffiti,
105+
Hex: hexutil.Encode([]byte(graffiti)),
103106
}
104107
err := db.ReaderDb.GetContext(ctx, &ret.Count, "select count(distinct proposer) from blocks where graffiti_text = $1;", graffiti)
105108
if err != nil {
@@ -110,3 +113,19 @@ func (d *DataAccessService) GetSearchValidatorsByGraffiti(ctx context.Context, c
110113
}
111114
return ret, nil
112115
}
116+
117+
func (d *DataAccessService) GetSearchValidatorsByGraffitiHex(ctx context.Context, chainId uint64, graffiti []byte) (*t.SearchValidatorsByGraffiti, error) {
118+
// TODO: implement handling of chainid
119+
ret := &t.SearchValidatorsByGraffiti{
120+
Graffiti: strings.TrimRight(string(graffiti), "\u0000"),
121+
Hex: hexutil.Encode(graffiti),
122+
}
123+
err := db.ReaderDb.GetContext(ctx, &ret.Count, "select count(distinct proposer) from blocks where graffiti = $1;", graffiti)
124+
if err != nil {
125+
return nil, err
126+
}
127+
if ret.Count == 0 {
128+
return nil, ErrNotFound
129+
}
130+
return ret, nil
131+
}

backend/pkg/api/handlers/input_validation.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ var (
3434
reEthereumAddress = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{40}$`)
3535
reWithdrawalCredential = regexp.MustCompile(`^(0x0[01])?[0-9a-fA-F]{62}$`)
3636
reEnsName = regexp.MustCompile(`^.+\.eth$`)
37-
reGraffiti = regexp.MustCompile(`^.{2,}$`) // at least 2 characters, so that queries won't time out
38-
reCursor = regexp.MustCompile(`^[A-Za-z0-9-_]+$`) // has to be base64
37+
reGraffiti = regexp.MustCompile(`^.{2,}$`) // at least 2 characters, so that queries won't time out
38+
reGraffitiHex = regexp.MustCompile(`^(0x)?([0-9a-fA-F]{2}){2,}$`) // at least 2 bytes, so that queries won't time out
39+
reCursor = regexp.MustCompile(`^[A-Za-z0-9-_]+$`) // has to be base64
3940
reEmail = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
4041
rePassword = regexp.MustCompile(`^.{5,}$`)
4142
reEmailUserToken = regexp.MustCompile(`^[a-z0-9]{40}$`)

backend/pkg/api/handlers/search.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
validatorsByWithdrawalAddress searchTypeKey = "validators_by_withdrawal_address"
3131
validatorsByWithdrawalEns searchTypeKey = "validators_by_withdrawal_ens_name"
3232
validatorsByGraffiti searchTypeKey = "validators_by_graffiti"
33+
validatorsByGraffitiHex searchTypeKey = "validators_by_graffiti_hex"
3334
)
3435

3536
// source of truth for all possible search types and their regex
@@ -70,6 +71,10 @@ var searchTypeMap = map[searchTypeKey]searchType{
7071
regex: reGraffiti,
7172
responseType: string(validatorsByGraffiti),
7273
},
74+
validatorsByGraffitiHex: {
75+
regex: reGraffitiHex,
76+
responseType: string(validatorsByGraffiti),
77+
},
7378
}
7479

7580
type searchType struct {
@@ -172,7 +177,9 @@ func (h *HandlerService) handleSearchType(ctx context.Context, input string, sea
172177
case validatorsByWithdrawalEns:
173178
return h.handleSearchValidatorsByWithdrawalEnsName(ctx, input, chainId)
174179
case validatorsByGraffiti:
175-
return h.handleSearchValidatorsByGraffiti(ctx, input, chainId)
180+
return h.handleSearchValidatorsByGraffitiText(ctx, input, chainId)
181+
case validatorsByGraffitiHex:
182+
return h.handleSearchValidatorsByGraffitiHex(ctx, input, chainId)
176183
default:
177184
return nil, errors.New("invalid search type")
178185
}
@@ -266,11 +273,20 @@ func (h *HandlerService) handleSearchValidatorsByWithdrawalEnsName(ctx context.C
266273
return asSearchResult(validatorsByWithdrawalEns, chainId, result, err)
267274
}
268275

269-
func (h *HandlerService) handleSearchValidatorsByGraffiti(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) {
270-
result, err := h.daService.GetSearchValidatorsByGraffiti(ctx, chainId, input)
276+
func (h *HandlerService) handleSearchValidatorsByGraffitiText(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) {
277+
result, err := h.daService.GetSearchValidatorsByGraffitiText(ctx, chainId, input)
271278
return asSearchResult(validatorsByGraffiti, chainId, result, err)
272279
}
273280

281+
func (h *HandlerService) handleSearchValidatorsByGraffitiHex(ctx context.Context, input string, chainId uint64) (*types.SearchResult, error) {
282+
graffitiHex, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
283+
if err != nil {
284+
return nil, err
285+
}
286+
result, err := h.daService.GetSearchValidatorsByGraffitiHex(ctx, chainId, graffitiHex)
287+
return asSearchResult(validatorsByGraffitiHex, chainId, result, err)
288+
}
289+
274290
// --------------------------------------
275291
// Input Validation
276292

backend/pkg/api/types/search.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ type SearchValidatorsByDepositAddress struct {
1717
Count uint64 `json:"count"`
1818
}
1919

20-
type SearchValidatorsByWithdrwalCredential struct {
20+
type SearchValidatorsByWithdrawalCredential struct {
2121
EnsName string `json:"ens_name,omitempty"`
2222
WithdrawalCredential string `json:"withdrawal_credential"`
2323
Count uint64 `json:"count"`
2424
}
2525

2626
type SearchValidatorsByGraffiti struct {
2727
Graffiti string `json:"graffiti"`
28+
Hex string `json:"hex"`
2829
Count uint64 `json:"count"`
2930
}
3031

@@ -35,5 +36,5 @@ type SearchResult struct {
3536
}
3637

3738
type InternalPostSearchResponse struct {
38-
Data []SearchResult `json:"data" tstype:"({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[]"`
39+
Data []SearchResult `json:"data" tstype:"({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrawalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[]"`
3940
}

frontend/types/api/search.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ export interface SearchValidatorsByDepositAddress {
1616
deposit_address: string;
1717
count: number /* uint64 */;
1818
}
19-
export interface SearchValidatorsByWithdrwalCredential {
19+
export interface SearchValidatorsByWithdrawalCredential {
2020
ens_name?: string;
2121
withdrawal_credential: string;
2222
count: number /* uint64 */;
2323
}
2424
export interface SearchValidatorsByGraffiti {
2525
graffiti: string;
26+
hex: string;
2627
count: number /* uint64 */;
2728
}
2829
export interface SearchResult {
@@ -31,5 +32,5 @@ export interface SearchResult {
3132
value: any;
3233
}
3334
export interface InternalPostSearchResponse {
34-
data: ({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrwalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[];
35+
data: ({ type: 'validator'; chain_id: number; value: SearchValidator } | { type: 'validator_list'; chain_id: number; value: SearchValidatorList } | { type: 'validators_by_deposit_address'; chain_id: number; value: SearchValidatorsByDepositAddress } | { type: 'validators_by_withdrawal_credential'; chain_id: number; value: SearchValidatorsByWithdrawalCredential } | { type: 'validators_by_graffiti'; chain_id: number; value: SearchValidatorsByGraffiti })[];
3536
}

0 commit comments

Comments
 (0)