Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a blob verification utility #1066

Merged
merged 20 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 16 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
74 changes: 74 additions & 0 deletions api/clients/v2/verification/blob_verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package verification

import (
"context"
"fmt"

"github.com/Layr-Labs/eigenda/common"

disperser "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
verifierBindings "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDABlobVerifier"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
gethcommon "github.com/ethereum/go-ethereum/common"
)

// BlobVerifier is responsible for making eth calls against the BlobVerifier contract to ensure cryptographic and
samlaf marked this conversation as resolved.
Show resolved Hide resolved
// structural integrity of V2 certificates
type BlobVerifier struct {
// go binding around the EigenDABlobVerifier ethereum contract
blobVerifierCaller *verifierBindings.ContractEigenDABlobVerifierCaller
}

// NewBlobVerifier constructs a BlobVerifier
func NewBlobVerifier(
ethClient *common.EthClient, // the eth client, which should already be set up
blobVerifierAddress string, // the hex address of the EigenDABlobVerifier contract
) (*BlobVerifier, error) {

verifierCaller, err := verifierBindings.NewContractEigenDABlobVerifierCaller(
gethcommon.HexToAddress(blobVerifierAddress),
*ethClient)

if err != nil {
return nil, fmt.Errorf("bind to verifier contract at %s: %s", blobVerifierAddress, err)
}

return &BlobVerifier{
blobVerifierCaller: verifierCaller,
}, nil
}

// VerifyBlobV2FromSignedBatch calls the verifyBlobV2FromSignedBatch view function on the EigenDABlobVerifier contract
//
// This method returns nil if the blob is successfully verified. Otherwise, it returns an error.
samlaf marked this conversation as resolved.
Show resolved Hide resolved
//
// It is the responsibility of the caller to configure a timeout on the ctx, if a timeout is required.
func (v *BlobVerifier) VerifyBlobV2FromSignedBatch(
ctx context.Context,
// The signed batch that contains the blob being verified. This is obtained from the disperser, and is used
// to verify that the described blob actually exists in a valid batch.
signedBatch *disperser.SignedBatch,
// Contains all necessary information about the blob, so that it can be verified.
blobVerificationProof *disperser.BlobVerificationInfo,
) error {
convertedSignedBatch, err := verifierBindings.ConvertSignedBatch(signedBatch)
if err != nil {
return fmt.Errorf("convert signed batch: %s", err)
}

convertedBlobVerificationProof, err := verifierBindings.ConvertVerificationProof(blobVerificationProof)
if err != nil {
return fmt.Errorf("convert blob verification proof: %s", err)
}

err = v.blobVerifierCaller.VerifyBlobV2FromSignedBatch(
&bind.CallOpts{Context: ctx},
epociask marked this conversation as resolved.
Show resolved Hide resolved
*convertedSignedBatch,
*convertedBlobVerificationProof)

if err != nil {
return fmt.Errorf("verify blob v2 from signed batch: %s", err)
}

return nil
}
229 changes: 229 additions & 0 deletions contracts/bindings/EigenDABlobVerifier/conversion_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package contractEigenDABlobVerifier

import (
"fmt"
"math"
"math/big"

"github.com/Layr-Labs/eigenda/api/grpc/common"
commonv2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
disperserv2 "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
"github.com/Layr-Labs/eigenda/core"
"github.com/consensys/gnark-crypto/ecc/bn254"
)

func ConvertSignedBatch(inputBatch *disperserv2.SignedBatch) (*SignedBatch, error) {
convertedBatchHeader, err := convertBatchHeader(inputBatch.GetHeader())
if err != nil {
return nil, fmt.Errorf("convert batch header: %s", err)
}

convertedAttestation, err := convertAttestation(inputBatch.GetAttestation())
if err != nil {
return nil, fmt.Errorf("convert attestation: %s", err)
}

outputSignedBatch := &SignedBatch{
BatchHeader: *convertedBatchHeader,
Attestation: *convertedAttestation,
}

return outputSignedBatch, nil
}

func convertBatchHeader(inputHeader *commonv2.BatchHeader) (*BatchHeaderV2, error) {
var outputBatchRoot [32]byte

inputBatchRoot := inputHeader.GetBatchRoot()
if len(inputBatchRoot) != 32 {
return nil, fmt.Errorf("BatchRoot must be 32 bytes (length was %d)", len(inputBatchRoot))
}
copy(outputBatchRoot[:], inputBatchRoot[:])

inputReferenceBlockNumber := inputHeader.GetReferenceBlockNumber()
if inputReferenceBlockNumber > math.MaxUint32 {
return nil, fmt.Errorf(
"ReferenceBlockNumber overflow: value was %d, but max allowable value is %d",
inputReferenceBlockNumber,
math.MaxUint32)
}

convertedHeader := &BatchHeaderV2{
BatchRoot: outputBatchRoot,
ReferenceBlockNumber: uint32(inputReferenceBlockNumber),
}

return convertedHeader, nil
}

func convertAttestation(inputAttestation *disperserv2.Attestation) (*Attestation, error) {
nonSignerPubkeys, err := repeatedBytesToG1Points(inputAttestation.GetNonSignerPubkeys())
if err != nil {
return nil, fmt.Errorf("convert non signer pubkeys to g1 points: %s", err)
}

quorumApks, err := repeatedBytesToG1Points(inputAttestation.GetQuorumApks())
if err != nil {
return nil, fmt.Errorf("convert quorum apks to g1 points: %s", err)
}

sigma, err := bytesToBN254G1Point(inputAttestation.GetSigma())
if err != nil {
return nil, fmt.Errorf("convert sigma to g1 point: %s", err)
}

apkG2, err := bytesToBN254G2Point(inputAttestation.GetApkG2())
if err != nil {
return nil, fmt.Errorf("convert apk g2 to g2 point: %s", err)
}

convertedAttestation := &Attestation{
NonSignerPubkeys: nonSignerPubkeys,
QuorumApks: quorumApks,
Sigma: *sigma,
ApkG2: *apkG2,
QuorumNumbers: inputAttestation.GetQuorumNumbers(),
}

return convertedAttestation, nil
}

func ConvertVerificationProof(inputVerificationInfo *disperserv2.BlobVerificationInfo) (*BlobVerificationProofV2, error) {
convertedBlobCertificate, err := convertBlobCertificate(inputVerificationInfo.GetBlobCertificate())

if err != nil {
return nil, fmt.Errorf("convert blob certificate: %s", err)
}

return &BlobVerificationProofV2{
BlobCertificate: *convertedBlobCertificate,
BlobIndex: inputVerificationInfo.GetBlobIndex(),
InclusionProof: inputVerificationInfo.GetInclusionProof(),
}, nil
}

func convertBlobCertificate(inputCertificate *commonv2.BlobCertificate) (*BlobCertificate, error) {
convertedBlobHeader, err := convertBlobHeader(inputCertificate.GetBlobHeader())
if err != nil {
return nil, fmt.Errorf("convert blob header: %s", err)
}

return &BlobCertificate{
BlobHeader: *convertedBlobHeader,
RelayKeys: inputCertificate.GetRelays(),
}, nil
}

func convertBlobHeader(inputHeader *commonv2.BlobHeader) (*BlobHeaderV2, error) {
inputVersion := inputHeader.GetVersion()
if inputVersion > math.MaxUint16 {
return nil, fmt.Errorf(
"version overflow: value was %d, but max allowable value is %d",
inputVersion,
math.MaxUint16)
}

var quorumNumbers []byte
for _, quorumNumber := range inputHeader.GetQuorumNumbers() {
if quorumNumber > math.MaxUint8 {
return nil, fmt.Errorf(
"quorum number overflow: value was %d, but max allowable value is %d",
quorumNumber,
uint8(math.MaxUint8))
}

quorumNumbers = append(quorumNumbers, byte(quorumNumber))
}

convertedBlobCommitment, err := convertBlobCommitment(inputHeader.GetCommitment())
if err != nil {
return nil, fmt.Errorf("convert blob commitment: %s", err)
}

paymentHeaderHash, err := core.ConvertToPaymentMetadata(inputHeader.GetPaymentHeader()).Hash()
if err != nil {
return nil, fmt.Errorf("hash payment header: %s", err)
}

return &BlobHeaderV2{
Version: uint16(inputVersion),
QuorumNumbers: quorumNumbers,
Commitment: *convertedBlobCommitment,
PaymentHeaderHash: paymentHeaderHash,
}, nil
}

func convertBlobCommitment(inputCommitment *common.BlobCommitment) (*BlobCommitment, error) {
convertedCommitment, err := bytesToBN254G1Point(inputCommitment.GetCommitment())
if err != nil {
return nil, fmt.Errorf("convert commitment to g1 point: %s", err)
}

convertedLengthCommitment, err := bytesToBN254G2Point(inputCommitment.GetLengthCommitment())
if err != nil {
return nil, fmt.Errorf("convert length commitment to g2 point: %s", err)
}

convertedLengthProof, err := bytesToBN254G2Point(inputCommitment.GetLengthProof())
if err != nil {
return nil, fmt.Errorf("convert length proof to g2 point: %s", err)
}

return &BlobCommitment{
Commitment: *convertedCommitment,
LengthCommitment: *convertedLengthCommitment,
LengthProof: *convertedLengthProof,
DataLength: inputCommitment.GetLength(),
}, nil
}

func bytesToBN254G1Point(bytes []byte) (*BN254G1Point, error) {
var g1Point bn254.G1Affine
_, err := g1Point.SetBytes(bytes)

if err != nil {
return nil, fmt.Errorf("deserialize g1 point: %s", err)
}

return &BN254G1Point{
X: g1Point.X.BigInt(new(big.Int)),
Y: g1Point.Y.BigInt(new(big.Int)),
}, nil
}

func bytesToBN254G2Point(bytes []byte) (*BN254G2Point, error) {
var g2Point bn254.G2Affine

// SetBytes checks that the result is in the correct subgroup
_, err := g2Point.SetBytes(bytes)
bxue-l2 marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return nil, fmt.Errorf("deserialize g2 point: %s", err)
}

var x, y [2]*big.Int
x[0] = g2Point.X.A0.BigInt(new(big.Int))
litt3 marked this conversation as resolved.
Show resolved Hide resolved
x[1] = g2Point.X.A1.BigInt(new(big.Int))

y[0] = g2Point.Y.A0.BigInt(new(big.Int))
y[1] = g2Point.Y.A1.BigInt(new(big.Int))

return &BN254G2Point{
X: x,
Y: y,
}, nil
}

func repeatedBytesToG1Points(repeatedBytes [][]byte) ([]BN254G1Point, error) {
var outputPoints []BN254G1Point
for _, bytes := range repeatedBytes {
g1Point, err := bytesToBN254G1Point(bytes)
if err != nil {
return nil, fmt.Errorf("deserialize g1 point: %s", err)
}

outputPoints = append(outputPoints, *g1Point)
}

return outputPoints, nil
}
Loading