diff --git a/builder/builder/builder.go b/builder/builder/builder.go index 913a199ed..d347e01d4 100644 --- a/builder/builder/builder.go +++ b/builder/builder/builder.go @@ -256,46 +256,23 @@ func (b *Builder) Start() error { return b.SubscribeProposerConstraints() } -// GenerateAuthenticationHeader generates an authentication string for the builder -// to subscribe to SSE constraint events emitted by relays -func (b *Builder) GenerateAuthenticationHeader() (string, error) { - // NOTE: the `slot` acts similarly to a nonce for the message to sign, to avoid replay attacks. - slot := b.slotAttrs.Slot - message, err := json.Marshal(common.ConstraintSubscriptionAuth{PublicKey: b.builderPublicKey, Slot: slot}) - if err != nil { - log.Error(fmt.Sprintf("Failed to marshal auth message: %v", err)) - return "", err - } - signatureEC := bls.Sign(b.builderSecretKey, message) - subscriptionSignatureJSON := `"` + phase0.BLSSignature(bls.SignatureToBytes(signatureEC)[:]).String() + `"` - authHeader := "BOLT " + subscriptionSignatureJSON + "," + string(message) - return authHeader, nil -} - // SubscribeProposerConstraints subscribes to the constraints made by Bolt proposers // which the builder pulls from relay(s) using SSE. func (b *Builder) SubscribeProposerConstraints() error { - // Create authentication signed message - authHeader, err := b.GenerateAuthenticationHeader() - if err != nil { - log.Error(fmt.Sprintf("Failed to generate authentication header: %v", err)) - return err - } - // Check if `b.relay` is a RemoteRelayAggregator, if so we need to subscribe to // the constraints made available by all the relays relayAggregator, ok := b.relay.(*RemoteRelayAggregator) if ok { for _, relay := range relayAggregator.relays { - go b.subscribeToRelayForConstraints(relay.Config().Endpoint, authHeader) + go b.subscribeToRelayForConstraints(relay.Config().Endpoint) } } else { - go b.subscribeToRelayForConstraints(b.relay.Config().Endpoint, authHeader) + go b.subscribeToRelayForConstraints(b.relay.Config().Endpoint) } return nil } -func (b *Builder) subscribeToRelayForConstraints(relayBaseEndpoint, authHeader string) error { +func (b *Builder) subscribeToRelayForConstraints(relayBaseEndpoint string) error { attempts := 0 maxAttempts := 60 // Max 10 minutes of retries retryInterval := 10 * time.Second @@ -315,7 +292,6 @@ func (b *Builder) subscribeToRelayForConstraints(relayBaseEndpoint, authHeader s log.Error(fmt.Sprintf("Failed to create new http request: %v", err)) return err } - req.Header.Set("Authorization", authHeader) client := http.Client{} diff --git a/builder/builder/builder_test.go b/builder/builder/builder_test.go index 6c1948183..2badb9196 100644 --- a/builder/builder/builder_test.go +++ b/builder/builder/builder_test.go @@ -443,10 +443,7 @@ func TestSubscribeProposerConstraints(t *testing.T) { _, ok := builder.constraintsCache.Get(0) require.Equal(t, false, ok) - // Create authentication signed message - authHeader, err := builder.GenerateAuthenticationHeader() - require.NoError(t, err) - builder.subscribeToRelayForConstraints(builder.relay.Config().Endpoint, authHeader) + builder.subscribeToRelayForConstraints(builder.relay.Config().Endpoint) // Wait 2 seconds to save all constraints in cache time.Sleep(2 * time.Second) diff --git a/mev-boost-relay/services/api/service.go b/mev-boost-relay/services/api/service.go index 7430a35bc..81c405cd1 100644 --- a/mev-boost-relay/services/api/service.go +++ b/mev-boost-relay/services/api/service.go @@ -3014,25 +3014,7 @@ func (api *RelayAPI) handleSubscribeConstraints(w http.ResponseWriter, req *http w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") - // TODO: Docs regarding this autorization schema - // NOTE: This authorization schema is not final, but works for now. - auth := req.Header.Get("Authorization") - - builderPublicKey, err := validateConstraintSubscriptionAuth(auth, api.headSlot.Load()) - if err != nil { - api.log.Infof("Failed to validate constraint subscription auth: %s. err: %s", auth, err) - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } - - _, ok := api.checkBuilderEntry(w, api.log, builderPublicKey) - if !ok { - api.log.Infof("Builder rejected: %s", builderPublicKey) - http.Error(w, "Builder rejected", http.StatusUnauthorized) - return - } - - api.log.Infof("New constraints consumer with builder pubkey: %s", builderPublicKey) + api.log.Infof("New constraints consumer connected") // Add the new consumer constraintsCh := make(chan *SignedConstraints, 256) @@ -3065,6 +3047,7 @@ func (api *RelayAPI) handleSubscribeConstraints(w http.ResponseWriter, req *http return case <-ticker.C: // Send a keepalive to the client + // NOTE: the length of the message is intentional, do not make it shorter fmt.Fprint(w, ": keepaliveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n\n") flusher.Flush() case constraint := <-constraintsCh: diff --git a/mev-boost-relay/services/api/service_test.go b/mev-boost-relay/services/api/service_test.go index 432490a67..833195f70 100644 --- a/mev-boost-relay/services/api/service_test.go +++ b/mev-boost-relay/services/api/service_test.go @@ -398,17 +398,6 @@ func TestSubscribeToConstraints(t *testing.T) { // Wait for the server to start time.Sleep(500 * time.Millisecond) - // Setup information of the builder making the request - builderPrivateKey, builderPublicKey, err := bls.GenerateNewKeypair() - require.NoError(t, err) - var phase0BuilderPublicKey phase0.BLSPubKey = builderPublicKey.Bytes() - - headSlot := backend.relay.headSlot.Load() - message, err := json.Marshal(ConstraintSubscriptionAuth{PublicKey: phase0BuilderPublicKey, Slot: headSlot}) - require.NoError(t, err) - signatureEC := bls.Sign(builderPrivateKey, message) - subscriptionSignatureJSON := `"` + phase0.BLSSignature(bls.SignatureToBytes(signatureEC)[:]).String() + `"` - // Run the request in a goroutine so that it doesn't block the test, // but it finishes as soon as the message is sent over the channel go func() { @@ -418,10 +407,6 @@ func TestSubscribeToConstraints(t *testing.T) { log.Fatalf("Failed to create request: %v", err) } - // Add authentication - authHeader := "BOLT " + subscriptionSignatureJSON + "," + string(message) - req.Header.Set("Authorization", authHeader) - // Send the request client := &http.Client{} // NOTE: this response arrives after the first data is flushed diff --git a/mev-boost-relay/services/api/utils.go b/mev-boost-relay/services/api/utils.go index fe0f37cfb..9c4b98008 100644 --- a/mev-boost-relay/services/api/utils.go +++ b/mev-boost-relay/services/api/utils.go @@ -150,62 +150,12 @@ func verifyBlockSignature(block *common.VersionedSignedBlindedBeaconBlock, domai return bls.VerifySignatureBytes(msg[:], sig[:], pubKey[:]) } -func getPayloadAttributesKey(parentHash string, slot uint64) string { - return fmt.Sprintf("%s-%d", parentHash, slot) -} - func broadcastToChannels[T any](constraintsConsumers []chan *T, constraint *T) { for _, consumer := range constraintsConsumers { consumer <- constraint } } -// validateConstraintSubscriptionAuth checks the authentication string data from the Builder, -// and returns its BLS public key if the authentication is valid. -func validateConstraintSubscriptionAuth(auth string, headSlot uint64) (phase0.BLSPubKey, error) { - zeroKey := phase0.BLSPubKey{} - if auth == "" { - return zeroKey, errors.Errorf("Authorization header missing") - } - // Authorization: - parts := strings.Split(auth, " ") - if len(parts) != 2 { - return zeroKey, errors.Errorf("Ill-formed authorization header") - } - if parts[0] != "BOLT" { - return zeroKey, errors.Errorf("Not BOLT authentication scheme") - } - // , - parts = strings.SplitN(parts[1], ",", 2) - if len(parts) != 2 { - return zeroKey, errors.Errorf("Ill-formed authorization header") - } - - signature := new(phase0.BLSSignature) - if err := signature.UnmarshalJSON([]byte(parts[0])); err != nil { - fmt.Println("Failed to unmarshal authData: ", err) - return zeroKey, errors.Errorf("Ill-formed authorization header") - } - - authDataRaw := []byte(parts[1]) - authData := new(ConstraintSubscriptionAuth) - if err := json.Unmarshal(authDataRaw, authData); err != nil { - fmt.Println("Failed to unmarshal authData: ", err) - return zeroKey, errors.Errorf("Ill-formed authorization header") - } - - // FIXME: this is broken on the devnet, let's skip it for now - // if headSlot != authData.Slot { - // return zeroKey, errors.Errorf("Invalid head slot. Expected %d, got %d", headSlot, authData.Slot) - // } - - ok, err := bls.VerifySignatureBytes(authDataRaw, signature[:], authData.PublicKey[:]) - if err != nil || !ok { - return zeroKey, errors.Errorf("Invalid signature") - } - return authData.PublicKey, nil -} - func JSONStringify[T any](obj T) string { out, err := json.Marshal(obj) if err != nil {