From 43f6bee34b62ee7a11fe51fe5c91ad1832fd4663 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 30 Nov 2023 00:21:26 +0000 Subject: [PATCH] working batch verification --- core/data.go | 7 + core/encoding.go | 3 + core/encoding/encoder.go | 46 +++++++ core/encoding/mock_encoder.go | 6 + core/validator.go | 105 ++++++++++++++ node/node.go | 20 +-- pkg/encoding/kzgEncoder/multiframe.go | 191 ++++++++++++++++++++++++++ 7 files changed, 359 insertions(+), 19 deletions(-) create mode 100644 pkg/encoding/kzgEncoder/multiframe.go diff --git a/core/data.go b/core/data.go index 2bcac82bfc..6017722d6c 100644 --- a/core/data.go +++ b/core/data.go @@ -160,3 +160,10 @@ func (cb Bundles) Serialize() ([][][]byte, error) { } return data, nil } + +type Sample struct { + Commitment *Commitment + Chunk *Chunk // contain proof and coeffs + EvalIndex ChunkNumber + BlobIndex int +} diff --git a/core/encoding.go b/core/encoding.go index ea1b07eaeb..089e007e7b 100644 --- a/core/encoding.go +++ b/core/encoding.go @@ -38,6 +38,9 @@ type Encoder interface { // VerifyChunks takes in the chunks, indices, commitments, and encoding parameters and returns an error if the chunks are invalid. VerifyChunks(chunks []*Chunk, indices []ChunkNumber, commitments BlobCommitments, params EncodingParams) error + // VerifyBatch takes in + UniversalVerifyChunks(params EncodingParams, samples []Sample, numBlobs int) error + // VerifyBlobLength takes in the commitments and returns an error if the blob length is invalid. VerifyBlobLength(commitments BlobCommitments) error diff --git a/core/encoding/encoder.go b/core/encoding/encoder.go index d5cad8fb3f..98bf29b868 100644 --- a/core/encoding/encoder.go +++ b/core/encoding/encoder.go @@ -2,6 +2,9 @@ package encoding import ( "crypto/sha256" + "errors" + "fmt" + "log" "github.com/Layr-Labs/eigenda/core" "github.com/Layr-Labs/eigenda/pkg/encoding/encoder" @@ -60,6 +63,7 @@ func (e *Encoder) Encode(data []byte, params core.EncodingParams) (core.BlobComm } } encParams := toEncParams(params) + fmt.Println("encParams", encParams) enc, err := e.EncoderGroup.GetKzgEncoder(encParams) if err != nil { @@ -78,6 +82,24 @@ func (e *Encoder) Encode(data []byte, params core.EncodingParams) (core.BlobComm Coeffs: frame.Coeffs, Proof: frame.Proof, } + + q, _ := encoder.GetLeadingCosetIndex(uint64(ind), uint64(len(chunks))) + lc := enc.Fs.ExpandedRootsOfUnity[uint64(q)] + ok := frame.Verify(enc.Ks, commit, &lc) + if !ok { + log.Fatalf("Proof %v failed\n", ind) + } else { + + fmt.Println("proof", frame.Proof.String()) + fmt.Println("commitment", commit.String()) + for i := 0; i < len(frame.Coeffs); i++ { + fmt.Printf("%v ", frame.Coeffs[i].String()) + } + fmt.Println("q", q, lc.String()) + + fmt.Println("***************tested frame and pass") + } + } length := uint(len(encoder.ToFrArray(data))) @@ -131,6 +153,30 @@ func (e *Encoder) VerifyChunks(chunks []*core.Chunk, indices []core.ChunkNumber, } +// convert struct understandable by the crypto library +func (e *Encoder) UniversalVerifyChunks(params core.EncodingParams, samplesCore []core.Sample, numBlobs int) error { + encParams := toEncParams(params) + + samples := make([]kzgEncoder.Sample, len(samplesCore)) + + for i, sc := range samplesCore { + sample := kzgEncoder.Sample{ + Commitment: *sc.Commitment.G1Point, + Proof: sc.Chunk.Proof, + Row: sc.BlobIndex, + Coeffs: sc.Chunk.Coeffs, + X: sc.EvalIndex, + } + samples[i] = sample + } + + if e.EncoderGroup.UniversalVerify(encParams, samples, numBlobs) { + return nil + } else { + return errors.New("Universal Verify wrong") + } +} + // Decode takes in the chunks, indices, and encoding parameters and returns the decoded blob // The result is trimmed to the given maxInputSize. func (e *Encoder) Decode(chunks []*core.Chunk, indices []core.ChunkNumber, params core.EncodingParams, maxInputSize uint64) ([]byte, error) { diff --git a/core/encoding/mock_encoder.go b/core/encoding/mock_encoder.go index 4dc1d4f1ea..be36b30a26 100644 --- a/core/encoding/mock_encoder.go +++ b/core/encoding/mock_encoder.go @@ -27,6 +27,12 @@ func (e *MockEncoder) VerifyChunks(chunks []*core.Chunk, indices []core.ChunkNum return args.Error(0) } +func (e *MockEncoder) UniversalVerifyChunks(params core.EncodingParams, samples []core.Sample, numBlobs int) error { + args := e.Called(params, samples, numBlobs) + time.Sleep(e.Delay) + return args.Error(0) +} + func (e *MockEncoder) VerifyBlobLength(commitments core.BlobCommitments) error { args := e.Called(commitments) diff --git a/core/validator.go b/core/validator.go index e5ebc83bbe..dcb5ddd1a9 100644 --- a/core/validator.go +++ b/core/validator.go @@ -2,6 +2,7 @@ package core import ( "errors" + "fmt" ) var ( @@ -10,6 +11,7 @@ var ( ) type ChunkValidator interface { + ValidateBatch([]*BlobMessage, *OperatorState) error ValidateBlob(*BlobMessage, *OperatorState) error UpdateOperatorID(OperatorID) } @@ -114,3 +116,106 @@ func (v *chunkValidator) ValidateBlob(blob *BlobMessage, operatorState *Operator func (v *chunkValidator) UpdateOperatorID(operatorID OperatorID) { v.operatorID = operatorID } + +func (v *chunkValidator) ValidateBatch(blobs []*BlobMessage, operatorState *OperatorState) error { + + batchGroup := make(map[EncodingParams][]Sample) + numBlobMap := make(map[EncodingParams]int) + + for i, blob := range blobs { + if len(blob.Bundles) != len(blob.BlobHeader.QuorumInfos) { + return errors.New("number of bundles does not match number of quorums") + } + + // Validate the blob length + err := v.encoder.VerifyBlobLength(blob.BlobHeader.BlobCommitments) + if err != nil { + return err + } + // for each quorum + for _, quorumHeader := range blob.BlobHeader.QuorumInfos { + // Check if the operator is a member of the quorum + if _, ok := operatorState.Operators[quorumHeader.QuorumID]; !ok { + continue + } + + // Get the assignments for the quorum + assignment, info, err := v.assignment.GetOperatorAssignment( + operatorState, + quorumHeader.QuorumID, + quorumHeader.QuantizationFactor, + v.operatorID, + ) + if err != nil { + return err + } + + // Validate the number of chunks + if assignment.NumChunks == 0 { + continue + } + if assignment.NumChunks != uint(len(blob.Bundles[quorumHeader.QuorumID])) { + return errors.New("number of chunks does not match assignment") + } + + chunkLength, err := v.assignment.GetChunkLengthFromHeader(operatorState, quorumHeader) + if err != nil { + return err + } + + // Get the chunk length + chunks := blob.Bundles[quorumHeader.QuorumID] + for _, chunk := range chunks { + if uint(chunk.Length()) != chunkLength { + return ErrChunkLengthMismatch + } + } + + // Validate the chunk length + numOperators := uint(len(operatorState.Operators[quorumHeader.QuorumID])) + if chunkLength*quorumHeader.QuantizationFactor*numOperators != quorumHeader.EncodedBlobLength { + return ErrInvalidHeader + } + + // Get Encoding Params + params := EncodingParams{ChunkLength: chunkLength, NumChunks: info.TotalChunks} + + // ToDo add a struct + _, ok := batchGroup[params] + if !ok { + batchGroup[params] = make([]Sample, 0) + numBlobMap[params] = 1 + } else { + numBlobMap[params] += 1 + } + + // Check the received chunks against the commitment + indices := assignment.GetIndices() + fmt.Println("indices", indices) + samples := make([]Sample, 0) + for ind := range chunks { + sample := Sample{ + Commitment: blob.BlobHeader.BlobCommitments.Commitment, + Chunk: chunks[ind], + EvalIndex: uint(indices[ind]), + BlobIndex: i, + } + samples = append(samples, sample) + } + batchGroup[params] = append(batchGroup[params], samples...) + } + } + + // ToDo parallelize + fmt.Println("num batchGroup", len(batchGroup)) + for params, samples := range batchGroup { + numBlobs, _ := numBlobMap[params] + err := v.encoder.UniversalVerifyChunks(params, samples, numBlobs) + if err != nil { + return err + } + } + + return nil + +} diff --git a/node/node.go b/node/node.go index a43037bb70..654c21a61e 100644 --- a/node/node.go +++ b/node/node.go @@ -25,7 +25,6 @@ import ( "github.com/Layr-Labs/eigensdk-go/nodeapi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/gammazero/workerpool" ) const ( @@ -322,24 +321,7 @@ func (n *Node) ValidateBatch(ctx context.Context, header *core.BatchHeader, blob return err } - pool := workerpool.New(n.Config.NumBatchValidators) - out := make(chan error, len(blobs)) - for _, blob := range blobs { - blob := blob - pool.Submit(func() { - n.validateBlob(ctx, blob, operatorState, out) - }) - } - - for i := 0; i < len(blobs); i++ { - err := <-out - if err != nil { - return err - } - } - - return nil - + return n.Validator.ValidateBatch(blobs, operatorState) } func (n *Node) updateSocketAddress(ctx context.Context, newSocketAddr string) { diff --git a/pkg/encoding/kzgEncoder/multiframe.go b/pkg/encoding/kzgEncoder/multiframe.go new file mode 100644 index 0000000000..ec82bc60b8 --- /dev/null +++ b/pkg/encoding/kzgEncoder/multiframe.go @@ -0,0 +1,191 @@ +package kzgEncoder + +import ( + "fmt" + "log" + + rs "github.com/Layr-Labs/eigenda/pkg/encoding/encoder" + kzg "github.com/Layr-Labs/eigenda/pkg/kzg" + bls "github.com/Layr-Labs/eigenda/pkg/kzg/bn254" +) + +type Sample struct { + Commitment bls.G1Point + Proof bls.G1Point + Row int + Coeffs []bls.Fr + X uint // X is int , at which index is evaluated +} + +// m is number of blob +func (group *KzgEncoderGroup) UniversalVerify(params rs.EncodingParams, samples []Sample, m int) bool { + verifier, _ := group.GetKzgVerifier(params) + ks := verifier.Ks + + for ind, s := range samples { + q, err := rs.GetLeadingCosetIndex( + uint64(s.X), + params.NumChunks, + ) + if err != nil { + return false + } + + lc := ks.FFTSettings.ExpandedRootsOfUnity[uint64(q)] + + ok := SingleVerify(ks, &s.Commitment, &lc, s.Coeffs, s.Proof) + if !ok { + fmt.Println("proof", s.Proof.String()) + fmt.Println("commitment", s.Commitment.String()) + + for i := 0; i < len(s.Coeffs); i++ { + fmt.Printf("%v ", s.Coeffs[i].String()) + } + fmt.Println("q", q, lc.String()) + + log.Fatalf("Proof %v failed\n", ind) + } else { + + fmt.Println("&&&&&&&&&&&&&&&&&&tested frame and pass", ind) + } + } + + D := len(samples[0].Coeffs) // chunkLen + + n := len(samples) + + rInt := uint64(22894) + var r bls.Fr + bls.AsFr(&r, rInt) + + randomsFr := make([]bls.Fr, n) + bls.AsFr(&randomsFr[0], rInt) + + // lhs + var tmp bls.Fr + + // power of r + for j := 0; j < n-1; j++ { + bls.MulModFr(&randomsFr[j+1], &randomsFr[j], &r) + } + + // array of proofs + proofs := make([]bls.G1Point, n) + for i := 0; i < n; i++ { + bls.CopyG1(&proofs[i], &samples[i].Proof) + } + + fmt.Printf("len proof %v len ran %v\n", len(proofs), len(randomsFr)) + // lhs g1 + lhsG1 := bls.LinCombG1(proofs, randomsFr) + + // lhs g2 + lhsG2 := &ks.Srs.G2[D] + + // rhs g2 + rhsG2 := &bls.GenG2 + + // rhs g1 + // get commitments + commits := make([]bls.G1Point, m) + //for k := 0 ; k < n ; k++ { + // commits[k] = samples[k].Commitment + //} + // get coeffs + ftCoeffs := make([]bls.Fr, m) + for k := 0; k < n; k++ { + s := samples[k] + row := s.Row + bls.AddModFr(&ftCoeffs[row], &ftCoeffs[row], &randomsFr[k]) + bls.CopyG1(&commits[row], &s.Commitment) + } + fmt.Printf("len commit %v len coeff %v\n", len(commits), len(ftCoeffs)) + + ftG1 := bls.LinCombG1(commits, ftCoeffs) + + // second term + stCoeffs := make([]bls.Fr, D) + for k := 0; k < n; k++ { + coeffs := samples[k].Coeffs + + rk := randomsFr[k] + for j := 0; j < D; j++ { + bls.MulModFr(&tmp, &coeffs[j], &rk) + bls.AddModFr(&stCoeffs[j], &stCoeffs[j], &tmp) + } + } + stG1 := bls.LinCombG1(ks.Srs.G1[:D], stCoeffs) + + // third term + ttCoeffs := make([]bls.Fr, n) + + // get leading coset powers + leadingDs := make([]bls.Fr, n) + + for k := 0; k < n; k++ { + x, err := rs.GetLeadingCosetIndex( + uint64(samples[k].X), + params.NumChunks, + ) + if err != nil { + return false + } + + h := ks.ExpandedRootsOfUnity[x] + var hPow bls.Fr + bls.CopyFr(&hPow, &bls.ONE) + + for j := 0; j < D; j++ { + bls.MulModFr(&tmp, &hPow, &h) + bls.CopyFr(&hPow, &tmp) + } + bls.CopyFr(&leadingDs[k], &hPow) + } + + // + for k := 0; k < n; k++ { + rk := randomsFr[k] + bls.MulModFr(&ttCoeffs[k], &rk, &leadingDs[k]) + } + ttG1 := bls.LinCombG1(proofs, ttCoeffs) + + var rhsG1 bls.G1Point + bls.SubG1(&rhsG1, ftG1, stG1) + bls.AddG1(&rhsG1, &rhsG1, ttG1) + + return bls.PairingsVerify(lhsG1, lhsG2, &rhsG1, rhsG2) +} + +func SingleVerify(ks *kzg.KZGSettings, commitment *bls.G1Point, x *bls.Fr, coeffs []bls.Fr, proof bls.G1Point) bool { + var xPow bls.Fr + bls.CopyFr(&xPow, &bls.ONE) + + var tmp bls.Fr + for i := 0; i < len(coeffs); i++ { + bls.MulModFr(&tmp, &xPow, x) + bls.CopyFr(&xPow, &tmp) + } + + // [x^n]_2 + var xn2 bls.G2Point + bls.MulG2(&xn2, &bls.GenG2, &xPow) + + // [s^n - x^n]_2 + var xnMinusYn bls.G2Point + bls.SubG2(&xnMinusYn, &ks.Srs.G2[len(coeffs)], &xn2) + + // [interpolation_polynomial(s)]_1 + is1 := bls.LinCombG1(ks.Srs.G1[:len(coeffs)], coeffs) + // [commitment - interpolation_polynomial(s)]_1 = [commit]_1 - [interpolation_polynomial(s)]_1 + var commitMinusInterpolation bls.G1Point + bls.SubG1(&commitMinusInterpolation, commitment, is1) + + // Verify the pairing equation + // + // e([commitment - interpolation_polynomial(s)], [1]) = e([proof], [s^n - x^n]) + // equivalent to + // e([commitment - interpolation_polynomial]^(-1), [1]) * e([proof], [s^n - x^n]) = 1_T + // + + return bls.PairingsVerify(&commitMinusInterpolation, &bls.GenG2, &proof, &xnMinusYn) +}