-
Notifications
You must be signed in to change notification settings - Fork 194
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
base: master
Are you sure you want to change the base?
Changes from 14 commits
a0d7808
dcbcc07
2e99455
30bf639
90e3e43
7c6984e
ec56c39
b44b119
4db5acc
8777294
2e79dc6
ca8d9d7
c752e5b
257264c
f43c7ce
f31e4a6
66249bc
62d7476
8bba429
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
// 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}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. knit - is there any utility in allowing a configurable user timeout for simulation calls? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there's not a default timeout used by the binding wrapper then we may wanna instate given this could result in the connection infinitely hanging There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment clarifying that the caller is responsible for any timeout c752e5b1 |
||
*convertedSignedBatch, | ||
*convertedBlobVerificationProof) | ||
|
||
if err != nil { | ||
return fmt.Errorf("verify blob v2 from signed batch: %s", err) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
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 | ||
_, err := g2Point.SetBytes(bytes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add a comment to say, this makes sure bytes is in the subgroup There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done f31e4a6b |
||
|
||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Has this been tested? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add link to BlobVerifier contract?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean like this? I don't like that this sort of link is instantly out of date, but I don't know of a better way to link here
eigenda/contracts/src/core/EigenDABlobVerifier.sol
Line 108 in 06e88b3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes did mean like this. We can also just link to the contract itself, in the master branch, so that it doesn't get out of date instantly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 8bba429a