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

Update registeredrelays #175

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
133 changes: 109 additions & 24 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,38 +832,98 @@ func (m *ApiService) handleOnchainMerkleProof(w http.ResponseWriter, req *http.R

func (m *ApiService) handleValidatorRelayers(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
valPubKey := vars["valpubkey"]
if !IsValidPubkey(valPubKey) {
m.respondError(w, http.StatusInternalServerError, fmt.Sprintf("invalid validator pubkey format"))
valPubKeys := vars["valpubkey"]
if valPubKeys == "" {
m.respondError(w, http.StatusBadRequest, "No validator pubkey provided!")
return
}

keys := strings.Split(valPubKeys, ",")
if len(keys) > 50 {
m.respondError(w, http.StatusBadRequest, "Maximum number of pubkeys exceeded (max: 50)")
return
}

for _, key := range keys {
if !IsValidPubkey(key) {
m.respondError(w, http.StatusBadRequest, "Invalid validator pubkey format: "+key)
return
}
}

results, allValid, err := m.processValidatorsConcurrently(keys)
if err != nil {
m.respondError(w, http.StatusInternalServerError, err.Error())
return
}

m.respondOK(w, httpOkMultiRelayersState{
Validators: results,
AllValidatorsCorrect: allValid,
IncorrectValidators: m.extractIncorrectValidators(results),
})
}

func (m *ApiService) processValidatorsConcurrently(keys []string) ([]httpOkRelayersState, bool, error) {
var results []httpOkRelayersState
allValidatorsRegisteredCorrectFee := true
resultsChan := make(chan validatorRelayResult)

for _, key := range keys {
go m.processSingleValidator(key, resultsChan)
}

for range keys {
res := <-resultsChan
if res.Err != nil {
return nil, false, res.Err
}
if !res.IsValidatorValid {
allValidatorsRegisteredCorrectFee = false
}
results = append(results, res.ValidatorResult)
}
close(resultsChan)

return results, allValidatorsRegisteredCorrectFee, nil
}

func (m *ApiService) processSingleValidator(valPubKey string, resultsChan chan validatorRelayResult) {
var correctFeeRelays []httpRelay
var wrongFeeRelays []httpRelay
var unregisteredRelays []httpRelay
registeredCorrectFee := false

relayers := m.cliCfg.RelayersEndpoints
relays := m.cliCfg.RelayersEndpoints

for _, relay := range relayers {
for _, relay := range relays {
url := fmt.Sprintf("%s/relay/v1/data/validator_registration?pubkey=%s", relay, valPubKey)
resp, err := http.Get(url)
if err != nil {
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
resultsChan <- validatorRelayResult{
Err: fmt.Errorf("error calling relayer %s for validator %s: %v", relay, valPubKey, err),
}
return
}
defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
resultsChan <- validatorRelayResult{
Err: fmt.Errorf("error reading response from relayer %s for validator %s: %v", relay, valPubKey, err),
}
return
}

// If the validator is or has been registered, the relayer will return a 200 message with the signed registration message.
// https://flashbots.github.io/relay-specs/#/Data/getValidatorRegistration
if resp.StatusCode == http.StatusOK {
signedRegistration := &builderApiV1.SignedValidatorRegistration{}

bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
return
}

if err = json.Unmarshal(bodyBytes, signedRegistration); err != nil {
m.respondError(w, http.StatusInternalServerError, "could not call relayer endpoint: "+err.Error())
resultsChan <- validatorRelayResult{
Err: fmt.Errorf("error unmarshalling relay response from relayer %s for validator %s: %v", relay, valPubKey, err),
}
return
}

Expand All @@ -873,29 +933,52 @@ func (m *ApiService) handleValidatorRelayers(w http.ResponseWriter, req *http.Re
Timestamp: fmt.Sprintf("%d", signedRegistration.Message.Timestamp.UnixNano()),
}

// If the fee recipient matches the pool address, the relayer is registered with the correct fee recipient (the smoothing pool one)
if utils.Equals(signedRegistration.Message.FeeRecipient.String(), m.Onchain.PoolAddress) {
correctFeeRelays = append(correctFeeRelays, relayRegistration)
} else {
// if the fee recipient does not match the pool address, the relayer is registered but with the wrong fee recipient
wrongFeeRelays = append(wrongFeeRelays, relayRegistration)
}

} else if resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusNotFound {
// If the validator has never been registered, the relayer will return code 400 or 404 (depending on the relay)
unregisteredRelays = append(unregisteredRelays, httpRelay{RelayAddress: relay})
} else {
unregisteredRelays = append(unregisteredRelays, httpRelay{
RelayAddress: relay,
})
// If we get here, the relayer had an internal server error, so we couldnt check if the validator is/was registered with the correct
// fee recipient. We return an error.
resultsChan <- validatorRelayResult{
Err: fmt.Errorf("error calling relayer %s for validator %s: %v", relay, valPubKey, string(bodyBytes)),
}
}
}

// Only if there are some correct registrations and no invalid ones, its ok
// If there are no wrong fee relays and there are correct fee relays, the validator is registered with the correct fee recipient
// we do not accept validators that have not registered to any relay
if len(wrongFeeRelays) == 0 && len(correctFeeRelays) > 0 {
registeredCorrectFee = true
}

m.respondOK(w, httpOkRelayersState{
CorrectFeeRecipients: registeredCorrectFee,
CorrectFeeRelays: correctFeeRelays,
WrongFeeRelays: wrongFeeRelays,
UnregisteredRelays: unregisteredRelays,
})
resultsChan <- validatorRelayResult{
ValidatorResult: httpOkRelayersState{
ValPubKey: valPubKey,
CorrectFeeRecipients: registeredCorrectFee,
CorrectFeeRelays: correctFeeRelays,
WrongFeeRelays: wrongFeeRelays,
UnregisteredRelays: unregisteredRelays,
},
IsValidatorValid: registeredCorrectFee,
}
}

func (m *ApiService) extractIncorrectValidators(results []httpOkRelayersState) []string {
var incorrectValidators []string
for _, result := range results {
if !result.CorrectFeeRecipients {
incorrectValidators = append(incorrectValidators, result.ValPubKey)
}
}
return incorrectValidators
}

func (m *ApiService) handleState(w http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -924,6 +1007,8 @@ func IsValidAddress(v string) bool {
return re.MatchString(v)
}

// The validator's BLS public key, uniquely identifying them. 48-bytes, hex encoded with 0x prefix, case insensitive.
// example: example: 0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7445a6d1a2753e5f3e8b1cfe39b46f43611ef74a
func IsValidPubkey(v string) bool {
re := regexp.MustCompile("^0x[0-9a-fA-f]{96}$")
return re.MatchString(v)
Expand Down
13 changes: 13 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,19 @@ type httpOkStatus struct {
}

type httpOkRelayersState struct {
ValPubKey string `json:"valpubkey"`
CorrectFeeRecipients bool `json:"correct_fee_recipients"`
CorrectFeeRelays []httpRelay `json:"correct_fee_relayers"`
WrongFeeRelays []httpRelay `json:"wrong_fee_relayers"`
UnregisteredRelays []httpRelay `json:"unregistered_relayers"`
}

type httpOkMultiRelayersState struct {
Validators []httpOkRelayersState `json:"validators"`
AllValidatorsCorrect bool `json:"all_validators_correct"`
IncorrectValidators []string `json:"incorrect_validators"`
}

type httpRelay struct {
RelayAddress string `json:"relay_address"`
FeeRecipient string `json:"fee_recipient"`
Expand Down Expand Up @@ -137,6 +144,12 @@ type httpOkValidatorInfo struct {
SubscriptionType string `json:"subscription_type"`
}

type validatorRelayResult struct {
ValidatorResult httpOkRelayersState
IsValidatorValid bool
Err error
}

// Subscription event and the associated validator (if any)
// TODO: Perhaps remove, no longer need if refactored a bit
type Subscription struct { //TODO: remove
Expand Down
Loading