Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions signer/core/apitypes/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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)
Expand Down
172 changes: 172 additions & 0 deletions signer/core/apitypes/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package apitypes

import (
"crypto/rand"
"crypto/sha256"
"encoding/json"
"fmt"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -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")
}
}
}
})
}
}