diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 9034e7e9ca..e18c121107 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -231,12 +231,13 @@ func (args *SendTxArgs) validateTxSidecar() error { } // len(blobs) == len(commitments) == len(proofs) == len(hashes) + // Note: proofs can be either 1:1 (Version 0) or 128:1 cell proofs (Version 1) n := len(args.Blobs) if args.Commitments != nil && len(args.Commitments) != n { return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n) } - if args.Proofs != nil && len(args.Proofs) != n { - return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n) + if args.Proofs != nil && len(args.Proofs) != n && len(args.Proofs) != n*kzg4844.CellProofsPerBlob { + return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d or %d)", len(args.Proofs), n, n*kzg4844.CellProofsPerBlob) } if args.BlobHashes != nil && len(args.BlobHashes) != n { return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n) @@ -261,9 +262,18 @@ func (args *SendTxArgs) validateTxSidecar() error { args.Commitments = commitments args.Proofs = proofs } else { - for i, b := range args.Blobs { - if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil { - return fmt.Errorf("failed to verify blob proof: %v", err) + // Verify proofs: either 1:1 blob proofs (Version 0) or 128:1 cell proofs (Version 1) + if len(args.Proofs) == n*kzg4844.CellProofsPerBlob { + // Version 1: Cell proofs - verify all at once + if err := kzg4844.VerifyCellProofs(args.Blobs, args.Commitments, args.Proofs); err != nil { + return fmt.Errorf("failed to verify cell proofs: %v", err) + } + } else { + // Version 0: Blob proofs - verify individually + for i, b := range args.Blobs { + if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil { + return fmt.Errorf("failed to verify blob proof: %v", err) + } } } } diff --git a/signer/core/apitypes/types_test.go b/signer/core/apitypes/types_test.go index ab9d1b22d8..e2343a6a57 100644 --- a/signer/core/apitypes/types_test.go +++ b/signer/core/apitypes/types_test.go @@ -19,9 +19,11 @@ package apitypes import ( "crypto/sha256" "encoding/json" + "math/big" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/holiman/uint256" @@ -139,6 +141,74 @@ func TestBlobTxs(t *testing.T) { t.Logf("tx %v", string(data)) } +func TestBlobTxsWithCellProofs(t *testing.T) { + blob := kzg4844.Blob{0x1} + commitment, err := kzg4844.BlobToCommitment(&blob) + if err != nil { + t.Fatal(err) + } + + // Generate cell proofs (128 proofs per blob) instead of a single blob proof + cellProofs, err := kzg4844.ComputeCellProofs(&blob) + if err != nil { + t.Fatal(err) + } + + // Verify we got the expected number of cell proofs + if len(cellProofs) != kzg4844.CellProofsPerBlob { + t.Fatalf("expected %d cell proofs, got %d", kzg4844.CellProofsPerBlob, len(cellProofs)) + } + + // Test that SendTxArgs accepts cell proofs and converts to Version 1 sidecar + hash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment) + args := &SendTxArgs{ + From: common.MixedcaseAddress{}, + To: &common.MixedcaseAddress{}, + Gas: 21000, + MaxFeePerGas: (*hexutil.Big)(big.NewInt(600)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(500)), + BlobFeeCap: (*hexutil.Big)(big.NewInt(700)), + Value: hexutil.Big(*big.NewInt(100)), + Nonce: 8, + ChainID: (*hexutil.Big)(big.NewInt(6)), + BlobHashes: []common.Hash{hash}, + Blobs: []kzg4844.Blob{blob}, + Commitments: []kzg4844.Commitment{commitment}, + Proofs: cellProofs, + } + + // This should succeed with the validation fix + tx, err := args.ToTransaction() + if err != nil { + t.Fatalf("failed to convert args with cell proofs to transaction: %v", err) + } + + // Verify the transaction type is BlobTx + if tx.Type() != types.BlobTxType { + t.Fatalf("expected BlobTxType, got %d", tx.Type()) + } + + // Verify the transaction has a Version 1 sidecar + sidecar := tx.BlobTxSidecar() + if sidecar == nil { + t.Fatal("expected sidecar to be present") + } + if sidecar.Version != types.BlobSidecarVersion1 { + t.Fatalf("expected sidecar version %d, got %d", types.BlobSidecarVersion1, sidecar.Version) + } + + // Verify the sidecar has the correct number of proofs + if len(sidecar.Proofs) != len(cellProofs) { + t.Fatalf("expected %d proofs in sidecar, got %d", len(cellProofs), len(sidecar.Proofs)) + } + + data, err := json.Marshal(tx) + if err != nil { + t.Fatal(err) + } + t.Logf("cell proof tx %v", string(data)) +} + func TestType_IsArray(t *testing.T) { t.Parallel() // Expected positives