Files
linea-monorepo/prover/backend/blobsubmission/blobcompression_test.go
Arya Tabaie ce964fc0cb Decompressor Library (#229)
* chore bring over changes from other branch

* chore: blocks as byte arrays not lists; tx.S <- 1

* build: update compressor lib release script by jpnovais

* docs and go generate

---------

Co-authored-by: Arya Tabaie <15056835+Tabaie@users.noreply.github.com>
2024-10-22 14:32:14 -05:00

349 lines
9.5 KiB
Go

package blobsubmission
import (
"crypto/rand"
"crypto/sha256"
"encoding/json"
"fmt"
"os"
"strings"
"testing"
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/encode"
fr381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/consensys/linea-monorepo/prover/utils"
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/stretchr/testify/assert"
)
const (
_inFile = "./samples/sample0.json"
_outFile = "./samples/sample0-response.json"
_inFileEIP4844 = "./samples/sample1.json"
_outFileEIP4844 = "./samples/sample1-response.json"
_inFileEIP4844MaxSize = "./samples/sample-max-size.json"
_outFileEIP4844MaxSize = "./samples/sample-max-size-response.json"
_inFileEIP4844Empty = "./samples/sample-empty.json"
_outFileEIP4844Empty = "./samples/sample-empty-response.json"
_inFileEIP4844TooLarge = "./samples/sample-too-large.json"
)
// blobsubmission with callData
// eip4844Enabled=false
func TestBlobSubmission(t *testing.T) {
fIn, err := os.Open(_inFile)
if err != nil {
t.Fatalf("could not open %s: %v", _inFile, err)
}
defer fIn.Close()
fOut, err := os.Open(_outFile)
if err != nil {
t.Fatalf("could not open %s: %v", _outFile, err)
}
defer fOut.Close()
var (
inp Request
outExpected Response
)
if err = json.NewDecoder(fIn).Decode(&inp); err != nil {
t.Fatalf("could not decode %++v: %v", inp, err)
}
if err = json.NewDecoder(fOut).Decode(&outExpected); err != nil {
t.Fatalf("could not decode %++v: %v", outExpected, err)
}
// call CraftResponseCalldata()
out, err := CraftResponse(&inp)
ok := assert.NoErrorf(t, err, "could not craft the response: %v", err)
if ok {
assert.Equal(t, outExpected, *out, "the response file should be the same")
}
// Stop the test after the first failed file to not overwhelm the
// logs.
if t.Failed() {
t.Fatalf("Got errors for file %s, stopping the test", _outFile)
}
}
// eip4844 blob submission
// eip4844Enabled = true
func TestBlobSubmissionEIP4844(t *testing.T) {
fIn, err := os.Open(_inFileEIP4844)
if err != nil {
t.Fatalf("could not open %s: %v", _inFileEIP4844, err)
}
defer fIn.Close()
fOut, err := os.Open(_outFileEIP4844)
if err != nil {
t.Fatalf("could not open %s: %v", _outFileEIP4844, err)
}
defer fOut.Close()
var (
inp Request
outExpected Response
)
if err = json.NewDecoder(fIn).Decode(&inp); err != nil {
t.Fatalf("could not decode %++v: %v", inp, err)
}
if err = json.NewDecoder(fOut).Decode(&outExpected); err != nil {
t.Fatalf("could not decode %++v: %v", outExpected, err)
}
out, err := CraftResponse(&inp)
ok := assert.NoErrorf(t, err, "could not craft the response: %v", err)
if ok {
assert.Equal(t, outExpected, *out, "the response file should be the same")
}
// Stop the test after the first failed file to not overwhelm the
// logs.
if t.Failed() {
t.Fatalf("Got errors for file %s, stopping the test", _outFileEIP4844)
}
}
// empty blob
func TestBlobSubmissionEIP4844EmptyBlob(t *testing.T) {
fIn, err := os.Open(_inFileEIP4844Empty)
if err != nil {
t.Fatalf("could not open %s: %v", _inFileEIP4844Empty, err)
}
defer fIn.Close()
fOut, err := os.Open(_outFileEIP4844Empty)
if err != nil {
t.Fatalf("could not open %s: %v", _outFileEIP4844Empty, err)
}
defer fOut.Close()
var (
inp Request
outExpected Response
)
if err = json.NewDecoder(fIn).Decode(&inp); err != nil {
t.Fatalf("could not decode %++v: %v", inp, err)
}
if err = json.NewDecoder(fOut).Decode(&outExpected); err != nil {
t.Fatalf("could not decode %++v: %v", outExpected, err)
}
compressedStream, _ := b64.DecodeString(inp.CompressedData)
// Check if len(compressedStream) is equal to 0
expectedLength := 0
actualLength := len(compressedStream)
assert.Equal(t, expectedLength, actualLength, "compressed stream length mismatch")
out, err := CraftResponse(&inp)
ok := assert.NoErrorf(t, err, "could not craft the response: %v", err)
if ok {
assert.Equal(t, outExpected, *out, "the response file should be the same")
}
// Stop the test after the first failed file to not overwhelm the
// logs.
if t.Failed() {
t.Fatalf("Got errors for file %s, stopping the test", _outFileEIP4844)
}
}
// random blob with [131072] bytes
func TestBlobSubmissionEIP4844MaxSize(t *testing.T) {
fIn, err := os.Open(_inFileEIP4844MaxSize)
if err != nil {
t.Fatalf("could not open %s: %v", _inFileEIP4844MaxSize, err)
}
defer fIn.Close()
fOut, err := os.Open(_outFileEIP4844MaxSize)
if err != nil {
t.Fatalf("could not open %s: %v", _outFileEIP4844MaxSize, err)
}
defer fOut.Close()
var (
inp Request
outExpected Response
)
if err = json.NewDecoder(fIn).Decode(&inp); err != nil {
t.Fatalf("could not decode %++v: %v", inp, err)
}
if err = json.NewDecoder(fOut).Decode(&outExpected); err != nil {
t.Fatalf("could not decode %++v: %v", _outFileEIP4844MaxSize, err)
}
compressedStream, _ := b64.DecodeString(inp.CompressedData)
// Check if len(compressedStream) is equal to 131072
expectedLength := 131072
actualLength := len(compressedStream)
assert.Equal(t, expectedLength, actualLength, "compressed stream length mismatch")
out, err := CraftResponse(&inp)
ok := assert.NoErrorf(t, err, "could not craft the response: %v", err)
if ok {
assert.Equal(t, outExpected, *out, "the response file should be the same")
}
// Stop the test after the first failed file to not overwhelm the
// logs.
if t.Failed() {
t.Fatalf("Got errors for file %s, stopping the test", _outFileEIP4844MaxSize)
}
}
// blob too large error with [131072 + 32] bytes
func TestBlobSubmissionEIP4844BlobTooLarge(t *testing.T) {
var blob kzg4844.Blob
fIn, err := os.Open(_inFileEIP4844TooLarge)
if err != nil {
t.Fatalf("could not open %s: %v", _inFileEIP4844TooLarge, err)
}
defer fIn.Close()
var (
inp Request
)
if err = json.NewDecoder(fIn).Decode(&inp); err != nil {
t.Fatalf("could not decode %++v: %v", inp, err)
}
_, err = CraftResponse(&inp)
// Check if err is not nil
if err == nil {
t.Errorf("expected an error `compressedStream length exceeds blob length (131072)`, but got nil")
return
}
compressedStream, _ := b64.DecodeString(inp.CompressedData)
// Check if the error message contains the expected substring
expectedErrorMsg := fmt.Sprintf("compressedStream length (%d) exceeds blob length (%d)", len(compressedStream), len(blob))
if !strings.Contains(err.Error(), expectedErrorMsg) {
t.Errorf("expected error message to contain %q, got %q", expectedErrorMsg, err.Error())
}
}
// a random blob from kzg4844 package and xPoint from evaluationChallenge()
func TestKZGWithPoint(t *testing.T) {
blobBytes := randBlob()
// Also zeroize all the bytes that are at position multiples of 32. This
// ensures that we will not have overflow when casting to the bls12 scalar
// field.
n := len(blobBytes)
n = utils.DivCeil(n, 32) * 32 // round up to the next multiple of 32
for i := 0; i < n; i += 32 {
blobBytes[i] = 0
}
commitment, err := kzg4844.BlobToCommitment(&blobBytes)
if err != nil {
t.Fatalf("failed to create KZG commitment from blob: %v", err)
}
// blobHash
blobHash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment)
if !kzg4844.IsValidVersionedHash(blobHash[:]) {
t.Fatalf("crafting response: invalid versionedHash (blobHash, dataHash): %v", err)
}
// Compute all the prover fields
snarkHash, err := encode.MiMCChecksumPackedData(blobBytes[:], fr381.Bits-1, encode.NoTerminalSymbol())
assert.NoError(t, err)
xUnreduced := evaluationChallenge(snarkHash, blobHash[:])
var tmp fr381.Element
tmp.SetBytes(xUnreduced[:])
xPoint := kzg4844.Point(tmp.Bytes())
proof, claim, err := kzg4844.ComputeProof(&blobBytes, xPoint)
if err != nil {
t.Fatalf("failed to create KZG proof at point: %v", err)
}
if err := kzg4844.VerifyProof(commitment, xPoint, claim, proof); err != nil {
t.Fatalf("failed to verify KZG proof at point: %v", err)
}
}
// a random blob and xPoint from kzg4844 package
func TestKZGWithPointSimple(t *testing.T) {
blob := randBlob()
commitment, err := kzg4844.BlobToCommitment(&blob)
if err != nil {
t.Fatalf("failed to create KZG commitment from blob: %v", err)
}
xPoint := randFieldElement()
proof, claim, err := kzg4844.ComputeProof(&blob, xPoint)
if err != nil {
t.Fatalf("failed to create KZG proof at point: %v", err)
}
if err := kzg4844.VerifyProof(commitment, xPoint, claim, proof); err != nil {
t.Fatalf("failed to verify KZG proof at point: %v", err)
}
}
// simple kzg from kzg4844 package
func TestKZGWithBlob(t *testing.T) {
blob := randBlob()
commitment, err := kzg4844.BlobToCommitment(&blob)
if err != nil {
t.Fatalf("failed to create KZG commitment from blob: %v", err)
}
proof, err := kzg4844.ComputeBlobProof(&blob, commitment)
if err != nil {
t.Fatalf("failed to create KZG proof for blob: %v", err)
}
if err := kzg4844.VerifyBlobProof(&blob, commitment, proof); err != nil {
t.Fatalf("failed to verify KZG proof for blob: %v", err)
}
}
// randFieldElement() from kzg4844 package
func randFieldElement() [32]byte {
bytes := make([]byte, 32)
_, err := rand.Read(bytes)
if err != nil {
panic("failed to get random field element")
}
var r fr381.Element
r.SetBytes(bytes)
return gokzg4844.SerializeScalar(r)
}
// randBlob() from kzg4844 package
func randBlob() kzg4844.Blob {
var blob kzg4844.Blob
for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize {
fieldElementBytes := randFieldElement()
copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:])
}
return blob
}