diff --git a/disperser/apiserver/server.go b/disperser/apiserver/server.go index bb48e75765..158e2f034a 100644 --- a/disperser/apiserver/server.go +++ b/disperser/apiserver/server.go @@ -803,7 +803,7 @@ func (s *DispersalServer) validateRequestAndGetBlob(ctx context.Context, req *pb } if len(req.GetCustomQuorumNumbers()) > 256 { - return nil, errors.New("invalid request: number of custom_quorum_numbers must not exceed 256") + return nil, errors.New("number of custom_quorum_numbers must not exceed 256") } quorumConfig, err := s.updateQuorumConfig(ctx) @@ -812,7 +812,7 @@ func (s *DispersalServer) validateRequestAndGetBlob(ctx context.Context, req *pb } if len(req.GetCustomQuorumNumbers()) > int(quorumConfig.QuorumCount) { - return nil, errors.New("invalid request: number of custom_quorum_numbers must not exceed number of quorums") + return nil, errors.New("number of custom_quorum_numbers must not exceed number of quorums") } seenQuorums := make(map[uint8]struct{}) @@ -821,16 +821,16 @@ func (s *DispersalServer) validateRequestAndGetBlob(ctx context.Context, req *pb for i := range req.GetCustomQuorumNumbers() { if req.GetCustomQuorumNumbers()[i] > 254 { - return nil, fmt.Errorf("invalid request: quorum_numbers must be in range [0, 254], but found %d", req.GetCustomQuorumNumbers()[i]) + return nil, fmt.Errorf("custom_quorum_numbers must be in range [0, 254], but found %d", req.GetCustomQuorumNumbers()[i]) } quorumID := uint8(req.GetCustomQuorumNumbers()[i]) if quorumID >= quorumConfig.QuorumCount { - return nil, fmt.Errorf("invalid request: the quorum_numbers must be in range [0, %d], but found %d", s.quorumConfig.QuorumCount-1, quorumID) + return nil, fmt.Errorf("custom_quorum_numbers must be in range [0, %d], but found %d", s.quorumConfig.QuorumCount-1, quorumID) } if _, ok := seenQuorums[quorumID]; ok { - return nil, fmt.Errorf("invalid request: quorum_numbers must not contain duplicates") + return nil, fmt.Errorf("custom_quorum_numbers must not contain duplicates") } seenQuorums[quorumID] = struct{}{} @@ -838,14 +838,22 @@ func (s *DispersalServer) validateRequestAndGetBlob(ctx context.Context, req *pb // Add the required quorums to the list of quorums to check for _, quorumID := range quorumConfig.RequiredQuorums { + // Note: no matter if dual quorum staking is enabled or not, custom_quorum_numbers cannot + // use any required quorums that are defined onchain. if _, ok := seenQuorums[quorumID]; ok { - return nil, fmt.Errorf("invalid request: custom_quorum_numbers should not include the required quorums %v, but required quorum %d was found", quorumConfig.RequiredQuorums, quorumID) + return nil, fmt.Errorf("custom_quorum_numbers should not include the required quorums %v, but required quorum %d was found", quorumConfig.RequiredQuorums, quorumID) + } + if s.serverConfig.EnableDualQuorums { + seenQuorums[quorumID] = struct{}{} + } else if quorumID == 0 { + // If dual quorum staking is not enabled, we only consider the quorum 0 as the + // required quorum. + seenQuorums[quorumID] = struct{}{} } - seenQuorums[quorumID] = struct{}{} } if len(seenQuorums) == 0 { - return nil, fmt.Errorf("invalid request: the blob must be sent to at least one quorum") + return nil, fmt.Errorf("the blob must be sent to at least one quorum") } params := make([]*core.SecurityParam, len(seenQuorums)) diff --git a/disperser/apiserver/server_test.go b/disperser/apiserver/server_test.go index e10e823e58..e0cf81e049 100644 --- a/disperser/apiserver/server_test.go +++ b/disperser/apiserver/server_test.go @@ -193,13 +193,13 @@ func TestDisperseBlobWithInvalidQuorum(t *testing.T) { Data: data, CustomQuorumNumbers: []uint32{2}, }) - assert.ErrorContains(t, err, "invalid request: the quorum_numbers must be in range [0, 1], but found 2") + assert.ErrorContains(t, err, "custom_quorum_numbers must be in range [0, 1], but found 2") _, err = dispersalServer.DisperseBlob(ctx, &pb.DisperseBlobRequest{ Data: data, CustomQuorumNumbers: []uint32{0, 0}, }) - assert.ErrorContains(t, err, "invalid request: quorum_numbers must not contain duplicates") + assert.ErrorContains(t, err, "custom_quorum_numbers must not contain duplicates") } diff --git a/disperser/cmd/apiserver/config.go b/disperser/cmd/apiserver/config.go index 03d592aac8..dffb7afc21 100644 --- a/disperser/cmd/apiserver/config.go +++ b/disperser/cmd/apiserver/config.go @@ -49,8 +49,9 @@ func NewConfig(ctx *cli.Context) (Config, error) { config := Config{ AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix), ServerConfig: disperser.ServerConfig{ - GrpcPort: ctx.GlobalString(flags.GrpcPortFlag.Name), - GrpcTimeout: ctx.GlobalDuration(flags.GrpcTimeoutFlag.Name), + GrpcPort: ctx.GlobalString(flags.GrpcPortFlag.Name), + GrpcTimeout: ctx.GlobalDuration(flags.GrpcTimeoutFlag.Name), + EnableDualQuorums: ctx.GlobalBool(flags.EnableDualQuorums.Name), }, BlobstoreConfig: blobstore.Config{ BucketName: ctx.GlobalString(flags.S3BucketNameFlag.Name), diff --git a/disperser/cmd/apiserver/flags/flags.go b/disperser/cmd/apiserver/flags/flags.go index ebc28b5f0d..93e57aca94 100644 --- a/disperser/cmd/apiserver/flags/flags.go +++ b/disperser/cmd/apiserver/flags/flags.go @@ -87,6 +87,12 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "RATE_BUCKET_STORE_SIZE"), Required: false, } + EnableDualQuorums = cli.BoolFlag{ + Name: common.PrefixFlag(FlagPrefix, "enable-dual-quorums"), + Usage: "Whether to enable dual quorum staking. If false, only quorum 0 is used as required quorum", + Required: false, + EnvVar: common.PrefixEnvVar(envVarPrefix, "ENABLE_DUAL_QUORUMS"), + } ) var requiredFlags = []cli.Flag{ @@ -104,6 +110,7 @@ var optionalFlags = []cli.Flag{ EnableRatelimiter, BucketStoreSize, GrpcTimeoutFlag, + EnableDualQuorums, } // Flags contains the list of configuration options available to the binary. diff --git a/disperser/server_config.go b/disperser/server_config.go index b094fca2a6..2c0b94a607 100644 --- a/disperser/server_config.go +++ b/disperser/server_config.go @@ -9,4 +9,9 @@ const ( type ServerConfig struct { GrpcPort string GrpcTimeout time.Duration + + // Feature flags + // Whether enable the dual quorums. + // If false, only quorum 0 will be used as required quorum. + EnableDualQuorums bool } diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index 5386426a77..db62acb307 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -186,6 +186,8 @@ func (env *Config) generateDisperserVars(ind int, key, address, logPath, dbPath, DISPERSER_SERVER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, DISPERSER_SERVER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, + + DISPERSER_SERVER_ENABLE_DUAL_QUORUMS: "true", } env.applyDefaults(&v, "DISPERSER_SERVER", "dis", ind) diff --git a/inabox/deploy/env_vars.go b/inabox/deploy/env_vars.go index c3aa643df9..fa64211064 100644 --- a/inabox/deploy/env_vars.go +++ b/inabox/deploy/env_vars.go @@ -72,6 +72,8 @@ type DisperserVars struct { DISPERSER_SERVER_RETRIEVAL_BLOB_RATE string DISPERSER_SERVER_RETRIEVAL_BYTE_RATE string + + DISPERSER_SERVER_ENABLE_DUAL_QUORUMS string } func (vars DisperserVars) getEnvMap() map[string]string {