Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(BIDS-2548) Only allow exact matches for the deposits searches #2606

Merged
merged 6 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
254 changes: 137 additions & 117 deletions db/db.go
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ const WithdrawalsQueryLimit = 10000
const BlsChangeQueryLimit = 10000
const MaxSqlInteger = 2147483647

var addressRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{40}$`)
var blsRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{96}$`)

var ErrNoStats = errors.New("no stats available")

func dbTestConnection(dbConn *sqlx.DB, dataBaseName string) {
Expand Down Expand Up @@ -185,8 +182,6 @@ func GetEth1Deposits(address string, length, start uint64) ([]*types.EthOneDepos
return deposits, nil
}

var searchLikeHash = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{2,96}`) // only search for pubkeys if string consists of 96 hex-chars

func GetEth1DepositsJoinEth2Deposits(query string, length, start uint64, orderBy, orderDir string, latestEpoch, validatorOnlineThresholdSlot uint64) ([]*types.EthOneDepositsData, uint64, error) {
deposits := []*types.EthOneDepositsData{}

Expand All @@ -208,38 +203,51 @@ func GetEth1DepositsJoinEth2Deposits(query string, length, start uint64, orderBy
var totalCount uint64
var err error

query = strings.Replace(query, "0x", "", -1)
deposistsCountQuery := `
SELECT COUNT(*) FROM eth1_deposits as eth1
%s`

if searchLikeHash.MatchString(query) {
if query != "" {
err = ReaderDb.Get(&totalCount, `
SELECT COUNT(*) FROM eth1_deposits as eth1
WHERE
ENCODE(eth1.publickey, 'hex') LIKE LOWER($1)
OR ENCODE(eth1.withdrawal_credentials, 'hex') LIKE LOWER($1)
OR ENCODE(eth1.from_address, 'hex') LIKE LOWER($1)
OR ENCODE(tx_hash, 'hex') LIKE LOWER($1)
OR CAST(eth1.block_number AS text) LIKE LOWER($1)`, query+"%")
}
} else {
if query != "" {
err = ReaderDb.Get(&totalCount, `
SELECT COUNT(*) FROM eth1_deposits as eth1
WHERE
CAST(eth1.block_number AS text) LIKE LOWER($1)`, query+"%")
trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
var hash []byte
if len(trimmedQuery)%2 == 0 && utils.HashLikeRegex.MatchString(trimmedQuery) {
hash, err = hex.DecodeString(trimmedQuery)
if err != nil {
return nil, 0, err
}
}

if query == "" {
err = ReaderDb.Get(&totalCount, "SELECT COUNT(*) FROM eth1_deposits")
if trimmedQuery != "" {
if utils.IsHash(trimmedQuery) {
searchQuery := `WHERE eth1.publickey = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if utils.IsEth1Tx(trimmedQuery) {
// Withdrawal credentials have the same length as a tx hash
searchQuery := ""
if utils.IsValidWithdrawalCredentials(trimmedQuery) {
searchQuery = `
WHERE
eth1.tx_hash = $1
OR eth1.withdrawal_credentials = $1`
} else {
searchQuery = `WHERE eth1.tx_hash = $1`
}
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if utils.IsEth1Address(trimmedQuery) {
searchQuery := `WHERE eth1.from_address = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if uiQuery, parseErr := strconv.ParseUint(query, 10, 64); parseErr == nil {
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved
searchQuery := `WHERE eth1.block_number = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), uiQuery)
}
} else {
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, ""))
}
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved

if err != nil && err != sql.ErrNoRows {
if err != nil {
return nil, 0, err
}

if query != "" {
wholeQuery := fmt.Sprintf(`
deposistsQuery := `
SELECT
eth1.tx_hash as tx_hash,
eth1.tx_input as tx_input,
Expand All @@ -263,61 +271,45 @@ func GetEth1DepositsJoinEth2Deposits(query string, length, start uint64, orderBy
) as v
ON
v.pubkey = eth1.publickey
WHERE
ENCODE(eth1.publickey, 'hex') LIKE LOWER($3)
OR ENCODE(eth1.withdrawal_credentials, 'hex') LIKE LOWER($3)
OR ENCODE(eth1.from_address, 'hex') LIKE LOWER($3)
OR ENCODE(tx_hash, 'hex') LIKE LOWER($3)
OR CAST(eth1.block_number AS text) LIKE LOWER($3)
%s
ORDER BY %s %s
LIMIT $1
OFFSET $2`, orderBy, orderDir)
err = ReaderDb.Select(&deposits, wholeQuery, length, start, query+"%")
OFFSET $2`

if trimmedQuery != "" {
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved
if utils.IsHash(trimmedQuery) {
searchQuery := `WHERE eth1.publickey = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if utils.IsEth1Tx(trimmedQuery) {
// Withdrawal credentials have the same length as a tx hash
searchQuery := ""
if utils.IsValidWithdrawalCredentials(trimmedQuery) {
searchQuery = `
WHERE
eth1.tx_hash = $3
OR eth1.withdrawal_credentials = $3`
} else {
searchQuery = `WHERE eth1.tx_hash = $3`
}
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if utils.IsEth1Address(trimmedQuery) {
searchQuery := `WHERE eth1.from_address = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if uiQuery, parseErr := strconv.ParseUint(query, 10, 64); parseErr == nil {
searchQuery := `WHERE eth1.block_number = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, uiQuery)
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
err = ReaderDb.Select(&deposits, fmt.Sprintf(`
SELECT
eth1.tx_hash as tx_hash,
eth1.tx_input as tx_input,
eth1.tx_index as tx_index,
eth1.block_number as block_number,
eth1.block_ts as block_ts,
eth1.from_address as from_address,
eth1.publickey as publickey,
eth1.withdrawal_credentials as withdrawal_credentials,
eth1.amount as amount,
eth1.signature as signature,
eth1.merkletree_index as merkletree_index,
eth1.valid_signature as valid_signature,
COALESCE(v.state, 'deposited') as state
FROM
eth1_deposits as eth1
LEFT JOIN
(
SELECT pubkey, status AS state
FROM validators
) as v
ON
v.pubkey = eth1.publickey
ORDER BY %s %s
LIMIT $1
OFFSET $2`, orderBy, orderDir), length, start)
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, "", orderBy, orderDir), length, start)
}

if err != nil && err != sql.ErrNoRows {
return nil, 0, err
}

return deposits, totalCount, nil
}

func GetEth1DepositsCount() (uint64, error) {
deposits := uint64(0)
err := ReaderDb.Get(&deposits, `SELECT COUNT(*) FROM eth1_deposits`)
if err != nil {
return 0, err
}
return deposits, nil
}

func GetEth1DepositsLeaderboard(query string, length, start uint64, orderBy, orderDir string) ([]*types.EthOneDepositLeaderboardData, uint64, error) {
deposits := []*types.EthOneDepositLeaderboardData{}

Expand Down Expand Up @@ -398,8 +390,9 @@ func GetEth2Deposits(query string, length, start uint64, orderBy, orderDir strin
orderBy = "block_slot"
}

if query != "" {
err := ReaderDb.Select(&deposits, fmt.Sprintf(`
var err error

deposistsQuery := `
SELECT
blocks_deposits.block_slot,
blocks_deposits.block_index,
Expand All @@ -410,65 +403,92 @@ func GetEth2Deposits(query string, length, start uint64, orderBy, orderDir strin
blocks_deposits.signature
FROM blocks_deposits
INNER JOIN blocks ON blocks_deposits.block_root = blocks.blockroot AND blocks.status = '1'
LEFT JOIN eth1_deposits ON blocks_deposits.publickey = eth1_deposits.publickey
WHERE ENCODE(blocks_deposits.publickey, 'hex') LIKE LOWER($3)
OR ENCODE(blocks_deposits.withdrawalcredentials, 'hex') LIKE LOWER($3)
OR CAST(blocks_deposits.block_slot as varchar) LIKE LOWER($3)
OR ENCODE(eth1_deposits.from_address, 'hex') LIKE LOWER($3)
%s
ORDER BY %s %s
LIMIT $1
OFFSET $2`, orderBy, orderDir), length, start, query+"%")
OFFSET $2`

trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
var hash []byte
if len(trimmedQuery)%2 == 0 && utils.HashLikeRegex.MatchString(trimmedQuery) {
hash, err = hex.DecodeString(trimmedQuery)
if err != nil {
return nil, err
}
} else {
err := ReaderDb.Select(&deposits, fmt.Sprintf(`
SELECT
blocks_deposits.block_slot,
blocks_deposits.block_index,
blocks_deposits.proof,
blocks_deposits.publickey,
blocks_deposits.withdrawalcredentials,
blocks_deposits.amount,
blocks_deposits.signature
FROM blocks_deposits
INNER JOIN blocks ON blocks_deposits.block_root = blocks.blockroot AND blocks.status = '1'
ORDER BY %s %s
LIMIT $1
OFFSET $2`, orderBy, orderDir), length, start)
if err != nil {
return nil, err
}

if trimmedQuery != "" {
if utils.IsHash(trimmedQuery) {
searchQuery := `WHERE blocks_deposits.publickey = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if utils.IsValidWithdrawalCredentials(trimmedQuery) {
searchQuery := `WHERE blocks_deposits.withdrawalcredentials = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if utils.IsEth1Address(trimmedQuery) {
searchQuery := `
LEFT JOIN eth1_deposits ON blocks_deposits.publickey = eth1_deposits.publickey
WHERE eth1_deposits.from_address = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, hash)
} else if uiQuery, parseErr := strconv.ParseUint(query, 10, 64); parseErr == nil {
searchQuery := `WHERE blocks_deposits.block_slot = $3`
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, searchQuery, orderBy, orderDir), length, start, uiQuery)
}
} else {
err = ReaderDb.Select(&deposits, fmt.Sprintf(deposistsQuery, "", orderBy, orderDir), length, start)
}

if err != nil && err != sql.ErrNoRows {
return nil, err
}

return deposits, nil
}

func GetEth2DepositsCount(search string) (uint64, error) {
deposits := uint64(0)
func GetEth2DepositsCount(query string) (uint64, error) {
totalCount := uint64(0)

var err error
if search == "" {
err = ReaderDb.Get(&deposits, `
SELECT COUNT(*)
FROM blocks_deposits
INNER JOIN blocks ON blocks_deposits.block_root = blocks.blockroot AND blocks.status = '1'`)
} else {
err = ReaderDb.Get(&deposits, `

deposistsCountQuery := `
SELECT COUNT(*)
FROM blocks_deposits
INNER JOIN blocks ON blocks_deposits.block_root = blocks.blockroot AND blocks.status = '1'
LEFT JOIN eth1_deposits ON blocks_deposits.publickey = eth1_deposits.publickey
WHERE ENCODE(blocks_deposits.publickey, 'hex') LIKE LOWER($1)
OR ENCODE(blocks_deposits.withdrawalcredentials, 'hex') LIKE LOWER($1)
OR CAST(blocks_deposits.block_slot as varchar) LIKE LOWER($1)
OR ENCODE(eth1_deposits.from_address, 'hex') LIKE LOWER($1)
`, search+"%")
%s`

trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
var hash []byte
if len(trimmedQuery)%2 == 0 && utils.HashLikeRegex.MatchString(trimmedQuery) {
hash, err = hex.DecodeString(trimmedQuery)
if err != nil {
return 0, err
}
}

if trimmedQuery != "" {
if utils.IsHash(trimmedQuery) {
searchQuery := `WHERE blocks_deposits.publickey = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if utils.IsValidWithdrawalCredentials(trimmedQuery) {
searchQuery := `WHERE blocks_deposits.withdrawalcredentials = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if utils.IsEth1Address(trimmedQuery) {
searchQuery := `
LEFT JOIN eth1_deposits ON blocks_deposits.publickey = eth1_deposits.publickey
WHERE eth1_deposits.from_address = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), hash)
} else if uiQuery, parseErr := strconv.ParseUint(query, 10, 64); parseErr == nil {
searchQuery := `WHERE blocks_deposits.block_slot = $1`
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, searchQuery), uiQuery)
}
} else {
err = ReaderDb.Get(&totalCount, fmt.Sprintf(deposistsCountQuery, ""))
}

if err != nil {
return 0, err
}

return deposits, nil
return totalCount, nil
}
func GetSlashingCount() (uint64, error) {
slashings := uint64(0)
Expand Down Expand Up @@ -2279,7 +2299,7 @@ func GetWithdrawalsCountForQuery(query string) (uint64, error) {
var err error = nil

trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
if addressRE.MatchString(query) {
if utils.IsEth1Address(query) {
searchQuery := `WHERE w.address = $1`
addr, decErr := hex.DecodeString(trimmedQuery)
if err != nil {
Expand Down Expand Up @@ -2344,7 +2364,7 @@ func GetWithdrawals(query string, length, start uint64, orderBy, orderDir string

trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
if trimmedQuery != "" {
if addressRE.MatchString(query) {
if utils.IsEth1Address(query) {
searchQuery := `WHERE w.address = $3`
addr, decErr := hex.DecodeString(trimmedQuery)
if decErr != nil {
Expand Down Expand Up @@ -2944,7 +2964,7 @@ func GetBLSChangesCountForQuery(query string) (uint64, error) {
trimmedQuery := strings.ToLower(strings.TrimPrefix(query, "0x"))
var err error = nil

if blsRE.MatchString(query) {
if utils.IsHash(query) {
searchQuery := `WHERE bls.pubkey = $1`
pubkey, decErr := hex.DecodeString(trimmedQuery)
if decErr != nil {
Expand Down Expand Up @@ -3004,7 +3024,7 @@ func GetBLSChanges(query string, length, start uint64, orderBy, orderDir string)
var err error = nil

if trimmedQuery != "" {
if blsRE.MatchString(query) {
if utils.IsHash(query) {
searchQuery := `WHERE bls.pubkey = $3`
pubkey, decErr := hex.DecodeString(trimmedQuery)
if decErr != nil {
Expand Down
4 changes: 2 additions & 2 deletions handlers/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ type ValidatorsDataQueryParams struct {
StateFilter string
}

var searchPubkeyExactRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{96}`) // only search for pubkeys if string consists of 96 hex-chars
var searchPubkeyLikeRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{2,96}`) // only search for pubkeys if string consists of 96 hex-chars
var searchPubkeyExactRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{96}`) // only search for pubkeys if string consists of 96 hex-chars
var searchPubkeyLikeRE = regexp.MustCompile(`^(0x)?[0-9a-fA-F]{2,96}`)

func parseValidatorsDataQueryParams(r *http.Request) (*ValidatorsDataQueryParams, error) {
q := r.URL.Query()
Expand Down
11 changes: 9 additions & 2 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,8 @@ func IsValidEth1Address(s string) bool {
return !zeroHashRE.MatchString(s) && eth1AddressRE.MatchString(s)
}

// IsEth1Address verifies whether a string represents an eth1-address. In contrast to IsValidEth1Address, this also returns true for the 0x0 address
// IsEth1Address verifies whether a string represents an eth1-address.
// In contrast to IsValidEth1Address, this also returns true for the 0x0 address
func IsEth1Address(s string) bool {
return eth1AddressRE.MatchString(s)
}
Expand All @@ -751,7 +752,13 @@ func IsValidEth1Tx(s string) bool {
return !zeroHashRE.MatchString(s) && eth1TxRE.MatchString(s)
}

// IsValidEth1Tx verifies whether a string represents a valid eth1-tx-hash.
// IsEth1Tx verifies whether a string represents an eth1-tx-hash.
// In contrast to IsValidEth1Tx, this also returns true for the 0x0 address
func IsEth1Tx(s string) bool {
return eth1TxRE.MatchString(s)
}

// IsHash verifies whether a string represents an eth1-hash.
LuccaBitfly marked this conversation as resolved.
Show resolved Hide resolved
func IsHash(s string) bool {
return hashRE.MatchString(s)
}
Expand Down
Loading