Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 11 additions & 11 deletions cre/consensus_aggregators.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func ConsensusIdenticalAggregation[T any]() ConsensusAggregation[T] {
return &consensusDescriptor[T]{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_IDENTICAL}}
}

return &consensusDescriptorError[T]{Error: fmt.Errorf("%T is not a valid type for identical consensus", t)}
return &consensusDescriptorError[T]{Error: fmt.Errorf("consensus configuration error: type %T is not valid for identical consensus aggregation. Identical consensus requires primitive types (string, bool, numeric), maps with string keys, structs with exported fields, or slices/arrays of these types", t)}
}

// ConsensusCommonPrefixAggregation uses the longest common prefix across nodes.
Expand All @@ -48,7 +48,7 @@ func ConsensusCommonPrefixAggregation[T any]() func() (ConsensusAggregation[[]T]
return &consensusDescriptor[[]T]{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_COMMON_PREFIX}}, nil
}

return &consensusDescriptor[[]T]{}, fmt.Errorf("%T is not a valid type for common prefix consensus", t)
return &consensusDescriptor[[]T]{}, fmt.Errorf("consensus configuration error: type %T is not valid for common prefix consensus aggregation. Common prefix requires slices or arrays of primitive types", t)
}
}

Expand All @@ -61,7 +61,7 @@ func ConsensusCommonSuffixAggregation[T any]() func() (ConsensusAggregation[[]T]
return &consensusDescriptor[[]T]{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_COMMON_SUFFIX}}, nil
}

return &consensusDescriptor[[]T]{}, fmt.Errorf("%T is not a valid type for common prefix consensus", t)
return &consensusDescriptor[[]T]{}, fmt.Errorf("consensus configuration error: type %T is not valid for common suffix consensus aggregation. Common suffix requires slices or arrays of primitive types", t)
}
}

Expand Down Expand Up @@ -96,7 +96,7 @@ func parseConsensusTag(t reflect.Type, path string) (*sdk.ConsensusDescriptor, e
}

if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("ConsensusAggregationFromTags expects a struct type, got %s", t.Kind())
return nil, fmt.Errorf("consensus configuration error: ConsensusAggregationFromTags expects a struct type, got %s. Define your consensus output as a struct with consensus_aggregation tags on each field", t.Kind())
}

descriptors := make(map[string]*sdk.ConsensusDescriptor)
Expand All @@ -105,7 +105,7 @@ func parseConsensusTag(t reflect.Type, path string) (*sdk.ConsensusDescriptor, e
tag := field.Tag.Get("consensus_aggregation")
if tag == "" {
if field.IsExported() {
return nil, fmt.Errorf("missing consensus tag on type %s accessed via %s", t.Name(), path+field.Name)
return nil, fmt.Errorf("consensus configuration error: missing consensus_aggregation tag on exported field %q of type %s (accessed via %s). Add a tag like `consensus_aggregation:\"median\"` or `consensus_aggregation:\"identical\"`, or use `consensus_aggregation:\"ignore\"` to skip this field", field.Name, t.Name(), path+field.Name)
}

continue
Expand All @@ -115,7 +115,7 @@ func parseConsensusTag(t reflect.Type, path string) (*sdk.ConsensusDescriptor, e
}

if !field.IsExported() {
return nil, fmt.Errorf("unexported field %s with consensus tag on type %s accessed via %s", field.Name, t.Name(), path)
return nil, fmt.Errorf("consensus configuration error: unexported field %q on type %s (accessed via %s) has a consensus_aggregation tag, but only exported fields can participate in consensus. Either export the field or remove the tag", field.Name, t.Name(), path)
}

serializedName := field.Name
Expand Down Expand Up @@ -145,22 +145,22 @@ func parseConsensusTag(t reflect.Type, path string) (*sdk.ConsensusDescriptor, e
switch tag {
case "median":
if !isNumeric(tpe) {
return nil, fmt.Errorf("field %s marked as median but is not a numeric type", field.Name)
return nil, fmt.Errorf("consensus configuration error: field %q is tagged as \"median\" but has non-numeric type %s. Median aggregation requires a numeric type (int, uint, float, big.Int, decimal.Decimal, or time.Time)", field.Name, tpe)
}
descriptors[serializedName] = &sdk.ConsensusDescriptor{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_MEDIAN}}
case "identical":
if !isIdenticalType(tpe) {
return nil, fmt.Errorf("field %s marked as identical but is not a valid type", field.Name)
return nil, fmt.Errorf("consensus configuration error: field %q is tagged as \"identical\" but has unsupported type %s. Identical aggregation requires primitive types (string, bool, numeric), maps with string keys, or structs with exported fields", field.Name, tpe)
}
descriptors[serializedName] = &sdk.ConsensusDescriptor{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_IDENTICAL}}
case "common_prefix":
if !isIdenticalSliceOrArray(tpe) {
return nil, fmt.Errorf("field %s marked as common_prefix but is not slice/array", field.Name)
return nil, fmt.Errorf("consensus configuration error: field %q is tagged as \"common_prefix\" but has type %s. Common prefix aggregation requires a slice or array of primitive types", field.Name, tpe)
}
descriptors[serializedName] = &sdk.ConsensusDescriptor{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_COMMON_PREFIX}}
case "common_suffix":
if !isIdenticalSliceOrArray(field.Type) {
return nil, fmt.Errorf("field %s marked as common_suffix but is not slice/array", field.Name)
return nil, fmt.Errorf("consensus configuration error: field %q is tagged as \"common_suffix\" but has type %s. Common suffix aggregation requires a slice or array of primitive types", field.Name, tpe)
}
descriptors[serializedName] = &sdk.ConsensusDescriptor{Descriptor_: &sdk.ConsensusDescriptor_Aggregation{Aggregation: sdk.AggregationType_AGGREGATION_TYPE_COMMON_SUFFIX}}
case "nested":
Expand All @@ -169,7 +169,7 @@ func parseConsensusTag(t reflect.Type, path string) (*sdk.ConsensusDescriptor, e
return nil, fmt.Errorf("nested field %s: %w", field.Name, err)
}
default:
return nil, fmt.Errorf("unknown consensus tag: %s on field %s", tag, field.Name)
return nil, fmt.Errorf("consensus configuration error: unknown consensus_aggregation tag %q on field %q. Valid tags are: \"median\", \"identical\", \"common_prefix\", \"common_suffix\", \"nested\", \"ignore\"", tag, field.Name)
}
}

Expand Down
10 changes: 5 additions & 5 deletions cre/consensus_aggregators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ func TestConsensusAggregationFromTags(t *testing.T) {
}

desc := cre.ConsensusAggregationFromTags[S]()
// unexported field privateV with consensus tag on type S accessed via
require.ErrorContains(t, desc.Err(), "unexported field privateV with consensus tag on type S")
// unexported field "privateV" on type S has a consensus_aggregation tag
require.ErrorContains(t, desc.Err(), "consensus configuration error: unexported field \"privateV\" on type S")
})

t.Run("valid identical", func(t *testing.T) {
Expand Down Expand Up @@ -390,7 +390,7 @@ func TestConsensusAggregationFromTags(t *testing.T) {
Val string `consensus_aggregation:"median"`
}
desc := cre.ConsensusAggregationFromTags[S]()
require.ErrorContains(t, desc.Err(), "not a numeric type")
require.ErrorContains(t, desc.Err(), "is tagged as \"median\" but has non-numeric type")
})

t.Run("invalid not a struct", func(t *testing.T) {
Expand Down Expand Up @@ -430,7 +430,7 @@ func TestConsensusAggregationFromTags(t *testing.T) {
Nested Nested `consensus_aggregation:"nested"`
}
desc := cre.ConsensusAggregationFromTags[S]()
require.ErrorContains(t, desc.Err(), "missing consensus tag on type Nested accessed via Nested.Foo")
require.ErrorContains(t, desc.Err(), "consensus configuration error: missing consensus_aggregation tag on exported field \"Foo\" of type Nested (accessed via Nested.Foo)")
})

t.Run("invalid identical", func(t *testing.T) {
Expand Down Expand Up @@ -548,5 +548,5 @@ func testInvalidIdenticalFieldHelper[T any](t *testing.T) {
desc := cre.ConsensusAggregationFromTags[struct {
Val T `consensus_aggregation:"identical"`
}]()
require.ErrorContains(t, desc.Err(), "field Val marked as identical but is not a valid type")
require.ErrorContains(t, desc.Err(), "consensus configuration error: field \"Val\" is tagged as \"identical\" but has unsupported type")
}
2 changes: 1 addition & 1 deletion cre/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cre

const (
DefaultMaxResponseSizeBytes = 5 * 1024 * 1024 // 5 MB
ResponseBufferTooSmall = "response buffer too small"
ResponseBufferTooSmall = "response buffer too small: the serialized capability response exceeds the maximum allowed size. Consider reducing the response payload size or increasing DefaultMaxResponseSizeBytes"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// proto encoder outputs a map with these keys so that user payload can be easily extracted
ReportMetadataHeaderLength = 109
)
Loading