diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index e3950b2a76..a6f7930fe4 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -58,6 +58,28 @@ type txJSON struct { Hash common.Hash `json:"hash"` } +var errBlobTxSidecarInJSON = errors.New("blob transaction sidecar fields are not supported in JSON") + +func (tx *txJSON) UnmarshalJSON(input []byte) error { + type txJSONNoMethod txJSON + dec := struct { + *txJSONNoMethod + Version json.RawMessage `json:"version,omitempty"` + Blobs json.RawMessage `json:"blobs,omitempty"` + Commitments json.RawMessage `json:"commitments,omitempty"` + Proofs json.RawMessage `json:"proofs,omitempty"` + }{ + txJSONNoMethod: (*txJSONNoMethod)(tx), + } + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Version != nil || dec.Blobs != nil || dec.Commitments != nil || dec.Proofs != nil { + return errBlobTxSidecarInJSON + } + return nil +} + // yParityValue returns the YParity value from JSON. For backwards-compatibility reasons, // this can be given in the 'v' field or the 'yParity' field. If both exist, they must match. func (tx *txJSON) yParityValue() (*big.Int, error) { diff --git a/core/types/tx_blob_test.go b/core/types/tx_blob_test.go index 3b368456a4..6e1f0c5c66 100644 --- a/core/types/tx_blob_test.go +++ b/core/types/tx_blob_test.go @@ -18,6 +18,7 @@ package types import ( "crypto/ecdsa" + "encoding/json" "testing" "github.com/ethereum/go-ethereum/common" @@ -74,6 +75,51 @@ func TestBlobTxSize(t *testing.T) { } } +func TestBlobTxJSONRejectsSidecar(t *testing.T) { + key, _ := crypto.GenerateKey() + withBlobs := createEmptyBlobTx(key, true) + withBlobsJSON, err := withBlobs.MarshalJSON() + if err != nil { + t.Fatal(err) + } + var decoded Transaction + if err := decoded.UnmarshalJSON(withBlobsJSON); err != errBlobTxSidecarInJSON { + t.Fatalf("wrong error: got %v, want %v", err, errBlobTxSidecarInJSON) + } + + withoutBlobsJSON, err := withBlobs.WithoutBlobTxSidecar().MarshalJSON() + if err != nil { + t.Fatal(err) + } + if err := decoded.UnmarshalJSON(withoutBlobsJSON); err != nil { + t.Fatalf("failed to unmarshal tx without sidecar: %v", err) + } + + var blobtx map[string]any + if err := json.Unmarshal(withoutBlobsJSON, &blobtx); err != nil { + t.Fatal(err) + } + for _, tc := range []struct { + field string + value any + }{ + {field: "version", value: "0x0"}, + {field: "blobs", value: []any{}}, + {field: "commitments", value: []any{}}, + {field: "proofs", value: []any{}}, + } { + blobtx[tc.field] = tc.value + payload, err := json.Marshal(blobtx) + if err != nil { + t.Fatal(err) + } + if err := decoded.UnmarshalJSON(payload); err != errBlobTxSidecarInJSON { + t.Fatalf("field %q: wrong error: got %v, want %v", tc.field, err, errBlobTxSidecarInJSON) + } + delete(blobtx, tc.field) + } +} + var ( emptyBlob = new(kzg4844.Blob) emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)