diff --git a/beacon-chain/rpc/eth/blob/handlers.go b/beacon-chain/rpc/eth/blob/handlers.go index 23c49c4ed..dd40c4620 100644 --- a/beacon-chain/rpc/eth/blob/handlers.go +++ b/beacon-chain/rpc/eth/blob/handlers.go @@ -1,6 +1,7 @@ package blob import ( + "fmt" "net/http" "net/url" "strconv" @@ -21,7 +22,11 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) { defer span.End() var sidecars []*eth.BlobSidecar - indices := parseIndices(r.URL) + indices, err := parseIndices(r.URL) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusBadRequest) + return + } segments := strings.Split(r.URL.Path, "/") blockId := segments[len(segments)-1] @@ -63,16 +68,19 @@ func (s *Server) Blobs(w http.ResponseWriter, r *http.Request) { } // parseIndices filters out invalid and duplicate blob indices -func parseIndices(url *url.URL) []uint64 { +func parseIndices(url *url.URL) ([]uint64, error) { rawIndices := url.Query()["indices"] indices := make([]uint64, 0, field_params.MaxBlobsPerBlock) + invalidIndices := make([]string, 0) loop: for _, raw := range rawIndices { ix, err := strconv.ParseUint(raw, 10, 64) if err != nil { + invalidIndices = append(invalidIndices, raw) continue } if ix >= field_params.MaxBlobsPerBlock { + invalidIndices = append(invalidIndices, raw) continue } for i := range indices { @@ -82,7 +90,11 @@ loop: } indices = append(indices, ix) } - return indices + + if len(invalidIndices) > 0 { + return nil, fmt.Errorf("requested blob indices %v are invalid", invalidIndices) + } + return indices, nil } func buildSidecarsResponse(sidecars []*eth.BlobSidecar) *structs.SidecarsResponse { diff --git a/beacon-chain/rpc/eth/blob/handlers_test.go b/beacon-chain/rpc/eth/blob/handlers_test.go index cfe293f0e..707cc567d 100644 --- a/beacon-chain/rpc/eth/blob/handlers_test.go +++ b/beacon-chain/rpc/eth/blob/handlers_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "reflect" "strings" "testing" "time" @@ -27,10 +28,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/testing/util" ) -func TestParseIndices(t *testing.T) { - assert.DeepEqual(t, []uint64{1, 2, 3}, parseIndices(&url.URL{RawQuery: "indices=100&indices=1&indices=2&indices=foo&indices=1&indices=3&bar=bar"})) -} - func TestBlobs(t *testing.T) { params.SetupTestConfigCleanup(t) cfg := params.BeaconConfig().Copy() @@ -372,3 +369,46 @@ func TestBlobs(t *testing.T) { require.Equal(t, len(writer.Body.Bytes()), 131932) }) } + +func Test_parseIndices(t *testing.T) { + tests := []struct { + name string + query string + want []uint64 + wantErr string + }{ + { + name: "happy path with duplicate indices within bound and other query parameters ignored", + query: "indices=1&indices=2&indices=1&indices=3&bar=bar", + want: []uint64{1, 2, 3}, + }, + { + name: "out of bounds indices throws error", + query: "indices=6&indices=7", + wantErr: "requested blob indices [6 7] are invalid", + }, + { + name: "negative indices", + query: "indices=-1&indices=-8", + wantErr: "requested blob indices [-1 -8] are invalid", + }, + { + name: "invalid indices", + query: "indices=foo&indices=bar", + wantErr: "requested blob indices [foo bar] are invalid", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseIndices(&url.URL{RawQuery: tt.query}) + if err != nil && tt.wantErr != "" { + require.StringContains(t, tt.wantErr, err.Error()) + return + } + require.NoError(t, err) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseIndices() got = %v, want %v", got, tt.want) + } + }) + } +}