feat: Validate Msg proto annotations (#13793)

Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
Amaury
2023-03-20 16:27:36 +01:00
committed by GitHub
co-authored by Marko
parent 176c61cf70
commit 897ef64712
27 changed files with 611 additions and 295 deletions
+60
View File
@@ -0,0 +1,60 @@
package msgservice
import (
"errors"
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
msg "cosmossdk.io/api/cosmos/msg/v1"
)
// ValidateAnnotations validates that the proto annotations are correct.
// More specifically, it verifies:
// - all services named "Msg" have `(cosmos.msg.v1.service) = true`,
//
// More validations can be added here in the future.
//
// If `protoFiles` is nil, then protoregistry.GlobalFile will be used.
func ValidateProtoAnnotations(protoFiles *protoregistry.Files) error {
if protoFiles == nil {
protoFiles = protoregistry.GlobalFiles
}
var serviceErrs []error
protoFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
for i := 0; i < fd.Services().Len(); i++ {
sd := fd.Services().Get(i)
if sd.Name() == "Msg" {
// We use the heuristic that services name Msg are exactly the
// ones that need the proto annotations check.
err := validateMsgServiceAnnotations(protoFiles, sd)
if err != nil {
serviceErrs = append(serviceErrs, err)
}
}
}
return true
})
return errors.Join(serviceErrs...)
}
// validateMsgServiceAnnotations validates that the service has the
// `(cosmos.msg.v1.service) = true` proto annotation.
func validateMsgServiceAnnotations(protoFiles *protoregistry.Files, sd protoreflect.ServiceDescriptor) error {
ext := proto.GetExtension(sd.Options(), msg.E_Service)
isService, ok := ext.(bool)
if !ok {
return fmt.Errorf("expected bool, got %T", ext)
}
if !isService {
return fmt.Errorf("service %s does not have cosmos.msg.v1.service proto annotation", sd.FullName())
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
package msgservice
import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
_ "cosmossdk.io/api/cosmos/bank/v1beta1"
)
func TestValidateServiceAnnotations(t *testing.T) {
// Find an arbitrary query service that hasn't the service=true annotation.
sd, err := protoregistry.GlobalFiles.FindDescriptorByName("cosmos.bank.v1beta1.Query")
require.NoError(t, err)
err = validateMsgServiceAnnotations(nil, sd.(protoreflect.ServiceDescriptor))
require.Error(t, err)
sd, err = protoregistry.GlobalFiles.FindDescriptorByName("cosmos.bank.v1beta1.Msg")
require.NoError(t, err)
err = validateMsgServiceAnnotations(nil, sd.(protoreflect.ServiceDescriptor))
require.NoError(t, err)
}