From 7b8aedbfe41ebfdc56ea5bbe29bd1ac10c3fdb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Mon, 18 Oct 2021 20:55:18 +0200 Subject: [PATCH] Update Beacon API to v2.1.0 (#9797) * `Eth-Consensus-Version` header * rename unused receiver * omit receiver name Co-authored-by: Raul Jordan Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- .../rpc/apimiddleware/custom_handlers.go | 23 +++++----- .../rpc/apimiddleware/custom_handlers_test.go | 43 ++++++++++++++++--- beacon-chain/rpc/apimiddleware/structs.go | 20 +++++++++ 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/beacon-chain/rpc/apimiddleware/custom_handlers.go b/beacon-chain/rpc/apimiddleware/custom_handlers.go index 2f66be3ade..4d6744433c 100644 --- a/beacon-chain/rpc/apimiddleware/custom_handlers.go +++ b/beacon-chain/rpc/apimiddleware/custom_handlers.go @@ -96,12 +96,12 @@ func handleGetSSZ( apimiddleware.WriteError(w, errJson, nil) return true } - responseSsz, errJson := serializeMiddlewareResponseIntoSSZ(config.responseJson.SSZData()) + respVersion, responseSsz, errJson := serializeMiddlewareResponseIntoSSZ(config.responseJson) if errJson != nil { apimiddleware.WriteError(w, errJson, nil) return true } - if errJson := writeSSZResponseHeaderAndBody(grpcResponse, w, responseSsz, config.fileName); errJson != nil { + if errJson := writeSSZResponseHeaderAndBody(grpcResponse, w, responseSsz, respVersion, config.fileName); errJson != nil { apimiddleware.WriteError(w, errJson, nil) return true } @@ -143,16 +143,16 @@ func prepareSSZRequestForProxying( return nil } -func serializeMiddlewareResponseIntoSSZ(data string) (sszResponse []byte, errJson apimiddleware.ErrorJson) { +func serializeMiddlewareResponseIntoSSZ(respJson sszResponseJson) (version string, ssz []byte, errJson apimiddleware.ErrorJson) { // Serialize the SSZ part of the deserialized value. - b, err := base64.StdEncoding.DecodeString(data) + data, err := base64.StdEncoding.DecodeString(respJson.SSZData()) if err != nil { - return nil, apimiddleware.InternalServerErrorWithMessage(err, "could not decode response body into base64") + return "", nil, apimiddleware.InternalServerErrorWithMessage(err, "could not decode response body into base64") } - return b, nil + return strings.ToLower(respJson.SSZVersion()), data, nil } -func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWriter, responseSsz []byte, fileName string) apimiddleware.ErrorJson { +func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWriter, respSsz []byte, respVersion, fileName string) apimiddleware.ErrorJson { var statusCodeHeader string for h, vs := range grpcResp.Header { // We don't want to expose any gRPC metadata in the HTTP response, so we skip forwarding metadata headers. @@ -166,6 +166,10 @@ func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWrite } } } + w.Header().Set("Content-Length", strconv.Itoa(len(respSsz))) + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename="+fileName) + w.Header().Set("Eth-Consensus-Version", respVersion) if statusCodeHeader != "" { code, err := strconv.Atoi(statusCodeHeader) if err != nil { @@ -175,10 +179,7 @@ func writeSSZResponseHeaderAndBody(grpcResp *http.Response, w http.ResponseWrite } else { w.WriteHeader(grpcResp.StatusCode) } - w.Header().Set("Content-Length", strconv.Itoa(len(responseSsz))) - w.Header().Set("Content-Type", "application/octet-stream") - w.Header().Set("Content-Disposition", "attachment; filename="+fileName) - if _, err := io.Copy(w, ioutil.NopCloser(bytes.NewReader(responseSsz))); err != nil { + if _, err := io.Copy(w, ioutil.NopCloser(bytes.NewReader(respSsz))); err != nil { return apimiddleware.InternalServerErrorWithMessage(err, "could not write response message") } return nil diff --git a/beacon-chain/rpc/apimiddleware/custom_handlers_test.go b/beacon-chain/rpc/apimiddleware/custom_handlers_test.go index 98b52560df..fa04e77c5f 100644 --- a/beacon-chain/rpc/apimiddleware/custom_handlers_test.go +++ b/beacon-chain/rpc/apimiddleware/custom_handlers_test.go @@ -18,6 +18,19 @@ import ( "github.com/r3labs/sse" ) +type testSSZResponseJson struct { + Version string `json:"version"` + Data string `json:"data"` +} + +func (t testSSZResponseJson) SSZVersion() string { + return t.Version +} + +func (t testSSZResponseJson) SSZData() string { + return t.Data +} + func TestSSZRequested(t *testing.T) { t.Run("ssz_requested", func(t *testing.T) { request := httptest.NewRequest("GET", "http://foo.example", nil) @@ -64,13 +77,22 @@ func TestPrepareSSZRequestForProxying(t *testing.T) { func TestSerializeMiddlewareResponseIntoSSZ(t *testing.T) { t.Run("ok", func(t *testing.T) { - ssz, errJson := serializeMiddlewareResponseIntoSSZ("Zm9v") + j := testSSZResponseJson{ + Version: "Version", + Data: "Zm9v", + } + v, ssz, errJson := serializeMiddlewareResponseIntoSSZ(j) require.Equal(t, true, errJson == nil) assert.DeepEqual(t, []byte("foo"), ssz) + assert.Equal(t, "version", v) }) t.Run("invalid_data", func(t *testing.T) { - _, errJson := serializeMiddlewareResponseIntoSSZ("invalid") + j := testSSZResponseJson{ + Version: "Version", + Data: "invalid", + } + _, _, errJson := serializeMiddlewareResponseIntoSSZ(j) require.Equal(t, false, errJson == nil) assert.Equal(t, true, strings.Contains(errJson.Msg(), "could not decode response body into base64")) assert.Equal(t, http.StatusInternalServerError, errJson.StatusCode()) @@ -78,6 +100,10 @@ func TestSerializeMiddlewareResponseIntoSSZ(t *testing.T) { } func TestWriteSSZResponseHeaderAndBody(t *testing.T) { + responseSsz := []byte("ssz") + version := "version" + fileName := "test.ssz" + t.Run("ok", func(t *testing.T) { response := &http.Response{ Header: http.Header{ @@ -85,11 +111,11 @@ func TestWriteSSZResponseHeaderAndBody(t *testing.T) { "Grpc-Metadata-" + grpc.HttpCodeMetadataKey: []string{"204"}, }, } - responseSsz := []byte("ssz") + writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, "test.ssz") + errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, version, fileName) require.Equal(t, true, errJson == nil) v, ok := writer.Header()["Foo"] require.Equal(t, true, ok, "header not found") @@ -107,6 +133,10 @@ func TestWriteSSZResponseHeaderAndBody(t *testing.T) { require.Equal(t, true, ok, "header not found") require.Equal(t, 1, len(v), "wrong number of header values") assert.Equal(t, "attachment; filename=test.ssz", v[0]) + v, ok = writer.Header()["Eth-Consensus-Version"] + require.Equal(t, true, ok, "header not found") + require.Equal(t, 1, len(v), "wrong number of header values") + assert.Equal(t, "version", v[0]) assert.Equal(t, 204, writer.Code) }) @@ -115,11 +145,10 @@ func TestWriteSSZResponseHeaderAndBody(t *testing.T) { Header: http.Header{}, StatusCode: 204, } - responseSsz := []byte("ssz") writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, "test.ssz") + errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, version, fileName) require.Equal(t, true, errJson == nil) assert.Equal(t, 204, writer.Code) }) @@ -135,7 +164,7 @@ func TestWriteSSZResponseHeaderAndBody(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, "test.ssz") + errJson := writeSSZResponseHeaderAndBody(response, writer, responseSsz, version, fileName) require.Equal(t, false, errJson == nil) assert.Equal(t, true, strings.Contains(errJson.Msg(), "could not parse status code")) assert.Equal(t, http.StatusInternalServerError, errJson.StatusCode()) diff --git a/beacon-chain/rpc/apimiddleware/structs.go b/beacon-chain/rpc/apimiddleware/structs.go index c9bc3a5145..7375a94ce0 100644 --- a/beacon-chain/rpc/apimiddleware/structs.go +++ b/beacon-chain/rpc/apimiddleware/structs.go @@ -1,7 +1,10 @@ package apimiddleware import ( + "strings" + "github.com/prysmaticlabs/prysm/api/gateway/apimiddleware" + ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" ) // genesisResponseJson is used in /beacon/genesis API endpoint. @@ -656,6 +659,7 @@ type syncCommitteeContributionJson struct { // sszResponseJson is a common abstraction over all SSZ responses. type sszResponseJson interface { + SSZVersion() string SSZData() string } @@ -668,6 +672,10 @@ func (ssz *blockSSZResponseJson) SSZData() string { return ssz.Data } +func (*blockSSZResponseJson) SSZVersion() string { + return strings.ToLower(ethpbv2.Version_PHASE0.String()) +} + // blockSSZResponseV2Json is used in /v2/beacon/blocks/{block_id} API endpoint. type blockSSZResponseV2Json struct { Version string `json:"version"` @@ -678,6 +686,10 @@ func (ssz *blockSSZResponseV2Json) SSZData() string { return ssz.Data } +func (ssz *blockSSZResponseV2Json) SSZVersion() string { + return ssz.Version +} + // beaconStateSSZResponseJson is used in /debug/beacon/states/{state_id} API endpoint. type beaconStateSSZResponseJson struct { Data string `json:"data"` @@ -687,6 +699,10 @@ func (ssz *beaconStateSSZResponseJson) SSZData() string { return ssz.Data } +func (*beaconStateSSZResponseJson) SSZVersion() string { + return strings.ToLower(ethpbv2.Version_PHASE0.String()) +} + // beaconStateSSZResponseV2Json is used in /v2/debug/beacon/states/{state_id} API endpoint. type beaconStateSSZResponseV2Json struct { Version string `json:"version"` @@ -697,6 +713,10 @@ func (ssz *beaconStateSSZResponseV2Json) SSZData() string { return ssz.Data } +func (ssz *beaconStateSSZResponseV2Json) SSZVersion() string { + return ssz.Version +} + // --------------- // Events. // ---------------