Skip to content

Commit

Permalink
chore: V2 abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
epociask committed Nov 15, 2024
1 parent f263439 commit c21b9f5
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 44 deletions.
43 changes: 33 additions & 10 deletions commitments/eigenda.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
package commitments

import "fmt"

type CertEncodingCommitment byte

const (
CertV0 CertEncodingCommitment = 0
CertV1 CertEncodingCommitment = 1
)

// OPCommitment is the binary representation of a commitment.
// CertCommitment is the binary representation of a commitment.
type CertCommitment interface {
CommitmentType() CertEncodingCommitment
Encode() []byte
Verify(input []byte) error
}

type CertCommitmentV0 []byte
type CertCommitmentV1 []byte

func NewCertCommitment(input []byte, version CertEncodingCommitment) (CertCommitment, error) {
switch version {
case CertV0:
return NewV0CertCommitment(input), nil

case CertV1:
return NewV1CertCommitment(input), nil

default:
return nil, fmt.Errorf("Invalid cert version provided")

Check failure on line 30 in commitments/eigenda.go

View workflow job for this annotation

GitHub Actions / Linter

ST1005: error strings should not be capitalized (stylecheck)
}
}

// NewV0CertCommitment creates a new commitment from the given input.
func NewV0CertCommitment(input []byte) CertCommitmentV0 {
return CertCommitmentV0(input)
}

// DecodeCertCommitment validates and casts the commitment into a Keccak256Commitment.
func DecodeCertCommitment(commitment []byte) (CertCommitmentV0, error) {
if len(commitment) == 0 {
return nil, ErrInvalidCommitment
}
return commitment, nil
}

// CommitmentType returns the commitment type of Keccak256.
func (c CertCommitmentV0) CommitmentType() CertEncodingCommitment {
return CertV0
Expand All @@ -37,3 +45,18 @@ func (c CertCommitmentV0) CommitmentType() CertEncodingCommitment {
func (c CertCommitmentV0) Encode() []byte {
return append([]byte{byte(CertV0)}, c...)
}

// NewV1CertCommitment creates a new commitment from the given input.
func NewV1CertCommitment(input []byte) CertCommitmentV1 {
return CertCommitmentV1(input)
}

// CommitmentType returns the commitment type of Keccak256.
func (c CertCommitmentV1) CommitmentType() CertEncodingCommitment {
return CertV0
}

// Encode adds a commitment type prefix self describing the commitment.
func (c CertCommitmentV1) Encode() []byte {
return append([]byte{byte(CertV0)}, c...)
}
16 changes: 12 additions & 4 deletions commitments/mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,27 @@ func StringToCommitmentMode(s string) (CommitmentMode, error) {
}
}

func EncodeCommitment(b []byte, c CommitmentMode) ([]byte, error) {
func EncodeCommitment(b []byte, c CommitmentMode, v CertEncodingCommitment) ([]byte, error) {
switch c {
case OptimismKeccak:
return Keccak256Commitment(b).Encode(), nil

case OptimismGeneric:
certCommit := NewV0CertCommitment(b).Encode()
svcCommit := EigenDASvcCommitment(certCommit).Encode()
certCommit, err := NewCertCommitment(b, v)
if err != nil {
return nil, err
}
svcCommit := EigenDASvcCommitment(certCommit.Encode()).Encode()
altDACommit := NewGenericCommitment(svcCommit).Encode()
return altDACommit, nil

case SimpleCommitmentMode:
return NewV0CertCommitment(b).Encode(), nil
certCommit, err := NewCertCommitment(b, v)
if err != nil {
return nil, err
}

return certCommit.Encode(), nil
}

return nil, fmt.Errorf("unknown commitment mode")
Expand Down
6 changes: 5 additions & 1 deletion server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,11 @@ func (svr *Server) handlePostShared(w http.ResponseWriter, r *http.Request, comm
return err
}

responseCommit, err := commitments.EncodeCommitment(commitment, meta.Mode)
// TODO: Pass in certificate version when encoding commitment. This will also be based on the
// "waitForSignatures" flag. Ideally the conception of V2 will be sufficiently obfuscated from the rollup user.
// I.e, triggering V2 mode should be done within proxy directly and not live as a hardcoded system cfg field within the rollup
// itself.
responseCommit, err := commitments.EncodeCommitment(commitment, meta.Mode, commitments.CertV0)
if err != nil {
err = MetaError{
Err: fmt.Errorf("failed to encode commitment %v (commitment mode %v): %w", commitment, meta.Mode, err),
Expand Down
8 changes: 6 additions & 2 deletions store/generated_key/eigenda/eigenda.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ func (e Store) Put(ctx context.Context, value []byte) ([]byte, error) {
return nil, fmt.Errorf("failed to verify commitment: %w", err)
}

err = e.verifier.VerifyCert(ctx, cert)
// TODO: Add an optional "waitForSigning" flag which denotes to use the V2
// dispersal flow and type switches for verification
err = e.verifier.VerifyBridgeCert(ctx, cert)
if err != nil {
return nil, fmt.Errorf("failed to verify DA cert: %w", err)
}
Expand Down Expand Up @@ -122,6 +124,8 @@ func (e Store) Verify(ctx context.Context, key []byte, value []byte) error {
return fmt.Errorf("failed to verify commitment: %w", err)
}

// TODO: Add an optional "waitForSigning" flag which denotes to use the V2
// dispersal flow and type switches for verification
// verify DA certificate against EigenDA's batch metadata that's bridged to Ethereum
return e.verifier.VerifyCert(ctx, &cert)
return e.verifier.VerifyBridgeCert(ctx, &cert)
}
2 changes: 1 addition & 1 deletion store/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (m *Manager) Put(ctx context.Context, cm commitments.CommitmentMode, key, v

// putEigenDAMode ... disperses blob to EigenDA backend
func (m *Manager) putEigenDAMode(ctx context.Context, value []byte) ([]byte, error) {
if m.eigenda != nil {
if m.eigenda != nil { // TODO: support two put functions (i.e, V0, V1 certs)
m.log.Debug("Storing data to EigenDA backend")
return m.eigenda.Put(ctx, value)
}
Expand Down
34 changes: 17 additions & 17 deletions verify/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ import (
"golang.org/x/exp/slices"
)

// CertVerifier verifies the DA certificate against on-chain EigenDA contracts
// BridgedCertVerifier verifies the DA certificate against on-chain EigenDA contracts
// to ensure disperser returned fields haven't been tampered with
type CertVerifier struct {
type BridgedCertVerifier struct {
l log.Logger
ethConfirmationDepth uint64
waitForFinalization bool
manager *binding.ContractEigenDAServiceManagerCaller
ethClient *ethclient.Client
}

func NewCertVerifier(cfg *Config, l log.Logger) (*CertVerifier, error) {
func NewBridgedCertVerifier(cfg *Config, l log.Logger) (*BridgedCertVerifier, error) {
log.Info("Enabling certificate verification", "confirmation_depth", cfg.EthConfirmationDepth)

client, err := ethclient.Dial(cfg.RPCURL)
Expand All @@ -43,7 +43,7 @@ func NewCertVerifier(cfg *Config, l log.Logger) (*CertVerifier, error) {
return nil, err
}

return &CertVerifier{
return &BridgedCertVerifier{
l: l,
manager: m,
ethConfirmationDepth: cfg.EthConfirmationDepth,
Expand All @@ -53,7 +53,7 @@ func NewCertVerifier(cfg *Config, l log.Logger) (*CertVerifier, error) {

// verifyBatchConfirmedOnChain verifies that batchMetadata (typically part of a received cert)
// matches the batch metadata hash stored on-chain
func (cv *CertVerifier) verifyBatchConfirmedOnChain(
func (bcv *BridgedCertVerifier) verifyBatchConfirmedOnChain(
ctx context.Context, batchID uint32, batchMetadata *disperser.BatchMetadata,
) error {
// 1. Verify batch is actually onchain at the batchMetadata's state confirmedBlockNumber.
Expand All @@ -65,7 +65,7 @@ func (cv *CertVerifier) verifyBatchConfirmedOnChain(
// but for now we opt to simply fail the verification, which will force the batcher to resubmit the batch to eigenda.
confirmationBlockNumber := batchMetadata.GetConfirmationBlockNumber()
confirmationBlockNumberBigInt := big.NewInt(0).SetInt64(int64(confirmationBlockNumber))
_, err := cv.retrieveBatchMetadataHash(ctx, batchID, confirmationBlockNumberBigInt)
_, err := bcv.retrieveBatchMetadataHash(ctx, batchID, confirmationBlockNumberBigInt)
if err != nil {
return fmt.Errorf("batch not found onchain at supposedly confirmed block %d: %w", confirmationBlockNumber, err)
}
Expand All @@ -78,11 +78,11 @@ func (cv *CertVerifier) verifyBatchConfirmedOnChain(
// We retry up to 60 seconds (allowing for reorgs up to 5 blocks deep), but we only wait 3 seconds between each retry,
// in case (2) is the case and the node simply needs to resync, which could happen fast.
onchainHash, err := retry.Do(ctx, 20, retry.Fixed(3*time.Second), func() ([32]byte, error) {
blockNumber, err := cv.getConfDeepBlockNumber(ctx)
blockNumber, err := bcv.getConfDeepBlockNumber(ctx)
if err != nil {
return [32]byte{}, fmt.Errorf("failed to get context block: %w", err)
}
return cv.retrieveBatchMetadataHash(ctx, batchID, blockNumber)
return bcv.retrieveBatchMetadataHash(ctx, batchID, blockNumber)
})
if err != nil {
return fmt.Errorf("retrieving batch that was confirmed at block %v: %w", confirmationBlockNumber, err)
Expand Down Expand Up @@ -111,7 +111,7 @@ func (cv *CertVerifier) verifyBatchConfirmedOnChain(
}

// verifies the blob batch inclusion proof against the blob root hash
func (cv *CertVerifier) verifyMerkleProof(inclusionProof []byte, root []byte,
func (bcv *BridgedCertVerifier) verifyMerkleProof(inclusionProof []byte, root []byte,
blobIndex uint32, blobHeader BlobHeader) error {
leafHash, err := HashEncodeBlobHeader(blobHeader)
if err != nil {
Expand All @@ -132,32 +132,32 @@ func (cv *CertVerifier) verifyMerkleProof(inclusionProof []byte, root []byte,
}

// fetches a block number provided a subtraction of a user defined conf depth from latest block
func (cv *CertVerifier) getConfDeepBlockNumber(ctx context.Context) (*big.Int, error) {
if cv.waitForFinalization {
func (bcv *BridgedCertVerifier) getConfDeepBlockNumber(ctx context.Context) (*big.Int, error) {
if bcv.waitForFinalization {
var header = types.Header{}
// We ask for the latest finalized block. The second parameter "hydrated txs" is set to false because we don't need full txs.
// See https://github.com/ethereum/execution-apis/blob/4140e528360fea53c34a766d86a000c6c039100e/src/eth/block.yaml#L61
// This is equivalent to `cast block finalized`, as opposed to `cast block finalized --full`.
err := cv.ethClient.Client().CallContext(ctx, &header, "eth_getBlockByNumber", "finalized", false)
err := bcv.ethClient.Client().CallContext(ctx, &header, "eth_getBlockByNumber", "finalized", false)
if err != nil {
return nil, fmt.Errorf("failed to get finalized block: %w", err)
}
return header.Number, nil
}
blockNumber, err := cv.ethClient.BlockNumber(ctx)
blockNumber, err := bcv.ethClient.BlockNumber(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get latest block number: %w", err)
}
if blockNumber < cv.ethConfirmationDepth {
if blockNumber < bcv.ethConfirmationDepth {
return big.NewInt(0), nil
}
return new(big.Int).SetUint64(blockNumber - cv.ethConfirmationDepth), nil
return new(big.Int).SetUint64(blockNumber - bcv.ethConfirmationDepth), nil
}

// retrieveBatchMetadataHash retrieves the batch metadata hash stored on-chain at a specific blockNumber for a given batchID
// returns an error if some problem calling the contract happens, or the hash is not found
func (cv *CertVerifier) retrieveBatchMetadataHash(ctx context.Context, batchID uint32, blockNumber *big.Int) ([32]byte, error) {
onchainHash, err := cv.manager.BatchIdToBatchMetadataHash(&bind.CallOpts{Context: ctx, BlockNumber: blockNumber}, batchID)
func (bcv *BridgedCertVerifier) retrieveBatchMetadataHash(ctx context.Context, batchID uint32, blockNumber *big.Int) ([32]byte, error) {
onchainHash, err := bcv.manager.BatchIdToBatchMetadataHash(&bind.CallOpts{Context: ctx, BlockNumber: blockNumber}, batchID)
if err != nil {
return [32]byte{}, fmt.Errorf("calling EigenDAServiceManager.BatchIdToBatchMetadataHash: %w", err)
}
Expand Down
18 changes: 9 additions & 9 deletions verify/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ type Verifier struct {
kzgVerifier *kzgverifier.Verifier
// cert verification is optional, and verifies certs retrieved from eigenDA when turned on
verifyCerts bool
cv *CertVerifier
bcv *BridgedCertVerifier
}

func NewVerifier(cfg *Config, l log.Logger) (*Verifier, error) {
var cv *CertVerifier
var bcv *BridgedCertVerifier
var err error

if cfg.VerifyCerts {
cv, err = NewCertVerifier(cfg, l)
bcv, err = NewBridgedCertVerifier(cfg, l)
if err != nil {
return nil, fmt.Errorf("failed to create cert verifier: %w", err)
}
Expand All @@ -56,24 +56,24 @@ func NewVerifier(cfg *Config, l log.Logger) (*Verifier, error) {
return &Verifier{
kzgVerifier: kzgVerifier,
verifyCerts: cfg.VerifyCerts,
cv: cv,
bcv: bcv,
}, nil
}

// verifies V0 eigenda certificate type
func (v *Verifier) VerifyCert(ctx context.Context, cert *Certificate) error {
func (v *Verifier) VerifyBridgeCert(ctx context.Context, cert *Certificate) error {
if !v.verifyCerts {
return nil
}

// 1 - verify batch in the cert is confirmed onchain
err := v.cv.verifyBatchConfirmedOnChain(ctx, cert.Proof().GetBatchId(), cert.Proof().GetBatchMetadata())
err := v.bcv.verifyBatchConfirmedOnChain(ctx, cert.Proof().GetBatchId(), cert.Proof().GetBatchMetadata())
if err != nil {
return fmt.Errorf("failed to verify batch: %w", err)
}

// 2 - verify merkle inclusion proof
err = v.cv.verifyMerkleProof(cert.Proof().GetInclusionProof(), cert.BatchHeaderRoot(), cert.Proof().GetBlobIndex(), cert.ReadBlobHeader())
err = v.bcv.verifyMerkleProof(cert.Proof().GetInclusionProof(), cert.BatchHeaderRoot(), cert.Proof().GetBlobIndex(), cert.ReadBlobHeader())
if err != nil {
return fmt.Errorf("failed to verify merkle proof: %w", err)
}
Expand Down Expand Up @@ -165,7 +165,7 @@ func (v *Verifier) verifySecurityParams(blobHeader BlobHeader, batchHeader *disp
confirmedQuorums[blobHeader.QuorumBlobParams[i].QuorumNumber] = true
}

requiredQuorums, err := v.cv.manager.QuorumNumbersRequired(&bind.CallOpts{BlockNumber: big.NewInt(int64(batchHeader.ReferenceBlockNumber))})
requiredQuorums, err := v.bcv.manager.QuorumNumbersRequired(&bind.CallOpts{BlockNumber: big.NewInt(int64(batchHeader.ReferenceBlockNumber))})
if err != nil {
log.Warn("failed to get required quorum numbers at block number", "err", err, "referenceBlockNumber", batchHeader.ReferenceBlockNumber)
}
Expand All @@ -183,7 +183,7 @@ func (v *Verifier) verifySecurityParams(blobHeader BlobHeader, batchHeader *disp
// getQuorumAdversaryThreshold reads the adversarial threshold percentage for a given quorum number,
// at a given block number. If the quorum number does not exist, it returns 0.
func (v *Verifier) getQuorumAdversaryThreshold(quorumNum uint8, blockNumber int64) (uint8, error) {
percentages, err := v.cv.manager.QuorumAdversaryThresholdPercentages(&bind.CallOpts{BlockNumber: big.NewInt(blockNumber)})
percentages, err := v.bcv.manager.QuorumAdversaryThresholdPercentages(&bind.CallOpts{BlockNumber: big.NewInt(blockNumber)})
if err != nil {
return 0, err
}
Expand Down

0 comments on commit c21b9f5

Please sign in to comment.