Skip to content

Commit db44575

Browse files
committed
implement concurrency + fixes
1 parent 30210a7 commit db44575

File tree

2 files changed

+158
-83
lines changed

2 files changed

+158
-83
lines changed

api/api.go

Lines changed: 151 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -832,122 +832,188 @@ func (m *ApiService) handleValidatorRelayers(w http.ResponseWriter, req *http.Re
832832
vars := mux.Vars(req)
833833
valPubKeys := vars["valpubkey"]
834834
if valPubKeys == "" {
835-
m.respondError(w, http.StatusBadRequest, "no validator pubkey provided")
835+
m.respondError(w, http.StatusBadRequest, "No validator pubkey provided!")
836836
return
837837
}
838838

839-
// Split the valPubKeys into individual keys (assuming they are comma-separated)
840839
keys := strings.Split(valPubKeys, ",")
841-
// Check if the number of pubkeys exceeds the maximum limit (50 in this case)
842840
if len(keys) > 50 {
843-
m.respondError(w, http.StatusBadRequest, "maximum number of pubkeys exceeded (max: 50)")
841+
m.respondError(w, http.StatusBadRequest, "Maximum number of pubkeys exceeded (max: 50)")
844842
return
845843
}
844+
845+
for _, key := range keys {
846+
if !IsValidPubkey(key) {
847+
m.respondError(w, http.StatusBadRequest, "Invalid validator pubkey format: "+key)
848+
return
849+
}
850+
}
851+
852+
results, allValid, err := m.processValidatorsConcurrently(keys)
853+
if err != nil {
854+
m.respondError(w, http.StatusInternalServerError, err.Error())
855+
return
856+
}
857+
858+
response := struct {
859+
Validators []httpOkRelayersState
860+
AllValidatorsCorrect bool
861+
IncorrectValidators []string
862+
}{
863+
Validators: results,
864+
AllValidatorsCorrect: allValid,
865+
IncorrectValidators: m.extractIncorrectValidators(results),
866+
}
867+
868+
m.respondOK(w, response)
869+
}
870+
871+
func (m *ApiService) processValidatorsConcurrently(keys []string) ([]httpOkRelayersState, bool, error) {
846872
var results []httpOkRelayersState
873+
allValidatorsRegisteredCorrectFee := true
874+
resultsChan := make(chan ValidatorRelayResult, len(keys))
847875

848-
allValidatorsRegisteredCorrectFee := true // Assume all validators have correct fee registrations by default
849-
var incorrectValidators []string // Initialize an array to store incorrect validators
876+
for i, key := range keys {
877+
go m.processSingleValidator(i, key, resultsChan)
878+
}
850879

851-
for _, valPubKey := range keys {
852-
if !IsValidPubkey(valPubKey) {
853-
m.respondError(w, http.StatusInternalServerError, fmt.Sprintf("invalid validator pubkey format: %s", valPubKey))
854-
return
880+
for range keys {
881+
res := <-resultsChan
882+
if res.Err != nil {
883+
return nil, false, res.Err
855884
}
856-
var correctFeeRelays []httpRelay
857-
var wrongFeeRelays []httpRelay
858-
var unregisteredRelays []httpRelay
859-
registeredCorrectFee := false
860-
var relays []string
861-
862-
if m.Network == "mainnet" {
863-
relays = config.MainnetRelays
864-
} else if m.Network == "goerli" {
865-
relays = config.GoerliRelays
866-
} else {
867-
m.respondError(w, http.StatusInternalServerError, fmt.Sprintf("invalid network: %s", m.Network))
868-
return
885+
if !res.IsValidatorValid {
886+
allValidatorsRegisteredCorrectFee = false
869887
}
888+
results = append(results, res.ValidatorResult)
889+
}
890+
close(resultsChan)
870891

871-
// Iterate through all the relays and check if the validator has registered with the correct fee
872-
// on any of them
873-
for _, relay := range relays {
874-
url := fmt.Sprintf("https://%s/relay/v1/data/validator_registration?pubkey=%s", relay, valPubKey)
875-
resp, err := http.Get(url)
876-
if err != nil {
877-
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
878-
return
879-
}
880-
defer resp.Body.Close()
892+
sort.Slice(results, func(i, j int) bool {
893+
return results[i].ValPubKey < results[j].ValPubKey
894+
})
881895

882-
if resp.StatusCode == http.StatusOK {
883-
signedRegistration := &builderApiV1.SignedValidatorRegistration{}
896+
return results, allValidatorsRegisteredCorrectFee, nil
897+
}
884898

885-
bodyBytes, err := io.ReadAll(resp.Body)
886-
if err != nil {
887-
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
888-
return
889-
}
899+
func (m *ApiService) processSingleValidator(idx int, valPubKey string, resultsChan chan ValidatorRelayResult) {
900+
var correctFeeRelays []httpRelay
901+
var wrongFeeRelays []httpRelay
902+
var unregisteredRelays []httpRelay
903+
registeredCorrectFee := false
904+
var relays []string
905+
906+
if m.Network == "mainnet" {
907+
relays = config.MainnetRelays
908+
} else if m.Network == "goerli" {
909+
relays = config.GoerliRelays
910+
} else {
911+
resultsChan <- ValidatorRelayResult{
912+
Index: idx,
913+
Err: fmt.Errorf("invalid network: %s", m.Network),
914+
}
915+
return
916+
}
890917

891-
if err = json.Unmarshal(bodyBytes, signedRegistration); err != nil {
892-
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
893-
return
894-
}
918+
for _, relay := range relays {
919+
url := fmt.Sprintf("https://%s/relay/v1/data/validator_registration?pubkey=%s", relay, valPubKey)
920+
resp, err := http.Get(url)
921+
if err != nil {
922+
resultsChan <- ValidatorRelayResult{
923+
Index: idx,
924+
Err: fmt.Errorf("error calling relayer %s for validator %s: %v", relay, valPubKey, err),
925+
}
926+
return
927+
}
895928

896-
relayRegistration := httpRelay{
897-
RelayAddress: relay,
898-
FeeRecipient: signedRegistration.Message.FeeRecipient.String(),
899-
Timestamp: fmt.Sprintf("%d", signedRegistration.Message.Timestamp.UnixNano()),
900-
}
929+
bodyBytes, err := io.ReadAll(resp.Body)
930+
resp.Body.Close()
931+
if err != nil {
932+
resultsChan <- ValidatorRelayResult{
933+
Index: idx,
934+
Err: fmt.Errorf("error reading response from relayer %s for validator %s: %v", relay, valPubKey, err),
935+
}
936+
return
937+
}
901938

902-
if utils.Equals(signedRegistration.Message.FeeRecipient.String(), m.Onchain.PoolAddress) {
903-
correctFeeRelays = append(correctFeeRelays, relayRegistration)
904-
} else {
905-
wrongFeeRelays = append(wrongFeeRelays, relayRegistration)
906-
registeredCorrectFee = false // Set to false if any wrong fee registration is found
939+
// If the validator is or has been registered, the relayer will return a 200 message
940+
// with the signed registration message. If the validator has never been registered,
941+
// the relayer will return code 400 or 404 (depending on the relay) with the following message:
942+
// {
943+
// "code": 404,
944+
// "message": "no registration found for validator 0xafcdacfb67396a41a72676f3b064bcf62e977e5ef1d8aebadeed06e97156d4f640516fb205d12211ada9a54fcc26cc58"
945+
// }
946+
// https://flashbots.github.io/relay-specs/#/Data/getValidatorRegistration
947+
if resp.StatusCode == http.StatusOK {
948+
signedRegistration := &builderApiV1.SignedValidatorRegistration{}
949+
950+
if err = json.Unmarshal(bodyBytes, signedRegistration); err != nil {
951+
resultsChan <- ValidatorRelayResult{
952+
Index: idx,
953+
Err: fmt.Errorf("error unmarshalling relay response from relayer %s for validator %s: %v", relay, valPubKey, err),
907954
}
955+
return
956+
}
957+
958+
relayRegistration := httpRelay{
959+
RelayAddress: relay,
960+
FeeRecipient: signedRegistration.Message.FeeRecipient.String(),
961+
Timestamp: fmt.Sprintf("%d", signedRegistration.Message.Timestamp.UnixNano()),
962+
}
963+
964+
// If the fee recipient matches the pool address, the relayer is registered
965+
if utils.Equals(signedRegistration.Message.FeeRecipient.String(), m.Onchain.PoolAddress) {
966+
correctFeeRelays = append(correctFeeRelays, relayRegistration)
908967
} else {
909-
unregisteredRelays = append(unregisteredRelays, httpRelay{
910-
RelayAddress: relay,
911-
})
968+
// if the fee recipient does not match the pool address, the relayer is registered but with the wrong fee recipient
969+
wrongFeeRelays = append(wrongFeeRelays, relayRegistration)
912970
}
913-
}
914971

915-
// Only if there are some correct registrations and no invalid ones, it's ok
916-
if len(wrongFeeRelays) == 0 && len(correctFeeRelays) > 0 {
917-
registeredCorrectFee = true
918-
}
972+
// else if (signedRegistration.Message.FeeRecipient.String() != m.Onchain.PoolAddress) && (signedRegistration.Message.FeeRecipient.String() != "") {
973+
// // If the fee recipient does not match the pool address, the relayer is registered but with the wrong fee recipient
974+
// wrongFeeRelays = append(wrongFeeRelays, relayRegistration)
975+
// }
919976

920-
// If the validator is incorrect, add its pubkey to the incorrectValidators array
921-
if !registeredCorrectFee {
922-
incorrectValidators = append(incorrectValidators, valPubKey)
977+
} else if resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusNotFound {
978+
// the validator is not registered with the relayer
979+
unregisteredRelays = append(unregisteredRelays, httpRelay{RelayAddress: relay})
980+
} else {
981+
// there was an error calling the relayer, so we couldnt check if the validator is/was registered with the correct
982+
// fee recipient, so we return an error.
983+
resultsChan <- ValidatorRelayResult{
984+
Index: idx,
985+
Err: fmt.Errorf("error calling relayer %s for validator %s: %v", relay, valPubKey, string(bodyBytes)),
986+
}
923987
}
988+
}
989+
990+
// If there are no wrong fee relays and there are correct fee relays, the validator is registered with the correct fee recipient
991+
// we do not accept validators that have not registered to any relay
992+
if len(wrongFeeRelays) == 0 && len(correctFeeRelays) > 0 {
993+
registeredCorrectFee = true
994+
}
924995

925-
results = append(results, httpOkRelayersState{
996+
resultsChan <- ValidatorRelayResult{
997+
Index: idx,
998+
ValidatorResult: httpOkRelayersState{
926999
ValPubKey: valPubKey,
9271000
CorrectFeeRecipients: registeredCorrectFee,
9281001
CorrectFeeRelays: correctFeeRelays,
9291002
WrongFeeRelays: wrongFeeRelays,
9301003
UnregisteredRelays: unregisteredRelays,
931-
})
932-
933-
// Update the allValidatorsRegisteredCorrectFee status
934-
if !registeredCorrectFee {
935-
allValidatorsRegisteredCorrectFee = false
936-
}
1004+
},
1005+
IsValidatorValid: registeredCorrectFee,
9371006
}
1007+
}
9381008

939-
// Define and populate the response struct.
940-
response := struct {
941-
Validators []httpOkRelayersState
942-
AllValidatorsCorrect bool
943-
IncorrectValidators []string
944-
}{
945-
Validators: results,
946-
AllValidatorsCorrect: allValidatorsRegisteredCorrectFee,
947-
IncorrectValidators: incorrectValidators,
1009+
func (m *ApiService) extractIncorrectValidators(results []httpOkRelayersState) []string {
1010+
var incorrectValidators []string
1011+
for _, result := range results {
1012+
if !result.CorrectFeeRecipients {
1013+
incorrectValidators = append(incorrectValidators, result.ValPubKey)
1014+
}
9481015
}
949-
950-
m.respondOK(w, response)
1016+
return incorrectValidators
9511017
}
9521018

9531019
func (m *ApiService) handleState(w http.ResponseWriter, req *http.Request) {
@@ -976,6 +1042,8 @@ func IsValidAddress(v string) bool {
9761042
return re.MatchString(v)
9771043
}
9781044

1045+
// The validator's BLS public key, uniquely identifying them. 48-bytes, hex encoded with 0x prefix, case insensitive.
1046+
// example: example: 0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7445a6d1a2753e5f3e8b1cfe39b46f43611ef74a
9791047
func IsValidPubkey(v string) bool {
9801048
re := regexp.MustCompile("^0x[0-9a-fA-f]{96}$")
9811049
return re.MatchString(v)

api/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ type httpOkValidatorInfo struct {
138138
SubscriptionType string `json:"subscription_type"`
139139
}
140140

141+
type ValidatorRelayResult struct {
142+
Index int
143+
ValidatorResult httpOkRelayersState
144+
IsValidatorValid bool
145+
Err error
146+
}
147+
141148
// Subscription event and the associated validator (if any)
142149
// TODO: Perhaps remove, no longer need if refactored a bit
143150
type Subscription struct { //TODO: remove

0 commit comments

Comments
 (0)