Skip to content

Commit

Permalink
[v2] Disperser request validation (#915)
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-shim authored Nov 21, 2024
1 parent 953397b commit 6ada199
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 25 deletions.
44 changes: 25 additions & 19 deletions disperser/apiserver/disperse_blob_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,33 +64,40 @@ func (s *DispersalServerV2) StoreBlob(ctx context.Context, data []byte, blobHead
func (s *DispersalServerV2) validateDispersalRequest(req *pb.DisperseBlobRequest) error {
data := req.GetData()
blobSize := len(data)
if uint64(blobSize) > s.maxNumSymbolsPerBlob*encoding.BYTES_PER_SYMBOL {
return api.NewErrorInvalidArg(fmt.Sprintf("blob size cannot exceed %v bytes", s.maxNumSymbolsPerBlob*encoding.BYTES_PER_SYMBOL))
}
if blobSize == 0 {
return api.NewErrorInvalidArg("blob size must be greater than 0")
}
blobLength := encoding.GetBlobLengthPowerOf2(uint(blobSize))
if blobLength > uint(s.maxNumSymbolsPerBlob) {
return api.NewErrorInvalidArg("blob size too big")
}

blobHeaderProto := req.GetBlobHeader()
if blobHeaderProto.GetCommitment() == nil {
return api.NewErrorInvalidArg("blob header must contain commitments")
}

if len(blobHeaderProto.GetQuorumNumbers()) == 0 {
return api.NewErrorInvalidArg("blob header must contain at least one quorum number")
}

if len(blobHeaderProto.GetQuorumNumbers()) > int(s.onchainState.QuorumCount) {
return api.NewErrorInvalidArg(fmt.Sprintf("too many quorum numbers specified: maximum is %d", s.onchainState.QuorumCount))
}

for _, quorum := range blobHeaderProto.GetQuorumNumbers() {
if quorum > corev2.MaxQuorumID || uint8(quorum) >= s.onchainState.QuorumCount {
return api.NewErrorInvalidArg(fmt.Sprintf("invalid quorum number %d; maximum is %d", quorum, s.onchainState.QuorumCount))
}
}

// validate every 32 bytes is a valid field element
_, err := rs.ToFrArray(data)
if err != nil {
s.logger.Error("failed to convert a 32bytes as a field element", "err", err)
return api.NewErrorInvalidArg("encountered an error to convert a 32-bytes into a valid field element, please use the correct format where every 32bytes(big-endian) is less than 21888242871839275222246405745257275088548364400416034343698204186575808495617")
}

if !containsRequiredQuorum(s.onchainState.RequiredQuorums, blobHeaderProto.GetQuorumNumbers()) {
return api.NewErrorInvalidArg(fmt.Sprintf("request must contain at least one required quorum: %v does not specify any of %v", blobHeaderProto.GetQuorumNumbers(), s.onchainState.RequiredQuorums))
}

if _, ok := s.onchainState.BlobVersionParameters[corev2.BlobVersion(blobHeaderProto.GetVersion())]; !ok {
validVersions := make([]int32, 0, len(s.onchainState.BlobVersionParameters))
for version := range s.onchainState.BlobVersionParameters {
Expand All @@ -107,18 +114,17 @@ func (s *DispersalServerV2) validateDispersalRequest(req *pb.DisperseBlobRequest
return api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error()))
}

// TODO(ian-shim): validate commitment, length is power of 2 and less than maxNumSymbolsPerBlob, payment metadata

return nil
}
if len(blobHeader.PaymentMetadata.AccountID) == 0 || blobHeader.PaymentMetadata.BinIndex == 0 || blobHeader.PaymentMetadata.CumulativePayment == nil {
return api.NewErrorInvalidArg("invalid payment metadata")
}

func containsRequiredQuorum(requiredQuorums []uint8, quorumNumbers []uint32) bool {
for _, required := range requiredQuorums {
for _, quorum := range quorumNumbers {
if uint8(quorum) == required {
return true
}
}
commitments, err := s.prover.GetCommitmentsForPaddedLength(data)
if err != nil {
return api.NewErrorInternal(fmt.Sprintf("failed to get commitments: %v", err))
}
if !commitments.Equal(&blobHeader.BlobCommitments) {
return api.NewErrorInvalidArg("invalid blob commitment")
}
return false

return nil
}
87 changes: 82 additions & 5 deletions disperser/apiserver/server_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
data := make([]byte, 50)
_, err := rand.Read(data)
assert.NoError(t, err)

signer := auth.NewLocalBlobRequestSigner(privateKeyHex)
data = codec.ConvertByPaddingEmptyByte(data)
commitments, err := prover.GetCommitmentsForPaddedLength(data)
assert.NoError(t, err)
Expand Down Expand Up @@ -150,10 +150,10 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
})
assert.ErrorContains(t, err, "too many quorum numbers specified")

// request without required quorums
// request with invalid quorum
invalidReqProto = &pbcommonv2.BlobHeader{
Version: 0,
QuorumNumbers: []uint32{2},
QuorumNumbers: []uint32{2, 54},
Commitment: commitmentProto,
PaymentHeader: &pbcommon.PaymentHeader{
AccountId: accountID,
Expand All @@ -165,7 +165,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
Data: data,
BlobHeader: invalidReqProto,
})
assert.ErrorContains(t, err, "request must contain at least one required quorum")
assert.ErrorContains(t, err, "invalid quorum")

// request with invalid blob version
invalidReqProto = &pbcommonv2.BlobHeader{
Expand Down Expand Up @@ -201,6 +201,83 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
BlobHeader: invalidReqProto,
})
assert.ErrorContains(t, err, "authentication failed")

// request with invalid payment metadata
invalidReqProto = &pbcommonv2.BlobHeader{
Version: 0,
QuorumNumbers: []uint32{0, 1},
Commitment: commitmentProto,
PaymentHeader: &pbcommon.PaymentHeader{
AccountId: accountID,
BinIndex: 0,
CumulativePayment: big.NewInt(100).Bytes(),
},
}
blobHeader, err := corev2.BlobHeaderFromProtobuf(invalidReqProto)
assert.NoError(t, err)
sig, err := signer.SignBlobRequest(blobHeader)
assert.NoError(t, err)
invalidReqProto.Signature = sig

_, err = c.DispersalServerV2.DisperseBlob(context.Background(), &pbv2.DisperseBlobRequest{
Data: data,
BlobHeader: invalidReqProto,
})
assert.ErrorContains(t, err, "invalid payment metadata")

// request with invalid commitment
invalidCommitment := commitmentProto
invalidCommitment.Length = commitmentProto.Length - 1
invalidReqProto = &pbcommonv2.BlobHeader{
Version: 0,
QuorumNumbers: []uint32{0, 1},
Commitment: invalidCommitment,
PaymentHeader: &pbcommon.PaymentHeader{
AccountId: accountID,
BinIndex: 5,
CumulativePayment: big.NewInt(100).Bytes(),
},
}
blobHeader, err = corev2.BlobHeaderFromProtobuf(invalidReqProto)
assert.NoError(t, err)
sig, err = signer.SignBlobRequest(blobHeader)
assert.NoError(t, err)
invalidReqProto.Signature = sig
_, err = c.DispersalServerV2.DisperseBlob(context.Background(), &pbv2.DisperseBlobRequest{
Data: data,
BlobHeader: invalidReqProto,
})
assert.ErrorContains(t, err, "invalid blob commitment")

// request with blob size exceeding the limit
data = make([]byte, 321)
_, err = rand.Read(data)
assert.NoError(t, err)
data = codec.ConvertByPaddingEmptyByte(data)
commitments, err = prover.GetCommitmentsForPaddedLength(data)
assert.NoError(t, err)
commitmentProto, err = commitments.ToProtobuf()
assert.NoError(t, err)
validHeader := &pbcommonv2.BlobHeader{
Version: 0,
QuorumNumbers: []uint32{0, 1},
Commitment: commitmentProto,
PaymentHeader: &pbcommon.PaymentHeader{
AccountId: accountID,
BinIndex: 5,
CumulativePayment: big.NewInt(100).Bytes(),
},
}
blobHeader, err = corev2.BlobHeaderFromProtobuf(validHeader)
assert.NoError(t, err)
sig, err = signer.SignBlobRequest(blobHeader)
assert.NoError(t, err)
validHeader.Signature = sig
_, err = c.DispersalServerV2.DisperseBlob(context.Background(), &pbv2.DisperseBlobRequest{
Data: data,
BlobHeader: validHeader,
})
assert.ErrorContains(t, err, "blob size too big")
}

func TestV2GetBlobStatus(t *testing.T) {
Expand Down Expand Up @@ -362,7 +439,7 @@ func newTestServerV2(t *testing.T) *testComponents {
s := apiserver.NewDispersalServerV2(disperser.ServerConfig{
GrpcPort: "51002",
GrpcTimeout: 1 * time.Second,
}, rateConfig, blobStore, blobMetadataStore, chainReader, nil, auth.NewAuthenticator(), prover, 100, time.Hour, logger)
}, rateConfig, blobStore, blobMetadataStore, chainReader, nil, auth.NewAuthenticator(), prover, 10, time.Hour, logger)

err = s.RefreshOnchainState(context.Background())
assert.NoError(t, err)
Expand Down
3 changes: 2 additions & 1 deletion disperser/cmd/apiserver/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Layr-Labs/eigenda/common/geth"
"github.com/Layr-Labs/eigenda/common/ratelimit"
"github.com/Layr-Labs/eigenda/disperser/apiserver"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/Layr-Labs/eigenda/encoding/kzg"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -150,7 +151,7 @@ var (
MaxNumSymbolsPerBlob = cli.UintFlag{
Name: common.PrefixFlag(FlagPrefix, "max-num-symbols-per-blob"),
Usage: "max number of symbols per blob. This flag is only relevant in v2",
Value: 65_536,
Value: 16 * 1024 * 1024 / encoding.BYTES_PER_SYMBOL, // this should allow for 16MiB blobs
EnvVar: common.PrefixEnvVar(envVarPrefix, "MAX_NUM_SYMBOLS_PER_BLOB"),
Required: false,
}
Expand Down
47 changes: 47 additions & 0 deletions encoding/data.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package encoding

import (
"bytes"

pbcommon "github.com/Layr-Labs/eigenda/api/grpc/common"
"github.com/consensys/gnark-crypto/ecc/bn254"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
Expand Down Expand Up @@ -54,6 +56,51 @@ func (c *BlobCommitments) ToProtobuf() (*pbcommon.BlobCommitment, error) {
}, nil
}

// Equal checks if two BlobCommitments are equal
func (c *BlobCommitments) Equal(c1 *BlobCommitments) bool {
if c.Length != c1.Length {
return false
}

cCommitment, err := c.Commitment.Serialize()
if err != nil {
return false
}
c1Commitment, err := c1.Commitment.Serialize()
if err != nil {
return false
}
if !bytes.Equal(cCommitment, c1Commitment) {
return false
}

cLengthCommitment, err := c.LengthCommitment.Serialize()
if err != nil {
return false
}
c1LengthCommitment, err := c1.LengthCommitment.Serialize()
if err != nil {
return false
}
if !bytes.Equal(cLengthCommitment, c1LengthCommitment) {
return false
}

cLengthProof, err := c.LengthProof.Serialize()
if err != nil {
return false
}
c1LengthProof, err := c1.LengthProof.Serialize()
if err != nil {
return false
}
if !bytes.Equal(cLengthProof, c1LengthProof) {
return false
}

return true
}

func BlobCommitmentsFromProtobuf(c *pbcommon.BlobCommitment) (*BlobCommitments, error) {
commitment, err := new(G1Commitment).Deserialize(c.Commitment)
if err != nil {
Expand Down

0 comments on commit 6ada199

Please sign in to comment.