Skip to content

Commit

Permalink
Merge pull request #2809 from gobitfly/BIDS-2604/AddSearchingForValid…
Browse files Browse the repository at this point in the history
…atorsByWithdrawalCredential

(BIDS-2604) Adds the possibility to search for validators by typing withdrawal credentials
  • Loading branch information
thib-wien authored Jan 31, 2024
2 parents 723348c + 8b1b958 commit 83c9593
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 9 deletions.
2 changes: 1 addition & 1 deletion handlers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2097,7 +2097,7 @@ func ApiValidatorBlsChange(w http.ResponseWriter, r *http.Request) {
Address: fmt.Sprintf("0x%x", d.Address),
Signature: fmt.Sprintf("0x%x", d.Signature),
WithdrawalCredentialsOld: fmt.Sprintf("0x%x", d.WithdrawalCredentialsOld),
WithdrawalCredentialsNew: fmt.Sprintf("0x010000000000000000000000%x", d.Address),
WithdrawalCredentialsNew: fmt.Sprintf("0x"+utils.BeginningOfSetWithdrawalCredentials+"%x", d.Address),
})
}

Expand Down
49 changes: 44 additions & 5 deletions handlers/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ func Search(w http.ResponseWriter, r *http.Request) {
var ensData *types.EnsDomainResponse
if utils.IsValidEnsDomain(search) {
ensData, _ = GetEnsDomain(search)

}
search = strings.Replace(search, "0x", "", -1)
if ensData != nil && len(ensData.Address) > 0 {
http.Redirect(w, r, "/address/"+ensData.Domain, http.StatusMovedPermanently)
} else if utils.IsValidWithdrawalCredentials(search) {
http.Redirect(w, r, "/validators/deposits?q="+search, http.StatusMovedPermanently)
} else if utils.IsValidEth1Tx(search) {
http.Redirect(w, r, "/tx/"+search, http.StatusMovedPermanently)
} else if len(search) == 96 {
Expand Down Expand Up @@ -206,7 +207,6 @@ func SearchAhead(w http.ResponseWriter, r *http.Request) {
err = fmt.Errorf("error searching for eth1AddressHash: %v", err)
}
case "indexed_validators":

// find all validators that have a publickey or index like the search-query
result = &types.SearchAheadValidatorsResult{}
indexNumeric, errParse := strconv.ParseInt(search, 10, 32)
Expand Down Expand Up @@ -251,7 +251,7 @@ func SearchAhead(w http.ResponseWriter, r *http.Request) {
if !searchLikeRE.MatchString(lowerStrippedSearch) {
break
}
// find validators per eth1-address (limit result by N addresses and M validators per address)
// find validators per eth1-address
result = &[]struct {
Eth1Address string `db:"from_address_text" json:"eth1_address"`
Count uint64 `db:"count" json:"count"`
Expand All @@ -267,7 +267,46 @@ func SearchAhead(w http.ResponseWriter, r *http.Request) {
WHERE from_address_text LIKE $1 || '%'
) a
GROUP BY from_address_text`, lowerStrippedSearch)

case "count_indexed_validators_by_withdrawal_credential":
var ensData *types.EnsDomainResponse
if utils.IsValidEnsDomain(search) {
ensData, _ = GetEnsDomain(search)
if len(ensData.Address) > 0 {
lowerStrippedSearch = strings.ToLower(strings.Replace(ensData.Address, "0x", "", -1))
}
}
if len(lowerStrippedSearch) == 40 {
// when the user gives an address (that validators might withdraw to) we transform the address into credentials
lowerStrippedSearch = utils.BeginningOfSetWithdrawalCredentials + lowerStrippedSearch
}
if !utils.IsValidWithdrawalCredentials(lowerStrippedSearch) {
break
}
decodedCredential, decodeErr := hex.DecodeString(lowerStrippedSearch)
if decodeErr != nil {
break
}
// find validators per withdrawal credential
dbFinding := []struct {
DecodedCredential []byte `db:"withdrawalcredentials"`
Count uint64 `db:"count"`
}{}
err = db.ReaderDb.Select(&dbFinding, `
SELECT withdrawalcredentials, COUNT(*) FROM validators
WHERE withdrawalcredentials = $1
GROUP BY withdrawalcredentials`, decodedCredential)
if err == nil {
res := make([]struct {
EncodedCredential string `json:"withdrawalcredentials"`
Count uint64 `json:"count"`
},
len(dbFinding))
for i := range dbFinding {
res[i].EncodedCredential = fmt.Sprintf("%x", dbFinding[i].DecodedCredential)
res[i].Count = dbFinding[i].Count
}
result = &res
}
case "indexed_validators_by_graffiti":
// find validators per graffiti (limit result by N graffities and M validators per graffiti)
res := []struct {
Expand Down Expand Up @@ -353,7 +392,7 @@ func SearchAhead(w http.ResponseWriter, r *http.Request) {
}
}

// search can ether be a valid ETH address or an ENS name mapping to one
// search can either be a valid ETH address or an ENS name mapping to one
func FindValidatorIndicesByEth1Address(search string) (types.SearchValidatorsByEth1Result, error) {
search = strings.ToLower(strings.Replace(ReplaceEnsNameWithAddress(search), "0x", "", -1))
if !utils.IsValidEth1Address(search) {
Expand Down
30 changes: 29 additions & 1 deletion static/js/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ $(document).ready(function () {

// set maxParallelRequests to number of datasets queried in each search
// make sure this is set in every one bloodhound object
let requestNum = 10
let requestNum = 11
var timeWait = 0

// used to overwrite Bloodhounds "transport._get" function which handles the rateLimitWait parameter
Expand Down Expand Up @@ -386,6 +386,20 @@ $(document).ready(function () {
})
bhValidatorsByAddress.remote.transport._get = debounce(bhValidatorsByAddress.remote.transport, bhValidatorsByAddress.remote.transport._get)

var bhValidatorsByWithdrawalCredential = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: function (obj) {
return obj.withdrawalcredentials
},
remote: {
url: "/search/count_indexed_validators_by_withdrawal_credential/%QUERY",
wildcard: "%QUERY",
maxPendingRequests: requestNum,
},
})
bhValidatorsByWithdrawalCredential.remote.transport._get = debounce(bhValidatorsByWithdrawalCredential.remote.transport, bhValidatorsByWithdrawalCredential.remote.transport._get)

var bhPubkey = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
Expand Down Expand Up @@ -527,6 +541,18 @@ $(document).ready(function () {
},
},
},
{
limit: 5,
name: "validators-by-withdrawal-credential",
source: bhValidatorsByWithdrawalCredential,
display: "withdrawalcredentials",
templates: {
header: '<h3 class="h5">Validators by Withdrawal Credentials</h3>',
suggestion: function (data) {
return `<div class="text-monospace text-truncate">${data.count}: 0x${data.withdrawalcredentials}</div>`
},
},
},
{
limit: 5,
name: "graffiti",
Expand Down Expand Up @@ -581,6 +607,8 @@ $(document).ready(function () {
window.location = "/address/" + sug.address
} else if (sug.eth1_address !== undefined) {
window.location = "/validators/deposits?q=" + sug.eth1_address
} else if (sug.withdrawalcredentials !== undefined) {
window.location = "/validators/deposits?q=" + sug.withdrawalcredentials
} else if (sug.graffiti !== undefined) {
// sug.graffiti is html-escaped to prevent xss, we need to unescape it
var el = document.createElement("textarea")
Expand Down
3 changes: 2 additions & 1 deletion utils/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
)

const CalculatingHint = `Calculating…`
const BeginningOfSetWithdrawalCredentials = "010000000000000000000000"

func FormatMessageToHtml(message string) template.HTML {
message = fmt.Sprint(strings.Replace(message, "Error: ", "", 1))
Expand Down Expand Up @@ -743,7 +744,7 @@ func FormatWithdawalCredentials(hash []byte, addCopyButton bool) template.HTML {
}

func FormatAddressToWithdrawalCredentials(address []byte, addCopyButton bool) template.HTML {
credentials, err := hex.DecodeString("010000000000000000000000")
credentials, err := hex.DecodeString(BeginningOfSetWithdrawalCredentials)
if err != nil {
return "INVALID CREDENTIALS"
}
Expand Down
2 changes: 1 addition & 1 deletion utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ func IsApiRequest(r *http.Request) bool {

var eth1AddressRE = regexp.MustCompile("^(0x)?[0-9a-fA-F]{40}$")
var withdrawalCredentialsRE = regexp.MustCompile("^(0x)?00[0-9a-fA-F]{62}$")
var withdrawalCredentialsAddressRE = regexp.MustCompile("^(0x)?010000000000000000000000[0-9a-fA-F]{40}$")
var withdrawalCredentialsAddressRE = regexp.MustCompile("^(0x)?" + BeginningOfSetWithdrawalCredentials + "[0-9a-fA-F]{40}$")
var eth1TxRE = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}$")
var zeroHashRE = regexp.MustCompile("^(0x)?0+$")
var hashRE = regexp.MustCompile("^(0x)?[0-9a-fA-F]{96}$")
Expand Down

0 comments on commit 83c9593

Please sign in to comment.