diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 9034e7e9ca0..19323246a75 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -108,6 +108,7 @@ type SendTxArgs struct { BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"` // For BlobTxType transactions with blob sidecar + BlobVersion byte `json:"blobVersion,omitempty"` Blobs []kzg4844.Blob `json:"blobs,omitempty"` Commitments []kzg4844.Commitment `json:"commitments,omitempty"` Proofs []kzg4844.Proof `json:"proofs,omitempty"` @@ -235,37 +236,56 @@ func (args *SendTxArgs) validateTxSidecar() error { 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.BlobHashes != nil && len(args.BlobHashes) != n { return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n) } - + if args.Proofs != nil { + if len(args.Proofs) == n { + // v1 transaction + 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) + } + } + } else if len(args.Proofs) == n*kzg4844.CellProofsPerBlob { + // v2 transaction + if err := kzg4844.VerifyCellProofs(args.Blobs, args.Commitments, args.Proofs); err != nil { + return fmt.Errorf("failed to verify blob proof: %v", err) + } + } else { + return fmt.Errorf("number of proofs and blobs mismatch (have=%d, want=%d or %d)", len(args.Proofs), n, n*kzg4844.CellProofsPerBlob) + } + } if args.Commitments == nil { // Generate commitment and proof. commitments := make([]kzg4844.Commitment, n) - proofs := make([]kzg4844.Proof, n) + proofs := make([]kzg4844.Proof, 0, n) for i, b := range args.Blobs { c, err := kzg4844.BlobToCommitment(&b) if err != nil { return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err) } commitments[i] = c - p, err := kzg4844.ComputeBlobProof(&b, c) - if err != nil { - return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err) + } + if args.BlobVersion == 1 { + for i, b := range args.Blobs { + p, err := kzg4844.ComputeCellProofs(&b) + if err != nil { + return fmt.Errorf("blobs[%d]: error computing cell proof: %v", i, err) + } + proofs = append(proofs, p...) + } + } else { + for i, b := range args.Blobs { + p, err := kzg4844.ComputeBlobProof(&b, commitments[i]) + if err != nil { + return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err) + } + proofs = append(proofs, p) } - proofs[i] = p } 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) - } - } } hashes := make([]common.Hash, n) diff --git a/signer/core/apitypes/types_test.go b/signer/core/apitypes/types_test.go index ab9d1b22d85..3e8f3bd43b6 100644 --- a/signer/core/apitypes/types_test.go +++ b/signer/core/apitypes/types_test.go @@ -17,8 +17,10 @@ package apitypes import ( + "crypto/rand" "crypto/sha256" "encoding/json" + "fmt" "testing" "github.com/ethereum/go-ethereum/common" @@ -229,3 +231,173 @@ func TestType_TypeName(t *testing.T) { } } } + +func TestValidateTxSidecar(t *testing.T) { + t.Parallel() + + // Helper function to create a test blob and its commitment/proof + createTestBlob := func() (kzg4844.Blob, kzg4844.Commitment, kzg4844.Proof, common.Hash) { + b := make([]byte, 31) + rand.Read(b) + var blob kzg4844.Blob + for i := range b { + blob[i+1] = b[i] + } + commitment, err := kzg4844.BlobToCommitment(&blob) + if err != nil { + t.Fatal(err) + } + proof, err := kzg4844.ComputeBlobProof(&blob, commitment) + if err != nil { + t.Fatal(err) + } + hash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment) + return blob, commitment, proof, hash + } + + // Helper function to create test cell proofs for v1 transactions + createTestCellProofs := func(blob kzg4844.Blob) []kzg4844.Proof { + cellProofs, err := kzg4844.ComputeCellProofs(&blob) + if err != nil { + t.Fatal(err) + } + return cellProofs + } + + blob1, commitment1, proof1, hash1 := createTestBlob() + blob2, commitment2, proof2, hash2 := createTestBlob() + + tests := []struct { + name string + args SendTxArgs + wantErr bool + }{ + { + name: "no blobs - should pass", + args: SendTxArgs{}, + wantErr: false, + }, + { + name: "valid blobs with commitments and proofs", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1, blob2}, + Commitments: []kzg4844.Commitment{commitment1, commitment2}, + Proofs: []kzg4844.Proof{proof1, proof2}, + BlobHashes: []common.Hash{hash1, hash2}, + }, + wantErr: false, + }, + { + name: "valid blobs without commitments/proofs - should generate them", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + }, + wantErr: false, + }, + { + name: "valid blobs with v1 cell proofs", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + Commitments: []kzg4844.Commitment{commitment1}, + Proofs: createTestCellProofs(blob1), + BlobHashes: []common.Hash{hash1}, + }, + wantErr: false, + }, + { + name: "blobs with v1 version flag - should generate cell proofs", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + BlobVersion: 1, + }, + wantErr: false, + }, + { + name: "proofs provided but commitments not", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + Proofs: []kzg4844.Proof{proof1}, + }, + wantErr: true, + }, + { + name: "commitments provided but proofs not", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + Commitments: []kzg4844.Commitment{commitment1}, + }, + wantErr: true, + }, + { + name: "mismatch between blobs and commitments", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1, blob2}, + Commitments: []kzg4844.Commitment{commitment1}, // Only one commitment for two blobs + Proofs: []kzg4844.Proof{proof1}, + }, + wantErr: true, + }, + { + name: "mismatch between blobs and hashes", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1, blob2}, + Commitments: []kzg4844.Commitment{commitment1, commitment2}, + Proofs: []kzg4844.Proof{proof1, proof2}, + BlobHashes: []common.Hash{hash1}, // Only one hash for two blobs + }, + wantErr: true, + }, + { + name: "wrong number of proofs", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1, blob2}, + Commitments: []kzg4844.Commitment{commitment1, commitment2}, + Proofs: []kzg4844.Proof{proof1, proof2, proof1}, // 3 proofs for 2 blobs + }, + wantErr: true, + }, + { + name: "invalid blob hash", + args: SendTxArgs{ + Blobs: []kzg4844.Blob{blob1}, + Commitments: []kzg4844.Commitment{commitment1}, + Proofs: []kzg4844.Proof{proof1}, + BlobHashes: []common.Hash{hash2}, // Wrong hash + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Make a copy to avoid modifying the original test case + args := tt.args + err := args.validateTxSidecar() + + if tt.wantErr { + if err == nil { + t.Errorf("validateTxSidecar() expected error but got none") + return + } + } else { + if err != nil { + t.Errorf("validateTxSidecar() unexpected error = %v", err) + } + + // For successful cases, verify that commitments and proofs were generated if they weren't provided + if len(args.Blobs) > 0 { + if args.Commitments == nil || len(args.Commitments) != len(args.Blobs) { + t.Errorf("validateTxSidecar() should have generated commitments") + } + if args.Proofs == nil || (len(args.Proofs) != len(args.Blobs) && len(args.Proofs) != len(args.Blobs)*kzg4844.CellProofsPerBlob) { + fmt.Println("proofs", args.Proofs) + t.Errorf("validateTxSidecar() should have generated proofs") + } + if args.BlobHashes == nil || len(args.BlobHashes) != len(args.Blobs) { + t.Errorf("validateTxSidecar() should have generated blob hashes") + } + } + } + }) + } +}