From 8851d6a5beae590df243cdfb318878d39d4c5a78 Mon Sep 17 00:00:00 2001 From: Giulio Date: Thu, 5 Mar 2026 16:57:13 +0100 Subject: [PATCH] eip-8161: remove EIP-8160 discovery, use flag-based SSZ-REST configuration Remove getClientCommunicationChannels, exchangeCapabilitiesV2, and getSupportedProtocols. The SSZ-REST endpoint is now enabled solely via the --authrpc.ssz-rest flag. CLs discover it via their own --ssz-rest-url flag. Co-Authored-By: Claude Opus 4.6 --- beacon/engine/ssz.go | 78 -------------------------------- beacon/engine/ssz_test.go | 23 ---------- beacon/engine/types.go | 11 ----- eth/catalyst/api.go | 43 ------------------ eth/catalyst/ssz_rest.go | 47 ------------------- eth/catalyst/ssz_rest_test.go | 85 ----------------------------------- 6 files changed, 287 deletions(-) diff --git a/beacon/engine/ssz.go b/beacon/engine/ssz.go index 24858f6a78..7e9500d187 100644 --- a/beacon/engine/ssz.go +++ b/beacon/engine/ssz.go @@ -229,84 +229,6 @@ func DecodeForkChoiceResponseSSZ(buf []byte) (*ForkChoiceResponse, error) { return resp, nil } -// --- CommunicationChannel SSZ --- - -// EncodeCommunicationChannelsSSZ encodes communication channels to SSZ. -func EncodeCommunicationChannelsSSZ(channels []CommunicationChannel) []byte { - if len(channels) == 0 { - return []byte{} - } - - var totalSize int - for _, ch := range channels { - totalSize += 4 + len(ch.Protocol) + 4 + len(ch.URL) - } - - buf := make([]byte, 4+totalSize) - binary.LittleEndian.PutUint32(buf[0:4], uint32(len(channels))) - - offset := 4 - for _, ch := range channels { - protBytes := []byte(ch.Protocol) - urlBytes := []byte(ch.URL) - - binary.LittleEndian.PutUint32(buf[offset:offset+4], uint32(len(protBytes))) - offset += 4 - copy(buf[offset:], protBytes) - offset += len(protBytes) - - binary.LittleEndian.PutUint32(buf[offset:offset+4], uint32(len(urlBytes))) - offset += 4 - copy(buf[offset:], urlBytes) - offset += len(urlBytes) - } - - return buf -} - -// DecodeCommunicationChannelsSSZ decodes communication channels from SSZ bytes. -func DecodeCommunicationChannelsSSZ(buf []byte) ([]CommunicationChannel, error) { - if len(buf) < 4 { - return nil, fmt.Errorf("CommunicationChannels: buffer too short") - } - - count := binary.LittleEndian.Uint32(buf[0:4]) - if count > 16 { - return nil, fmt.Errorf("CommunicationChannels: too many channels (%d > 16)", count) - } - - channels := make([]CommunicationChannel, 0, count) - offset := uint32(4) - - for i := uint32(0); i < count; i++ { - if offset+4 > uint32(len(buf)) { - return nil, fmt.Errorf("CommunicationChannels: unexpected end of buffer") - } - protLen := binary.LittleEndian.Uint32(buf[offset : offset+4]) - offset += 4 - if protLen > 32 || offset+protLen > uint32(len(buf)) { - return nil, fmt.Errorf("CommunicationChannels: protocol too long or truncated") - } - protocol := string(buf[offset : offset+protLen]) - offset += protLen - - if offset+4 > uint32(len(buf)) { - return nil, fmt.Errorf("CommunicationChannels: unexpected end of buffer") - } - urlLen := binary.LittleEndian.Uint32(buf[offset : offset+4]) - offset += 4 - if urlLen > 256 || offset+urlLen > uint32(len(buf)) { - return nil, fmt.Errorf("CommunicationChannels: URL too long or truncated") - } - url := string(buf[offset : offset+urlLen]) - offset += urlLen - - channels = append(channels, CommunicationChannel{Protocol: protocol, URL: url}) - } - - return channels, nil -} - // --- Capabilities SSZ --- // EncodeCapabilitiesSSZ encodes a list of capability strings to SSZ. diff --git a/beacon/engine/ssz_test.go b/beacon/engine/ssz_test.go index 28e98b4c77..db2e84419e 100644 --- a/beacon/engine/ssz_test.go +++ b/beacon/engine/ssz_test.go @@ -180,29 +180,6 @@ func TestCapabilitiesSSZRoundTrip(t *testing.T) { } } -func TestCommunicationChannelsSSZRoundTrip(t *testing.T) { - channels := []CommunicationChannel{ - {Protocol: "json_rpc", URL: "localhost:8551"}, - {Protocol: "ssz_rest", URL: "http://localhost:8552"}, - } - encoded := EncodeCommunicationChannelsSSZ(channels) - decoded, err := DecodeCommunicationChannelsSSZ(encoded) - if err != nil { - t.Fatalf("decode error: %v", err) - } - if len(decoded) != len(channels) { - t.Fatalf("length mismatch: got %d, want %d", len(decoded), len(channels)) - } - for i, ch := range channels { - if decoded[i].Protocol != ch.Protocol { - t.Errorf("channel[%d].Protocol mismatch: got %s, want %s", i, decoded[i].Protocol, ch.Protocol) - } - if decoded[i].URL != ch.URL { - t.Errorf("channel[%d].URL mismatch: got %s, want %s", i, decoded[i].URL, ch.URL) - } - } -} - func TestClientVersionSSZRoundTrip(t *testing.T) { cv := &ClientVersionV1{ Code: "GE", diff --git a/beacon/engine/types.go b/beacon/engine/types.go index d9b8183624..edf852f70c 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -413,14 +413,3 @@ func (v *ClientVersionV1) String() string { return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit) } -// CommunicationChannel represents a communication protocol supported by the EL (EIP-8160). -type CommunicationChannel struct { - Protocol string `json:"protocol"` - URL string `json:"url"` -} - -// ExchangeCapabilitiesV2Response is the response to engine_exchangeCapabilitiesV2 (EIP-8160). -type ExchangeCapabilitiesV2Response struct { - Capabilities []string `json:"capabilities"` - SupportedProtocols []CommunicationChannel `json:"supportedProtocols"` -} diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index fec7252992..56f47124dd 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -52,12 +52,7 @@ import ( func Register(stack *node.Node, backend *eth.Ethereum) error { api := NewConsensusAPI(backend) - // Configure SSZ-REST fields from the node config cfg := stack.Config() - api.authAddr = cfg.AuthAddr - api.authPort = cfg.AuthPort - api.sszRestEnabled = cfg.SszRestEnabled - api.sszRestPort = cfg.SszRestPort stack.RegisterAPIs([]rpc.API{ newTestingAPI(backend), @@ -145,11 +140,6 @@ type ConsensusAPI struct { forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method newPayloadLock sync.Mutex // Lock for the NewPayload method - // SSZ-REST server config (EIP-8161) - sszRestEnabled bool - sszRestPort int - authAddr string - authPort int } // NewConsensusAPI creates a new consensus api for the given backend. @@ -1109,8 +1099,6 @@ func (api *ConsensusAPI) ExchangeCapabilities([]string) []string { // Methods that should not be advertised via V1 capabilities skip := map[string]bool{ "ExchangeCapabilities": true, - "ExchangeCapabilitiesV2": true, - "GetClientCommunicationChannelsV1": true, } valueT := reflect.TypeOf(api) caps := make([]string, 0, valueT.NumMethod()) @@ -1142,37 +1130,6 @@ func (api *ConsensusAPI) GetClientVersionV1(info engine.ClientVersionV1) []engin } } -// ExchangeCapabilitiesV2 extends ExchangeCapabilities with supported protocols (EIP-8160). -func (api *ConsensusAPI) ExchangeCapabilitiesV2(fromCl []string) engine.ExchangeCapabilitiesV2Response { - capabilities := api.ExchangeCapabilities(fromCl) - return engine.ExchangeCapabilitiesV2Response{ - Capabilities: capabilities, - SupportedProtocols: api.getSupportedProtocols(), - } -} - -// GetClientCommunicationChannelsV1 returns the communication protocols supported by this EL (EIP-8160). -func (api *ConsensusAPI) GetClientCommunicationChannelsV1() []engine.CommunicationChannel { - return api.getSupportedProtocols() -} - -// getSupportedProtocols returns the list of communication protocols supported by this EL. -func (api *ConsensusAPI) getSupportedProtocols() []engine.CommunicationChannel { - channels := []engine.CommunicationChannel{ - { - Protocol: "json_rpc", - URL: fmt.Sprintf("%s:%d", api.authAddr, api.authPort), - }, - } - if api.sszRestEnabled && api.sszRestPort > 0 { - channels = append(channels, engine.CommunicationChannel{ - Protocol: "ssz_rest", - URL: fmt.Sprintf("http://%s:%d", api.authAddr, api.sszRestPort), - }) - } - return channels -} - // GetPayloadBodiesByHashV1 implements engine_getPayloadBodiesByHashV1 which allows for retrieval of a list // of block bodies by the engine api. func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engine.ExecutionPayloadBody { diff --git a/eth/catalyst/ssz_rest.go b/eth/catalyst/ssz_rest.go index 1c09c0df36..9fb6f7e9d4 100644 --- a/eth/catalyst/ssz_rest.go +++ b/eth/catalyst/ssz_rest.go @@ -153,11 +153,6 @@ func (s *SszRestServer) registerRoutes(mux *http.ServeMux) { // getClientVersion mux.HandleFunc("POST /engine/v1/get_client_version", s.handleGetClientVersion) - // getClientCommunicationChannels (deprecated, kept for backward compat) - mux.HandleFunc("POST /engine/v1/get_client_communication_channels", s.handleGetClientCommunicationChannels) - - // exchangeCapabilitiesV2 (EIP-8160) - mux.HandleFunc("POST /engine/v2/exchange_capabilities", s.handleExchangeCapabilitiesV2) } // --- newPayload handlers --- @@ -499,48 +494,6 @@ func (s *SszRestServer) handleGetClientVersion(w http.ResponseWriter, r *http.Re sszResponse(w, engine.EncodeClientVersionsSSZ(result)) } -// --- exchangeCapabilitiesV2 handler (EIP-8160) --- - -func (s *SszRestServer) handleExchangeCapabilitiesV2(w http.ResponseWriter, r *http.Request) { - log.Info("[SSZ-REST] Received ExchangeCapabilitiesV2") - - body, err := readBody(r, 1024*1024) - if err != nil { - sszErrorResponse(w, http.StatusBadRequest, -32602, "failed to read request body") - return - } - - capabilities, err := engine.DecodeCapabilitiesSSZ(body) - if err != nil { - sszErrorResponse(w, http.StatusBadRequest, -32602, err.Error()) - return - } - - result := s.api.ExchangeCapabilitiesV2(capabilities) - - capBuf := engine.EncodeCapabilitiesSSZ(result.Capabilities) - chanBuf := engine.EncodeCommunicationChannelsSSZ(result.SupportedProtocols) - - // SSZ Container: capabilities_offset(4) + channels_offset(4) + data - fixedSize := uint32(8) - buf := make([]byte, 8+len(capBuf)+len(chanBuf)) - binary.LittleEndian.PutUint32(buf[0:4], fixedSize) - binary.LittleEndian.PutUint32(buf[4:8], fixedSize+uint32(len(capBuf))) - copy(buf[8:], capBuf) - copy(buf[8+len(capBuf):], chanBuf) - - sszResponse(w, buf) -} - -// --- getClientCommunicationChannels handler --- - -func (s *SszRestServer) handleGetClientCommunicationChannels(w http.ResponseWriter, r *http.Request) { - log.Info("[SSZ-REST] Received GetClientCommunicationChannels") - - result := s.api.GetClientCommunicationChannelsV1() - sszResponse(w, engine.EncodeCommunicationChannelsSSZ(result)) -} - // handleEngineError converts engine errors to appropriate HTTP error responses. func (s *SszRestServer) handleEngineError(w http.ResponseWriter, err error) { log.Warn("[SSZ-REST] Engine error", "err", err) diff --git a/eth/catalyst/ssz_rest_test.go b/eth/catalyst/ssz_rest_test.go index 44ae91c3c0..8a56831983 100644 --- a/eth/catalyst/ssz_rest_test.go +++ b/eth/catalyst/ssz_rest_test.go @@ -140,88 +140,3 @@ func TestSszRestSuccessFormat(t *testing.T) { } } -// TestSszRestExchangeCapabilitiesV2Format tests the V2 response container format. -func TestSszRestExchangeCapabilitiesV2Format(t *testing.T) { - caps := []string{"engine_newPayloadV4", "engine_getPayloadV4"} - channels := []engine.CommunicationChannel{ - {Protocol: "json_rpc", URL: "localhost:8551"}, - {Protocol: "ssz_rest", URL: "http://localhost:8552"}, - } - - capBuf := engine.EncodeCapabilitiesSSZ(caps) - chanBuf := engine.EncodeCommunicationChannelsSSZ(channels) - - // Build the V2 response container: offset(4) + offset(4) + data - fixedSize := uint32(8) - buf := make([]byte, 8+len(capBuf)+len(chanBuf)) - le32(buf[0:4], fixedSize) - le32(buf[4:8], fixedSize+uint32(len(capBuf))) - copy(buf[8:], capBuf) - copy(buf[8+len(capBuf):], chanBuf) - - // Decode - capOffset := rd32(buf[0:4]) - chanOffset := rd32(buf[4:8]) - - decodedCaps, err := engine.DecodeCapabilitiesSSZ(buf[capOffset:chanOffset]) - if err != nil { - t.Fatal(err) - } - decodedChannels, err := engine.DecodeCommunicationChannelsSSZ(buf[chanOffset:]) - if err != nil { - t.Fatal(err) - } - - if len(decodedCaps) != 2 || decodedCaps[0] != "engine_newPayloadV4" { - t.Errorf("caps mismatch: %v", decodedCaps) - } - if len(decodedChannels) != 2 || decodedChannels[1].Protocol != "ssz_rest" { - t.Errorf("channels mismatch: %v", decodedChannels) - } -} - -// TestSszRestGetSupportedProtocols tests the getSupportedProtocols helper. -func TestSszRestGetSupportedProtocols(t *testing.T) { - api := &ConsensusAPI{ - sszRestEnabled: true, - sszRestPort: 8552, - authAddr: "127.0.0.1", - authPort: 8551, - } - - channels := api.getSupportedProtocols() - if len(channels) != 2 { - t.Fatalf("expected 2 channels, got %d", len(channels)) - } - if channels[0].Protocol != "json_rpc" { - t.Errorf("first channel should be json_rpc, got %s", channels[0].Protocol) - } - if channels[1].Protocol != "ssz_rest" { - t.Errorf("second channel should be ssz_rest, got %s", channels[1].Protocol) - } - if channels[1].URL != "http://127.0.0.1:8552" { - t.Errorf("unexpected URL: %s", channels[1].URL) - } - - // Without SSZ-REST - api2 := &ConsensusAPI{ - sszRestEnabled: false, - authAddr: "127.0.0.1", - authPort: 8551, - } - channels2 := api2.getSupportedProtocols() - if len(channels2) != 1 { - t.Fatalf("expected 1 channel without SSZ-REST, got %d", len(channels2)) - } -} - -func le32(buf []byte, v uint32) { - buf[0] = byte(v) - buf[1] = byte(v >> 8) - buf[2] = byte(v >> 16) - buf[3] = byte(v >> 24) -} - -func rd32(buf []byte) uint32 { - return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 -}