From b024711dfe4f788251912c86474c46ae519c5131 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 4 Dec 2023 17:33:32 +0100 Subject: [PATCH 01/33] add: off-chain sign and verify file --- .../offChain/msgSignArbitraryData.proto | 20 + .../offChain/msgSignArbitraryData.pulsar.go | 731 ++++++++++++++++++ client/v2/offChain/builder.go | 406 ++++++++++ client/v2/offChain/cli.go | 82 ++ client/v2/offChain/sign.go | 147 ++++ client/v2/offChain/sign_test.go | 122 +++ client/v2/offChain/verify.go | 96 +++ client/v2/offChain/verify_test.go | 71 ++ simapp/simd/cmd/commands.go | 2 + 9 files changed, 1677 insertions(+) create mode 100644 client/v2/internal/offChain/msgSignArbitraryData.proto create mode 100644 client/v2/internal/offChain/msgSignArbitraryData.pulsar.go create mode 100644 client/v2/offChain/builder.go create mode 100644 client/v2/offChain/cli.go create mode 100644 client/v2/offChain/sign.go create mode 100644 client/v2/offChain/sign_test.go create mode 100644 client/v2/offChain/verify.go create mode 100644 client/v2/offChain/verify_test.go diff --git a/client/v2/internal/offChain/msgSignArbitraryData.proto b/client/v2/internal/offChain/msgSignArbitraryData.proto new file mode 100644 index 000000000000..9e1fa3f31162 --- /dev/null +++ b/client/v2/internal/offChain/msgSignArbitraryData.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package offChain; + +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "amino/amino.proto"; + +// MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message +message MsgSignArbitraryData { + option (amino.name) = "offChain/MsgSignArbitraryData"; + option (cosmos.msg.v1.signer) = "signerAddress"; + // AppDomain is the application requesting off-chain message signing + string appDomain = 1; + // Signer is the sdk.AccAddress of the message signer + string signerAddress = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // Data represents the raw bytes of the content that is signed (text, json, etc) + string data = 3; +} + diff --git a/client/v2/internal/offChain/msgSignArbitraryData.pulsar.go b/client/v2/internal/offChain/msgSignArbitraryData.pulsar.go new file mode 100644 index 000000000000..f2a5b98c5742 --- /dev/null +++ b/client/v2/internal/offChain/msgSignArbitraryData.pulsar.go @@ -0,0 +1,731 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package offChain + +import ( + _ "cosmossdk.io/api/amino" + _ "cosmossdk.io/api/cosmos/msg/v1" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + runtime "github.com/cosmos/cosmos-proto/runtime" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_MsgSignArbitraryData protoreflect.MessageDescriptor + fd_MsgSignArbitraryData_appDomain protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_signerAddress protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_data protoreflect.FieldDescriptor +) + +func init() { + file_offChain_msgSignArbitraryData_proto_init() + md_MsgSignArbitraryData = File_offChain_msgSignArbitraryData_proto.Messages().ByName("MsgSignArbitraryData") + fd_MsgSignArbitraryData_appDomain = md_MsgSignArbitraryData.Fields().ByName("appDomain") + fd_MsgSignArbitraryData_signerAddress = md_MsgSignArbitraryData.Fields().ByName("signerAddress") + fd_MsgSignArbitraryData_data = md_MsgSignArbitraryData.Fields().ByName("data") +} + +var _ protoreflect.Message = (*fastReflection_MsgSignArbitraryData)(nil) + +type fastReflection_MsgSignArbitraryData MsgSignArbitraryData + +func (x *MsgSignArbitraryData) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgSignArbitraryData)(x) +} + +func (x *MsgSignArbitraryData) slowProtoReflect() protoreflect.Message { + mi := &file_offChain_msgSignArbitraryData_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgSignArbitraryData_messageType fastReflection_MsgSignArbitraryData_messageType +var _ protoreflect.MessageType = fastReflection_MsgSignArbitraryData_messageType{} + +type fastReflection_MsgSignArbitraryData_messageType struct{} + +func (x fastReflection_MsgSignArbitraryData_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgSignArbitraryData)(nil) +} +func (x fastReflection_MsgSignArbitraryData_messageType) New() protoreflect.Message { + return new(fastReflection_MsgSignArbitraryData) +} +func (x fastReflection_MsgSignArbitraryData_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgSignArbitraryData +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgSignArbitraryData) Descriptor() protoreflect.MessageDescriptor { + return md_MsgSignArbitraryData +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgSignArbitraryData) Type() protoreflect.MessageType { + return _fastReflection_MsgSignArbitraryData_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgSignArbitraryData) New() protoreflect.Message { + return new(fastReflection_MsgSignArbitraryData) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgSignArbitraryData) Interface() protoreflect.ProtoMessage { + return (*MsgSignArbitraryData)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgSignArbitraryData) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.AppDomain != "" { + value := protoreflect.ValueOfString(x.AppDomain) + if !f(fd_MsgSignArbitraryData_appDomain, value) { + return + } + } + if x.SignerAddress != "" { + value := protoreflect.ValueOfString(x.SignerAddress) + if !f(fd_MsgSignArbitraryData_signerAddress, value) { + return + } + } + if x.Data != "" { + value := protoreflect.ValueOfString(x.Data) + if !f(fd_MsgSignArbitraryData_data, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + return x.AppDomain != "" + case "offChain.MsgSignArbitraryData.signerAddress": + return x.SignerAddress != "" + case "offChain.MsgSignArbitraryData.data": + return x.Data != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + x.AppDomain = "" + case "offChain.MsgSignArbitraryData.signerAddress": + x.SignerAddress = "" + case "offChain.MsgSignArbitraryData.data": + x.Data = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + value := x.AppDomain + return protoreflect.ValueOfString(value) + case "offChain.MsgSignArbitraryData.signerAddress": + value := x.SignerAddress + return protoreflect.ValueOfString(value) + case "offChain.MsgSignArbitraryData.data": + value := x.Data + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + x.AppDomain = value.Interface().(string) + case "offChain.MsgSignArbitraryData.signerAddress": + x.SignerAddress = value.Interface().(string) + case "offChain.MsgSignArbitraryData.data": + x.Data = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + panic(fmt.Errorf("field appDomain of message offChain.MsgSignArbitraryData is not mutable")) + case "offChain.MsgSignArbitraryData.signerAddress": + panic(fmt.Errorf("field signerAddress of message offChain.MsgSignArbitraryData is not mutable")) + case "offChain.MsgSignArbitraryData.data": + panic(fmt.Errorf("field data of message offChain.MsgSignArbitraryData is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgSignArbitraryData) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "offChain.MsgSignArbitraryData.appDomain": + return protoreflect.ValueOfString("") + case "offChain.MsgSignArbitraryData.signerAddress": + return protoreflect.ValueOfString("") + case "offChain.MsgSignArbitraryData.data": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + } + panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgSignArbitraryData) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in offChain.MsgSignArbitraryData", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgSignArbitraryData) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgSignArbitraryData) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgSignArbitraryData) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgSignArbitraryData) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.AppDomain) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.SignerAddress) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Data) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgSignArbitraryData) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.Data) > 0 { + i -= len(x.Data) + copy(dAtA[i:], x.Data) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Data))) + i-- + dAtA[i] = 0x1a + } + if len(x.SignerAddress) > 0 { + i -= len(x.SignerAddress) + copy(dAtA[i:], x.SignerAddress) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.SignerAddress))) + i-- + dAtA[i] = 0x12 + } + if len(x.AppDomain) > 0 { + i -= len(x.AppDomain) + copy(dAtA[i:], x.AppDomain) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.AppDomain))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgSignArbitraryData) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgSignArbitraryData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgSignArbitraryData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field AppDomain", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.AppDomain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SignerAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.SignerAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Data = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: offChain/msgSignArbitraryData.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message +type MsgSignArbitraryData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // AppDomain is the application requesting off-chain message signing + AppDomain string `protobuf:"bytes,1,opt,name=appDomain,proto3" json:"appDomain,omitempty"` + // Signer is the sdk.AccAddress of the message signer + SignerAddress string `protobuf:"bytes,2,opt,name=signerAddress,proto3" json:"signerAddress,omitempty"` + // Data represents the raw bytes of the content that is signed (text, json, etc) + Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *MsgSignArbitraryData) Reset() { + *x = MsgSignArbitraryData{} + if protoimpl.UnsafeEnabled { + mi := &file_offChain_msgSignArbitraryData_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgSignArbitraryData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgSignArbitraryData) ProtoMessage() {} + +// Deprecated: Use MsgSignArbitraryData.ProtoReflect.Descriptor instead. +func (*MsgSignArbitraryData) Descriptor() ([]byte, []int) { + return file_offChain_msgSignArbitraryData_proto_rawDescGZIP(), []int{0} +} + +func (x *MsgSignArbitraryData) GetAppDomain() string { + if x != nil { + return x.AppDomain + } + return "" +} + +func (x *MsgSignArbitraryData) GetSignerAddress() string { + if x != nil { + return x.SignerAddress + } + return "" +} + +func (x *MsgSignArbitraryData) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +var File_offChain_msgSignArbitraryData_proto protoreflect.FileDescriptor + +var file_offChain_msgSignArbitraryData_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x6d, 0x73, 0x67, 0x53, 0x69, + 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x1a, + 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x01, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x53, 0x69, + 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x1c, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3e, 0x0a, + 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, + 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x0d, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x3a, 0x34, 0x82, 0xe7, 0xb0, 0x2a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x2f, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, + 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, + 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, + 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, + 0x73, 0x64, 0x6b, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0xa2, + 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0xca, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, + 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_offChain_msgSignArbitraryData_proto_rawDescOnce sync.Once + file_offChain_msgSignArbitraryData_proto_rawDescData = file_offChain_msgSignArbitraryData_proto_rawDesc +) + +func file_offChain_msgSignArbitraryData_proto_rawDescGZIP() []byte { + file_offChain_msgSignArbitraryData_proto_rawDescOnce.Do(func() { + file_offChain_msgSignArbitraryData_proto_rawDescData = protoimpl.X.CompressGZIP(file_offChain_msgSignArbitraryData_proto_rawDescData) + }) + return file_offChain_msgSignArbitraryData_proto_rawDescData +} + +var file_offChain_msgSignArbitraryData_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_offChain_msgSignArbitraryData_proto_goTypes = []interface{}{ + (*MsgSignArbitraryData)(nil), // 0: offChain.MsgSignArbitraryData +} +var file_offChain_msgSignArbitraryData_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_offChain_msgSignArbitraryData_proto_init() } +func file_offChain_msgSignArbitraryData_proto_init() { + if File_offChain_msgSignArbitraryData_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_offChain_msgSignArbitraryData_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgSignArbitraryData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_offChain_msgSignArbitraryData_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_offChain_msgSignArbitraryData_proto_goTypes, + DependencyIndexes: file_offChain_msgSignArbitraryData_proto_depIdxs, + MessageInfos: file_offChain_msgSignArbitraryData_proto_msgTypes, + }.Build() + File_offChain_msgSignArbitraryData_proto = out.File + file_offChain_msgSignArbitraryData_proto_rawDesc = nil + file_offChain_msgSignArbitraryData_proto_goTypes = nil + file_offChain_msgSignArbitraryData_proto_depIdxs = nil +} diff --git a/client/v2/offChain/builder.go b/client/v2/offChain/builder.go new file mode 100644 index 000000000000..533133a84c12 --- /dev/null +++ b/client/v2/offChain/builder.go @@ -0,0 +1,406 @@ +package offChain + +import ( + "errors" + "fmt" + + "github.com/cosmos/cosmos-proto/anyutil" + protov2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" + apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + apitx "cosmossdk.io/api/cosmos/tx/v1beta1" + "cosmossdk.io/math" + authsigning "cosmossdk.io/x/auth/signing" + authtx "cosmossdk.io/x/auth/tx" + txsigning "cosmossdk.io/x/tx/signing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" + typestx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +type builder struct { + cdc codec.Codec + + tx *apitx.Tx +} + +func newBuilder(cdc codec.Codec) *builder { + return &builder{ + cdc: cdc, + tx: &apitx.Tx{ + Body: &apitx.TxBody{}, + AuthInfo: &apitx.AuthInfo{ + Fee: &apitx.Fee{ + Amount: nil, + GasLimit: 0, + Payer: "", + Granter: "", + }, + }, + Signatures: nil, + }, + } +} + +func (b *builder) GetMsgs() []types.Msg { + msgs := make([]types.Msg, len(b.tx.Body.Messages)) + for i, v := range b.tx.Body.Messages { + msgs[i] = types.Msg(v) + } + return msgs +} + +func (b *builder) GetMsgsV2() ([]protov2.Message, error) { + _, msgs, err := b.getSigners() + return msgs, err +} + +func (b *builder) GetMemo() string { + return b.tx.Body.Memo +} + +func (b *builder) GetGas() uint64 { + return b.tx.AuthInfo.Fee.GasLimit +} + +func (b *builder) GetFee() types.Coins { + coins := make(types.Coins, len(b.tx.AuthInfo.Fee.Amount)) + for i, v := range b.tx.AuthInfo.Fee.Amount { + res, ok := math.NewIntFromString(v.Amount) + if !ok { + panic("could not convert amount") + } + coins[i] = types.Coin{ + Denom: v.Denom, + Amount: res, + } + } + return coins +} + +func (b *builder) FeePayer() []byte { + feePayer := b.tx.AuthInfo.Fee.Payer + if feePayer != "" { + feePayerAddr, err := b.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(feePayer) + if err != nil { + panic(err) + } + return feePayerAddr + } + // use first signer as default if no payer specified + signers, err := b.GetSigners() + if err != nil { + panic(err) + } + + return signers[0] +} + +func (b *builder) FeeGranter() []byte { + feeGranter := b.tx.AuthInfo.Fee.Granter + if feeGranter != "" { + feeGranterAddr, err := b.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(feeGranter) + if err != nil { + panic(err) + } + + return feeGranterAddr + } + return nil +} + +func (b *builder) GetTimeoutHeight() uint64 { + return b.tx.Body.TimeoutHeight +} + +func (b *builder) ValidateBasic() error { + return nil +} + +func (b *builder) GetProtoTx() *apitx.Tx { + return b.tx +} + +func (b *builder) GetSigningTxData() txsigning.TxData { + body := b.tx.Body + authInfo := b.tx.AuthInfo + + msgs := make([]*anypb.Any, len(body.Messages)) + for i, msg := range body.Messages { + msgs[i] = &anypb.Any{ + TypeUrl: msg.TypeUrl, + Value: msg.Value, + } + } + + extOptions := make([]*anypb.Any, len(body.ExtensionOptions)) + for i, extOption := range body.ExtensionOptions { + extOptions[i] = &anypb.Any{ + TypeUrl: extOption.TypeUrl, + Value: extOption.Value, + } + } + + nonCriticalExtOptions := make([]*anypb.Any, len(body.NonCriticalExtensionOptions)) + for i, extOption := range body.NonCriticalExtensionOptions { + nonCriticalExtOptions[i] = &anypb.Any{ + TypeUrl: extOption.TypeUrl, + Value: extOption.Value, + } + } + + feeCoins := authInfo.Fee.Amount + feeAmount := make([]*basev1beta1.Coin, len(feeCoins)) + for i, coin := range feeCoins { + feeAmount[i] = &basev1beta1.Coin{ + Denom: coin.Denom, + Amount: coin.Amount, + } + } + + txSignerInfos := make([]*apitx.SignerInfo, len(authInfo.SignerInfos)) + for i, signerInfo := range authInfo.SignerInfos { + txSignerInfo := &apitx.SignerInfo{ + PublicKey: &anypb.Any{ + TypeUrl: signerInfo.PublicKey.TypeUrl, + Value: signerInfo.PublicKey.Value, + }, + Sequence: signerInfo.Sequence, + ModeInfo: signerInfo.ModeInfo, + } + txSignerInfos[i] = txSignerInfo + } + + txAuthInfo := &apitx.AuthInfo{ + SignerInfos: txSignerInfos, + Fee: &apitx.Fee{ + Amount: feeAmount, + GasLimit: authInfo.Fee.GasLimit, + Payer: authInfo.Fee.Payer, + Granter: authInfo.Fee.Granter, + }, + } + + txBody := &apitx.TxBody{ + Messages: msgs, + Memo: body.Memo, + TimeoutHeight: body.TimeoutHeight, + ExtensionOptions: extOptions, + NonCriticalExtensionOptions: nonCriticalExtOptions, + } + authInfoBz, err := protov2.Marshal(b.tx.AuthInfo) + if err != nil { + panic(err) + } + bodyBz, err := protov2.Marshal(b.tx.Body) + if err != nil { + panic(err) + } + txData := txsigning.TxData{ + AuthInfo: txAuthInfo, + AuthInfoBytes: authInfoBz, + Body: txBody, + BodyBytes: bodyBz, + } + return txData +} + +func (b *builder) GetTx() authsigning.Tx { + return b +} + +func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer already has pubkey in context, this list will have nil in its place + signerInfos := b.tx.AuthInfo.SignerInfos + pks := make([]cryptotypes.PubKey, len(signerInfos)) + + for i, si := range signerInfos { + // NOTE: it is okay to leave this nil if there is no PubKey in the SignerInfo. + // PubKey's can be left unset in SignerInfo. + if si.PublicKey == nil { + continue + } + var pk cryptotypes.PubKey + anyPk := &codectypes.Any{ + TypeUrl: si.PublicKey.TypeUrl, + Value: si.PublicKey.Value, + } + err := b.cdc.UnpackAny(anyPk, &pk) + if err != nil { + return nil, err + } + pks[i] = pk + } + + return pks, nil +} + +func (b *builder) GetSignaturesV2() ([]signing.SignatureV2, error) { + signerInfos := b.tx.AuthInfo.SignerInfos + sigs := b.tx.Signatures + pubKeys, err := b.GetPubKeys() + if err != nil { + return nil, err + } + n := len(signerInfos) + res := make([]signing.SignatureV2, n) + + for i, si := range signerInfos { + // handle nil signatures (in case of simulation) + if si.ModeInfo == nil { + res[i] = signing.SignatureV2{ + PubKey: pubKeys[i], + } + } else { + var err error + sigData, err := modeInfoAndSigToSignatureData(si.ModeInfo, sigs[i]) + if err != nil { + return nil, err + } + // sequence number is functionally a transaction nonce and referred to as such in the SDK + nonce := si.GetSequence() + res[i] = signing.SignatureV2{ + PubKey: pubKeys[i], + Data: sigData, + Sequence: nonce, + } + + } + } + + return res, nil +} + +func (b *builder) GetSigners() ([][]byte, error) { + signers, _, err := b.getSigners() + return signers, err +} + +func (b *builder) getSigners() ([][]byte, []protov2.Message, error) { + var signers [][]byte + seen := map[string]bool{} + + var msgsv2 []protov2.Message + for _, msg := range b.tx.Body.Messages { + msgv2, err := anyutil.Unpack(msg, b.cdc.InterfaceRegistry(), nil) + if err != nil { + return nil, nil, err + } + xs, err := b.cdc.InterfaceRegistry().SigningContext().GetSigners(msgv2) + if err != nil { + return nil, nil, err + } + + msgsv2 = append(msgsv2, msg) + + for _, signer := range xs { + if !seen[string(signer)] { + signers = append(signers, signer) + seen[string(signer)] = true + } + } + } + + // ensure any specified fee payer is included in the required signers (at the end) + feePayer := b.tx.AuthInfo.Fee.Payer + var feePayerAddr []byte + if feePayer != "" { + var err error + if err != nil { + return nil, nil, err + } + } + if feePayerAddr != nil && !seen[string(feePayerAddr)] { + signers = append(signers, feePayerAddr) + seen[string(feePayerAddr)] = true + } + + return signers, msgsv2, nil +} + +func (b *builder) setMsgs(msgs ...types.Msg) error { + anys := make([]*anypb.Any, len(msgs)) + for i, msg := range msgs { + protoMsg, ok := msg.(protov2.Message) + if !ok { + return errors.New("") + } + protov2MarshalOpts := protov2.MarshalOptions{Deterministic: true} + bz, err := protov2MarshalOpts.Marshal(protoMsg) + if err != nil { + return err + } + anys[i] = &anypb.Any{ + TypeUrl: types.MsgTypeURL(msg), + Value: bz, + } + } + b.tx.Body.Messages = anys + return nil +} + +func (b *builder) SetSignatures(signatures ...signing.SignatureV2) error { + n := len(signatures) + signerInfos := make([]*apitx.SignerInfo, n) + rawSigs := make([][]byte, n) + + for i, sig := range signatures { + var mi *typestx.ModeInfo + mi, rawSigs[i] = authtx.SignatureDataToModeInfoAndSig(sig.Data) + modeinfo, err := castModeInfo(mi) + if err != nil { + return err + } + pubKey, err := codectypes.NewAnyWithValue(sig.PubKey) + if err != nil { + return err + } + signerInfos[i] = &apitx.SignerInfo{ + PublicKey: &anypb.Any{ + TypeUrl: pubKey.TypeUrl, + Value: pubKey.Value, + }, + ModeInfo: modeinfo, + Sequence: sig.Sequence, + } + } + + b.tx.AuthInfo.SignerInfos = signerInfos + b.tx.Signatures = rawSigs + + return nil +} + +// TODO: cast multisig +func castModeInfo(modeinfo *typestx.ModeInfo) (*apitx.ModeInfo, error) { + mi, ok := modeinfo.GetSum().(*typestx.ModeInfo_Single_) + if !ok { + return nil, errors.New("") + } + return &apitx.ModeInfo{ + Sum: &apitx.ModeInfo_Single_{ + Single: &apitx.ModeInfo_Single{ + Mode: apitxsigning.SignMode(mi.Single.Mode), + }, + }, + }, nil +} + +// modeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData +func modeInfoAndSigToSignatureData(modeInfo *apitx.ModeInfo, sig []byte) (signing.SignatureData, error) { + switch modeInfoType := modeInfo.Sum.(type) { + case *apitx.ModeInfo_Single_: + return &signing.SingleSignatureData{ + SignMode: signing.SignMode(modeInfoType.Single.Mode), + Signature: sig, + }, nil + + default: + panic(fmt.Errorf("unexpected ModeInfo data type %T", modeInfo)) + } +} diff --git a/client/v2/offChain/cli.go b/client/v2/offChain/cli.go new file mode 100644 index 000000000000..1c9ad38e4cc9 --- /dev/null +++ b/client/v2/offChain/cli.go @@ -0,0 +1,82 @@ +package offChain + +import ( + "os" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +// TODO: auto-cli + +// OffChain off-chain utilities +func OffChain() *cobra.Command { + cmd := &cobra.Command{ + Use: "offchain", + Short: "offchain msg utilities", + Long: ``, + } + + cmd.AddCommand( + SignFile(), + VerifyFile(), + ) + + cmd.PersistentFlags().String(flags.FlagOutput, "text", "Output format (text|json)") + flags.AddKeyringFlags(cmd.PersistentFlags()) + + return cmd +} + +// SignFile sign a file with a key +func SignFile() *cobra.Command { + return &cobra.Command{ + Use: "sign ", + Short: "Sign a file", + Long: "Sign a file using a key.", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + bz, err := os.ReadFile(args[1]) + if err != nil { + return err + } + + signedTx, err := Sign(clientCtx, bz, args[0], clientCtx.SignModeStr) + if err != nil { + return err + } + cmd.Println(signedTx) + return nil + }, + } +} + +// VerifyFile sign a file with a key +func VerifyFile() *cobra.Command { + return &cobra.Command{ + Use: "verify ", + Short: "", + Long: "", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + bz, err := os.ReadFile(args[1]) + if err != nil { + return err + } + + return Verify(clientCtx, bz) + }, + } +} diff --git a/client/v2/offChain/sign.go b/client/v2/offChain/sign.go new file mode 100644 index 000000000000..1bfcae3b47f6 --- /dev/null +++ b/client/v2/offChain/sign.go @@ -0,0 +1,147 @@ +package offChain + +import ( + "context" + "google.golang.org/protobuf/encoding/protojson" + + apitx "cosmossdk.io/api/cosmos/tx/v1beta1" + "cosmossdk.io/client/v2/internal/offChain" + authsigning "cosmossdk.io/x/auth/signing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/version" +) + +const ( + // ExpectedChainID defines the chain id an off-chain message must have + ExpectedChainID = "" + // ExpectedAccountNumber defines the account number an off-chain message must have + ExpectedAccountNumber = 0 + // ExpectedSequence defines the sequence number an off-chain message must have + ExpectedSequence = 0 +) + +type encodingFunc = func([]byte) (string, error) + +func noEncoding(digest []byte) (string, error) { + return string(digest), nil +} + +func getSignMode(signModeStr string) signing.SignMode { + signMode := signing.SignMode_SIGN_MODE_UNSPECIFIED + switch signModeStr { + case flags.SignModeDirect: + signMode = signing.SignMode_SIGN_MODE_DIRECT + case flags.SignModeLegacyAminoJSON: + signMode = signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON + case flags.SignModeDirectAux: + signMode = signing.SignMode_SIGN_MODE_DIRECT_AUX + case flags.SignModeTextual: + signMode = signing.SignMode_SIGN_MODE_TEXTUAL + } + return signMode +} + +func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (string, error) { + digest, err := noEncoding(rawBytes) + if err != nil { + return "", err + } + + tx, err := sign(ctx, fromName, digest, getSignMode(signMode)) + if err != nil { + return "", err + } + + return marshalOffChainTx(tx, true, " ") +} + +func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode) (*apitx.Tx, error) { + keybase := ctx.Keyring + r, err := keybase.Key(fromName) + if err != nil { + return nil, err + } + + pubKey, err := r.GetPubKey() + if err != nil { + return nil, err + } + + msg := &offChain.MsgSignArbitraryData{ + AppDomain: version.AppName, + SignerAddress: types.AccAddress(pubKey.Address()).String(), + Data: digest, + } + + txBuilder := newBuilder(ctx.Codec) + err = txBuilder.setMsgs(msg) + if err != nil { + return nil, err + } + + signerData := authsigning.SignerData{ + Address: types.AccAddress(pubKey.Address()).String(), + ChainID: ExpectedChainID, + AccountNumber: ExpectedAccountNumber, + Sequence: ExpectedSequence, + PubKey: pubKey, + } + sigData := signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + } + sig := signing.SignatureV2{ + PubKey: pubKey, + Data: &sigData, + Sequence: ExpectedSequence, + } + + sigs := []signing.SignatureV2{sig} + err = txBuilder.SetSignatures(sigs...) + if err != nil { + return nil, err + } + + bytesToSign, err := authsigning.GetSignBytesAdapter( + context.Background(), ctx.TxConfig.SignModeHandler(), + signMode, signerData, txBuilder.GetTx()) + if err != nil { + return nil, err + } + + signedBytes, _, err := keybase.Sign(fromName, bytesToSign, signMode) + if err != nil { + return nil, err + } + + sigData = signing.SingleSignatureData{ + SignMode: signMode, + Signature: signedBytes, + } + sig = signing.SignatureV2{ + PubKey: pubKey, + Data: &sigData, + Sequence: ExpectedSequence, + } + err = txBuilder.SetSignatures(sig) + if err != nil { + return nil, err + } + + return txBuilder.GetProtoTx(), nil +} + +func marshalOffChainTx(tx *apitx.Tx, emitUnpopulated bool, indent string) (string, error) { + bytesTx, err := protojson.MarshalOptions{ + EmitUnpopulated: emitUnpopulated, + Indent: indent, + }.Marshal(tx) + if err != nil { + return "", err + } + return string(bytesTx), nil +} diff --git a/client/v2/offChain/sign_test.go b/client/v2/offChain/sign_test.go new file mode 100644 index 000000000000..ace3044d0457 --- /dev/null +++ b/client/v2/offChain/sign_test.go @@ -0,0 +1,122 @@ +package offChain + +import ( + "github.com/cosmos/cosmos-sdk/codec/testutil" + "testing" + + "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + "cosmossdk.io/x/auth/tx" + txmodule "cosmossdk.io/x/auth/tx/config" + "cosmossdk.io/x/tx/signing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/address" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +func getCodec() codec.Codec { + registry := testutil.CodecOptions{}.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + return codec.NewProtoCodec(registry) +} + +func MakeTestTxConfig() client.TxConfig { + enabledSignModes := []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT, + signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signingtypes.SignMode_SIGN_MODE_TEXTUAL, + } + initClientCtx := client.Context{} + txConfigOpts := tx.ConfigOptions{ + EnabledSignModes: enabledSignModes, + TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), + } + ir, err := codectypes.NewInterfaceRegistryWithOptions(codectypes.InterfaceRegistryOptions{ + ProtoFiles: proto.HybridResolver, + SigningOptions: signing.Options{ + AddressCodec: address.NewBech32Codec("cosmos"), + ValidatorAddressCodec: address.NewBech32Codec("cosmosvaloper"), + }, + }) + if err != nil { + panic(err) + } + cryptocodec.RegisterInterfaces(ir) + cdc := codec.NewProtoCodec(ir) + txConfig, err := tx.NewTxConfigWithOptions(cdc, txConfigOpts) + if err != nil { + panic(err) + } + return txConfig +} + +func Test_sign(t *testing.T) { + k := keyring.NewInMemory(getCodec()) + type args struct { + ctx client.Context + fromName string + digest string + signMode signingtypes.SignMode + } + tests := []struct { + name string + args args + }{ + { + name: "signMode direct", + args: args{ + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + }, + fromName: "test", + digest: "Hello world!", + signMode: signingtypes.SignMode_SIGN_MODE_DIRECT, + }, + }, + { + name: "signMode textual", + args: args{ + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + }, + fromName: "test", + digest: "Hello world!", + signMode: signingtypes.SignMode_SIGN_MODE_TEXTUAL, + }, + }, + { + name: "signMode LegacyAmino", + args: args{ + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + }, + fromName: "test", + digest: "Hello world!", + signMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := k.NewAccount(tt.args.fromName, testdata.TestMnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) + require.NoError(t, err) + + got, err := sign(tt.args.ctx, tt.args.fromName, tt.args.digest, tt.args.signMode) + require.NoError(t, err) + require.NotNil(t, got) + }) + } +} diff --git a/client/v2/offChain/verify.go b/client/v2/offChain/verify.go new file mode 100644 index 000000000000..73369141ca8c --- /dev/null +++ b/client/v2/offChain/verify.go @@ -0,0 +1,96 @@ +package offChain + +import ( + "bytes" + "context" + "errors" + "fmt" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/anypb" + + apitx "cosmossdk.io/api/cosmos/tx/v1beta1" + authsigning "cosmossdk.io/x/auth/signing" + txsigning "cosmossdk.io/x/tx/signing" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types" +) + +func Verify(ctx client.Context, digest []byte) error { + tx := &apitx.Tx{} + err := protojson.Unmarshal(digest, tx) + if err != nil { + return err + } + err = verify(ctx, tx) + if err != nil { + return err + } + fmt.Printf("Verification OK") + return nil +} + +func verify(ctx client.Context, tx *apitx.Tx) error { + sigTx := builder{ + cdc: ctx.Codec, + tx: tx, + } + txConfig := ctx.TxConfig // TODO? + signModeHandler := txConfig.SignModeHandler() + signers, err := sigTx.GetSigners() + if err != nil { + return err + } + + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return err + } + + if len(sigs) != len(signers) { + return errors.New("") + } + + for i, sig := range sigs { + var ( + pubKey = sig.PubKey + sigAddr = types.AccAddress(pubKey.Address()) + ) + + if !bytes.Equal(sigAddr, signers[i]) { + return errors.New("signature does not match its respective signer") + } + + signingData := authsigning.SignerData{ + Address: sigAddr.String(), + ChainID: ExpectedChainID, + AccountNumber: ExpectedAccountNumber, + Sequence: ExpectedSequence, + PubKey: pubKey, + } + + anyPk, err := codectypes.NewAnyWithValue(pubKey) + if err != nil { + return err + } + txSignerData := txsigning.SignerData{ + ChainID: signingData.ChainID, + AccountNumber: signingData.AccountNumber, + Sequence: signingData.Sequence, + Address: signingData.Address, + PubKey: &anypb.Any{ + TypeUrl: anyPk.TypeUrl, + Value: anyPk.Value, + }, + } + + txData := sigTx.GetSigningTxData() + err = authsigning.VerifySignature(context.Background(), pubKey, txSignerData, sig.Data, signModeHandler, txData) + if err != nil { + return err + } + } + return nil +} diff --git a/client/v2/offChain/verify_test.go b/client/v2/offChain/verify_test.go new file mode 100644 index 000000000000..128b1967dd75 --- /dev/null +++ b/client/v2/offChain/verify_test.go @@ -0,0 +1,71 @@ +package offChain + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +func Test_verify(t *testing.T) { + k := keyring.NewInMemory(getCodec()) + tests := []struct { + name string + fromName string + digest string + signMode signing.SignMode + ctx client.Context + wantErr bool + }{ + { + name: "signMode direct", + fromName: "directKey", + digest: "hello world", + signMode: signing.SignMode_SIGN_MODE_DIRECT, + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + }, + }, + { + name: "signMode textual", + fromName: "textualKey", + digest: "hello world", + signMode: signing.SignMode_SIGN_MODE_TEXTUAL, + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + }, + }, + { + name: "signMode legacyAmino", + fromName: "textualKey", + digest: "hello world", + signMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := k.NewAccount(tt.fromName, testdata.TestMnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) + require.NoError(t, err) + + tx, err := sign(tt.ctx, tt.fromName, tt.digest, tt.signMode) + require.NoError(t, err) + + err = verify(tt.ctx, tx) + require.NoError(t, err) + }) + } +} diff --git a/simapp/simd/cmd/commands.go b/simapp/simd/cmd/commands.go index 32d8f6150e49..5e98f6ff6f2c 100644 --- a/simapp/simd/cmd/commands.go +++ b/simapp/simd/cmd/commands.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "cosmossdk.io/client/v2/offChain" "cosmossdk.io/log" "cosmossdk.io/simapp" confixcmd "cosmossdk.io/tools/confix/cmd" @@ -59,6 +60,7 @@ func initRootCmd( queryCommand(), txCommand(), keys.Commands(), + offChain.OffChain(), ) } From bfa6ec451ff41a854e78dc07d590e558c24670e1 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 11 Dec 2023 09:18:55 +0100 Subject: [PATCH 02/33] fix: package name --- .../msgSignArbitraryData.proto | 4 +- .../msgSignArbitraryData.pulsar.go | 148 +++++++++--------- client/v2/{offChain => offchain}/builder.go | 2 +- client/v2/{offChain => offchain}/cli.go | 2 +- client/v2/{offChain => offchain}/sign.go | 7 +- client/v2/{offChain => offchain}/sign_test.go | 4 +- client/v2/{offChain => offchain}/verify.go | 2 +- .../v2/{offChain => offchain}/verify_test.go | 2 +- simapp/simd/cmd/commands.go | 4 +- 9 files changed, 88 insertions(+), 87 deletions(-) rename client/v2/internal/{offChain => offchain}/msgSignArbitraryData.proto (89%) rename client/v2/internal/{offChain => offchain}/msgSignArbitraryData.pulsar.go (85%) rename client/v2/{offChain => offchain}/builder.go (99%) rename client/v2/{offChain => offchain}/cli.go (98%) rename client/v2/{offChain => offchain}/sign.go (97%) rename client/v2/{offChain => offchain}/sign_test.go (99%) rename client/v2/{offChain => offchain}/verify.go (99%) rename client/v2/{offChain => offchain}/verify_test.go (98%) diff --git a/client/v2/internal/offChain/msgSignArbitraryData.proto b/client/v2/internal/offchain/msgSignArbitraryData.proto similarity index 89% rename from client/v2/internal/offChain/msgSignArbitraryData.proto rename to client/v2/internal/offchain/msgSignArbitraryData.proto index 9e1fa3f31162..1483138357b0 100644 --- a/client/v2/internal/offChain/msgSignArbitraryData.proto +++ b/client/v2/internal/offchain/msgSignArbitraryData.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package offChain; +package offchain; import "cosmos_proto/cosmos.proto"; import "cosmos/msg/v1/msg.proto"; @@ -8,7 +8,7 @@ import "amino/amino.proto"; // MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message message MsgSignArbitraryData { - option (amino.name) = "offChain/MsgSignArbitraryData"; + option (amino.name) = "offchain/MsgSignArbitraryData"; option (cosmos.msg.v1.signer) = "signerAddress"; // AppDomain is the application requesting off-chain message signing string appDomain = 1; diff --git a/client/v2/internal/offChain/msgSignArbitraryData.pulsar.go b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go similarity index 85% rename from client/v2/internal/offChain/msgSignArbitraryData.pulsar.go rename to client/v2/internal/offchain/msgSignArbitraryData.pulsar.go index f2a5b98c5742..e03470083461 100644 --- a/client/v2/internal/offChain/msgSignArbitraryData.pulsar.go +++ b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-go-pulsar. DO NOT EDIT. -package offChain +package offchain import ( _ "cosmossdk.io/api/amino" @@ -23,8 +23,8 @@ var ( ) func init() { - file_offChain_msgSignArbitraryData_proto_init() - md_MsgSignArbitraryData = File_offChain_msgSignArbitraryData_proto.Messages().ByName("MsgSignArbitraryData") + file_offchain_msgSignArbitraryData_proto_init() + md_MsgSignArbitraryData = File_offchain_msgSignArbitraryData_proto.Messages().ByName("MsgSignArbitraryData") fd_MsgSignArbitraryData_appDomain = md_MsgSignArbitraryData.Fields().ByName("appDomain") fd_MsgSignArbitraryData_signerAddress = md_MsgSignArbitraryData.Fields().ByName("signerAddress") fd_MsgSignArbitraryData_data = md_MsgSignArbitraryData.Fields().ByName("data") @@ -39,7 +39,7 @@ func (x *MsgSignArbitraryData) ProtoReflect() protoreflect.Message { } func (x *MsgSignArbitraryData) slowProtoReflect() protoreflect.Message { - mi := &file_offChain_msgSignArbitraryData_proto_msgTypes[0] + mi := &file_offchain_msgSignArbitraryData_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -128,17 +128,17 @@ func (x *fastReflection_MsgSignArbitraryData) Range(f func(protoreflect.FieldDes // a repeated field is populated if it is non-empty. func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescriptor) bool { switch fd.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.appDomain": return x.AppDomain != "" - case "offChain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signerAddress": return x.SignerAddress != "" - case "offChain.MsgSignArbitraryData.data": + case "offchain.MsgSignArbitraryData.data": return x.Data != "" default: if fd.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", fd.FullName())) } } @@ -150,17 +150,17 @@ func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescripto // Clear is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescriptor) { switch fd.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.appDomain": x.AppDomain = "" - case "offChain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signerAddress": x.SignerAddress = "" - case "offChain.MsgSignArbitraryData.data": + case "offchain.MsgSignArbitraryData.data": x.Data = "" default: if fd.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", fd.FullName())) } } @@ -172,20 +172,20 @@ func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescrip // of the value; to obtain a mutable reference, use Mutable. func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { switch descriptor.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.appDomain": value := x.AppDomain return protoreflect.ValueOfString(value) - case "offChain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signerAddress": value := x.SignerAddress return protoreflect.ValueOfString(value) - case "offChain.MsgSignArbitraryData.data": + case "offchain.MsgSignArbitraryData.data": value := x.Data return protoreflect.ValueOfString(value) default: if descriptor.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", descriptor.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", descriptor.FullName())) } } @@ -201,17 +201,17 @@ func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldD // Set is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { switch fd.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.appDomain": x.AppDomain = value.Interface().(string) - case "offChain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signerAddress": x.SignerAddress = value.Interface().(string) - case "offChain.MsgSignArbitraryData.data": + case "offchain.MsgSignArbitraryData.data": x.Data = value.Interface().(string) default: if fd.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", fd.FullName())) } } @@ -227,17 +227,17 @@ func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescripto // Mutable is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": - panic(fmt.Errorf("field appDomain of message offChain.MsgSignArbitraryData is not mutable")) - case "offChain.MsgSignArbitraryData.signerAddress": - panic(fmt.Errorf("field signerAddress of message offChain.MsgSignArbitraryData is not mutable")) - case "offChain.MsgSignArbitraryData.data": - panic(fmt.Errorf("field data of message offChain.MsgSignArbitraryData is not mutable")) + case "offchain.MsgSignArbitraryData.appDomain": + panic(fmt.Errorf("field appDomain of message offchain.MsgSignArbitraryData is not mutable")) + case "offchain.MsgSignArbitraryData.signerAddress": + panic(fmt.Errorf("field signerAddress of message offchain.MsgSignArbitraryData is not mutable")) + case "offchain.MsgSignArbitraryData.data": + panic(fmt.Errorf("field data of message offchain.MsgSignArbitraryData is not mutable")) default: if fd.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", fd.FullName())) } } @@ -246,17 +246,17 @@ func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescr // For lists, maps, and messages, this returns a new, empty, mutable value. func (x *fastReflection_MsgSignArbitraryData) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "offChain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.appDomain": return protoreflect.ValueOfString("") - case "offChain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signerAddress": return protoreflect.ValueOfString("") - case "offChain.MsgSignArbitraryData.data": + case "offchain.MsgSignArbitraryData.data": return protoreflect.ValueOfString("") default: if fd.IsExtension() { - panic(fmt.Errorf("proto3 declared messages do not support extensions: offChain.MsgSignArbitraryData")) + panic(fmt.Errorf("proto3 declared messages do not support extensions: offchain.MsgSignArbitraryData")) } - panic(fmt.Errorf("message offChain.MsgSignArbitraryData does not contain field %s", fd.FullName())) + panic(fmt.Errorf("message offchain.MsgSignArbitraryData does not contain field %s", fd.FullName())) } } @@ -266,7 +266,7 @@ func (x *fastReflection_MsgSignArbitraryData) NewField(fd protoreflect.FieldDesc func (x *fastReflection_MsgSignArbitraryData) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { switch d.FullName() { default: - panic(fmt.Errorf("%s is not a oneof field in offChain.MsgSignArbitraryData", d.FullName())) + panic(fmt.Errorf("%s is not a oneof field in offchain.MsgSignArbitraryData", d.FullName())) } panic("unreachable") } @@ -567,7 +567,7 @@ func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods // versions: // protoc-gen-go v1.27.0 // protoc (unknown) -// source: offChain/msgSignArbitraryData.proto +// source: offchain/msgSignArbitraryData.proto const ( // Verify that this generated code is sufficiently up-to-date. @@ -593,7 +593,7 @@ type MsgSignArbitraryData struct { func (x *MsgSignArbitraryData) Reset() { *x = MsgSignArbitraryData{} if protoimpl.UnsafeEnabled { - mi := &file_offChain_msgSignArbitraryData_proto_msgTypes[0] + mi := &file_offchain_msgSignArbitraryData_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -607,7 +607,7 @@ func (*MsgSignArbitraryData) ProtoMessage() {} // Deprecated: Use MsgSignArbitraryData.ProtoReflect.Descriptor instead. func (*MsgSignArbitraryData) Descriptor() ([]byte, []int) { - return file_offChain_msgSignArbitraryData_proto_rawDescGZIP(), []int{0} + return file_offchain_msgSignArbitraryData_proto_rawDescGZIP(), []int{0} } func (x *MsgSignArbitraryData) GetAppDomain() string { @@ -631,12 +631,12 @@ func (x *MsgSignArbitraryData) GetData() string { return "" } -var File_offChain_msgSignArbitraryData_proto protoreflect.FileDescriptor +var File_offchain_msgSignArbitraryData_proto protoreflect.FileDescriptor -var file_offChain_msgSignArbitraryData_proto_rawDesc = []byte{ - 0x0a, 0x23, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x6d, 0x73, 0x67, 0x53, 0x69, +var file_offchain_msgSignArbitraryData_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x6d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x1a, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, @@ -651,39 +651,39 @@ var file_offChain_msgSignArbitraryData_proto_rawDesc = []byte{ 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x34, 0x82, 0xe7, 0xb0, 0x2a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, - 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, + 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0xa2, - 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0xca, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, - 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xa2, + 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0xca, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_offChain_msgSignArbitraryData_proto_rawDescOnce sync.Once - file_offChain_msgSignArbitraryData_proto_rawDescData = file_offChain_msgSignArbitraryData_proto_rawDesc + file_offchain_msgSignArbitraryData_proto_rawDescOnce sync.Once + file_offchain_msgSignArbitraryData_proto_rawDescData = file_offchain_msgSignArbitraryData_proto_rawDesc ) -func file_offChain_msgSignArbitraryData_proto_rawDescGZIP() []byte { - file_offChain_msgSignArbitraryData_proto_rawDescOnce.Do(func() { - file_offChain_msgSignArbitraryData_proto_rawDescData = protoimpl.X.CompressGZIP(file_offChain_msgSignArbitraryData_proto_rawDescData) +func file_offchain_msgSignArbitraryData_proto_rawDescGZIP() []byte { + file_offchain_msgSignArbitraryData_proto_rawDescOnce.Do(func() { + file_offchain_msgSignArbitraryData_proto_rawDescData = protoimpl.X.CompressGZIP(file_offchain_msgSignArbitraryData_proto_rawDescData) }) - return file_offChain_msgSignArbitraryData_proto_rawDescData + return file_offchain_msgSignArbitraryData_proto_rawDescData } -var file_offChain_msgSignArbitraryData_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_offChain_msgSignArbitraryData_proto_goTypes = []interface{}{ - (*MsgSignArbitraryData)(nil), // 0: offChain.MsgSignArbitraryData +var file_offchain_msgSignArbitraryData_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_offchain_msgSignArbitraryData_proto_goTypes = []interface{}{ + (*MsgSignArbitraryData)(nil), // 0: offchain.MsgSignArbitraryData } -var file_offChain_msgSignArbitraryData_proto_depIdxs = []int32{ +var file_offchain_msgSignArbitraryData_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name @@ -691,13 +691,13 @@ var file_offChain_msgSignArbitraryData_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for field type_name } -func init() { file_offChain_msgSignArbitraryData_proto_init() } -func file_offChain_msgSignArbitraryData_proto_init() { - if File_offChain_msgSignArbitraryData_proto != nil { +func init() { file_offchain_msgSignArbitraryData_proto_init() } +func file_offchain_msgSignArbitraryData_proto_init() { + if File_offchain_msgSignArbitraryData_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_offChain_msgSignArbitraryData_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_offchain_msgSignArbitraryData_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MsgSignArbitraryData); i { case 0: return &v.state @@ -714,18 +714,18 @@ func file_offChain_msgSignArbitraryData_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_offChain_msgSignArbitraryData_proto_rawDesc, + RawDescriptor: file_offchain_msgSignArbitraryData_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_offChain_msgSignArbitraryData_proto_goTypes, - DependencyIndexes: file_offChain_msgSignArbitraryData_proto_depIdxs, - MessageInfos: file_offChain_msgSignArbitraryData_proto_msgTypes, + GoTypes: file_offchain_msgSignArbitraryData_proto_goTypes, + DependencyIndexes: file_offchain_msgSignArbitraryData_proto_depIdxs, + MessageInfos: file_offchain_msgSignArbitraryData_proto_msgTypes, }.Build() - File_offChain_msgSignArbitraryData_proto = out.File - file_offChain_msgSignArbitraryData_proto_rawDesc = nil - file_offChain_msgSignArbitraryData_proto_goTypes = nil - file_offChain_msgSignArbitraryData_proto_depIdxs = nil + File_offchain_msgSignArbitraryData_proto = out.File + file_offchain_msgSignArbitraryData_proto_rawDesc = nil + file_offchain_msgSignArbitraryData_proto_goTypes = nil + file_offchain_msgSignArbitraryData_proto_depIdxs = nil } diff --git a/client/v2/offChain/builder.go b/client/v2/offchain/builder.go similarity index 99% rename from client/v2/offChain/builder.go rename to client/v2/offchain/builder.go index 533133a84c12..eba08b554cf7 100644 --- a/client/v2/offChain/builder.go +++ b/client/v2/offchain/builder.go @@ -1,4 +1,4 @@ -package offChain +package offchain import ( "errors" diff --git a/client/v2/offChain/cli.go b/client/v2/offchain/cli.go similarity index 98% rename from client/v2/offChain/cli.go rename to client/v2/offchain/cli.go index 1c9ad38e4cc9..675ab635650e 100644 --- a/client/v2/offChain/cli.go +++ b/client/v2/offchain/cli.go @@ -1,4 +1,4 @@ -package offChain +package offchain import ( "os" diff --git a/client/v2/offChain/sign.go b/client/v2/offchain/sign.go similarity index 97% rename from client/v2/offChain/sign.go rename to client/v2/offchain/sign.go index 1bfcae3b47f6..ce67d7d098ba 100644 --- a/client/v2/offChain/sign.go +++ b/client/v2/offchain/sign.go @@ -1,11 +1,12 @@ -package offChain +package offchain import ( "context" + "google.golang.org/protobuf/encoding/protojson" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" - "cosmossdk.io/client/v2/internal/offChain" + "cosmossdk.io/client/v2/internal/offchain" authsigning "cosmossdk.io/x/auth/signing" "github.com/cosmos/cosmos-sdk/client" @@ -71,7 +72,7 @@ func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode return nil, err } - msg := &offChain.MsgSignArbitraryData{ + msg := &offchain.MsgSignArbitraryData{ AppDomain: version.AppName, SignerAddress: types.AccAddress(pubKey.Address()).String(), Data: digest, diff --git a/client/v2/offChain/sign_test.go b/client/v2/offchain/sign_test.go similarity index 99% rename from client/v2/offChain/sign_test.go rename to client/v2/offchain/sign_test.go index ace3044d0457..7063ac684e2b 100644 --- a/client/v2/offChain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -1,7 +1,6 @@ -package offChain +package offchain import ( - "github.com/cosmos/cosmos-sdk/codec/testutil" "testing" "github.com/cosmos/gogoproto/proto" @@ -14,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/codec/testutil" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" diff --git a/client/v2/offChain/verify.go b/client/v2/offchain/verify.go similarity index 99% rename from client/v2/offChain/verify.go rename to client/v2/offchain/verify.go index 73369141ca8c..d20d8c817a8d 100644 --- a/client/v2/offChain/verify.go +++ b/client/v2/offchain/verify.go @@ -1,4 +1,4 @@ -package offChain +package offchain import ( "bytes" diff --git a/client/v2/offChain/verify_test.go b/client/v2/offchain/verify_test.go similarity index 98% rename from client/v2/offChain/verify_test.go rename to client/v2/offchain/verify_test.go index 128b1967dd75..9718d684811d 100644 --- a/client/v2/offChain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -1,4 +1,4 @@ -package offChain +package offchain import ( "testing" diff --git a/simapp/simd/cmd/commands.go b/simapp/simd/cmd/commands.go index 5e98f6ff6f2c..ae4bc424689a 100644 --- a/simapp/simd/cmd/commands.go +++ b/simapp/simd/cmd/commands.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "cosmossdk.io/client/v2/offChain" + "cosmossdk.io/client/v2/offchain" "cosmossdk.io/log" "cosmossdk.io/simapp" confixcmd "cosmossdk.io/tools/confix/cmd" @@ -60,7 +60,7 @@ func initRootCmd( queryCommand(), txCommand(), keys.Commands(), - offChain.OffChain(), + offchain.OffChain(), ) } From b3743b8223150bb2eb47c621af08549b9dbbd788 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 12 Dec 2023 12:28:43 +0100 Subject: [PATCH 03/33] update: remove some dependencies --- client/v2/offchain/builder.go | 170 ++++++++++-------------------- client/v2/offchain/cli.go | 12 +-- client/v2/offchain/sign.go | 73 +++++++++---- client/v2/offchain/sign_test.go | 24 +++-- client/v2/offchain/signature.go | 34 ++++++ client/v2/offchain/verify.go | 61 +++++++++-- client/v2/offchain/verify_test.go | 34 +++--- 7 files changed, 237 insertions(+), 171 deletions(-) create mode 100644 client/v2/offchain/signature.go diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index eba08b554cf7..701db1e481a8 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -5,23 +5,17 @@ import ( "fmt" "github.com/cosmos/cosmos-proto/anyutil" + "github.com/cosmos/gogoproto/proto" protov2 "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" - apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" - "cosmossdk.io/math" - authsigning "cosmossdk.io/x/auth/signing" - authtx "cosmossdk.io/x/auth/tx" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types" - typestx "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" ) type builder struct { @@ -48,81 +42,6 @@ func newBuilder(cdc codec.Codec) *builder { } } -func (b *builder) GetMsgs() []types.Msg { - msgs := make([]types.Msg, len(b.tx.Body.Messages)) - for i, v := range b.tx.Body.Messages { - msgs[i] = types.Msg(v) - } - return msgs -} - -func (b *builder) GetMsgsV2() ([]protov2.Message, error) { - _, msgs, err := b.getSigners() - return msgs, err -} - -func (b *builder) GetMemo() string { - return b.tx.Body.Memo -} - -func (b *builder) GetGas() uint64 { - return b.tx.AuthInfo.Fee.GasLimit -} - -func (b *builder) GetFee() types.Coins { - coins := make(types.Coins, len(b.tx.AuthInfo.Fee.Amount)) - for i, v := range b.tx.AuthInfo.Fee.Amount { - res, ok := math.NewIntFromString(v.Amount) - if !ok { - panic("could not convert amount") - } - coins[i] = types.Coin{ - Denom: v.Denom, - Amount: res, - } - } - return coins -} - -func (b *builder) FeePayer() []byte { - feePayer := b.tx.AuthInfo.Fee.Payer - if feePayer != "" { - feePayerAddr, err := b.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(feePayer) - if err != nil { - panic(err) - } - return feePayerAddr - } - // use first signer as default if no payer specified - signers, err := b.GetSigners() - if err != nil { - panic(err) - } - - return signers[0] -} - -func (b *builder) FeeGranter() []byte { - feeGranter := b.tx.AuthInfo.Fee.Granter - if feeGranter != "" { - feeGranterAddr, err := b.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(feeGranter) - if err != nil { - panic(err) - } - - return feeGranterAddr - } - return nil -} - -func (b *builder) GetTimeoutHeight() uint64 { - return b.tx.Body.TimeoutHeight -} - -func (b *builder) ValidateBasic() error { - return nil -} - func (b *builder) GetProtoTx() *apitx.Tx { return b.tx } @@ -211,10 +130,6 @@ func (b *builder) GetSigningTxData() txsigning.TxData { return txData } -func (b *builder) GetTx() authsigning.Tx { - return b -} - func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer already has pubkey in context, this list will have nil in its place signerInfos := b.tx.AuthInfo.SignerInfos pks := make([]cryptotypes.PubKey, len(signerInfos)) @@ -240,7 +155,7 @@ func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer alre return pks, nil } -func (b *builder) GetSignaturesV2() ([]signing.SignatureV2, error) { +func (b *builder) GetSignaturesV2() ([]SignatureV2, error) { signerInfos := b.tx.AuthInfo.SignerInfos sigs := b.tx.Signatures pubKeys, err := b.GetPubKeys() @@ -248,12 +163,12 @@ func (b *builder) GetSignaturesV2() ([]signing.SignatureV2, error) { return nil, err } n := len(signerInfos) - res := make([]signing.SignatureV2, n) + res := make([]SignatureV2, n) for i, si := range signerInfos { // handle nil signatures (in case of simulation) if si.ModeInfo == nil { - res[i] = signing.SignatureV2{ + res[i] = SignatureV2{ PubKey: pubKeys[i], } } else { @@ -264,7 +179,7 @@ func (b *builder) GetSignaturesV2() ([]signing.SignatureV2, error) { } // sequence number is functionally a transaction nonce and referred to as such in the SDK nonce := si.GetSequence() - res[i] = signing.SignatureV2{ + res[i] = SignatureV2{ PubKey: pubKeys[i], Data: sigData, Sequence: nonce, @@ -323,7 +238,7 @@ func (b *builder) getSigners() ([][]byte, []protov2.Message, error) { return signers, msgsv2, nil } -func (b *builder) setMsgs(msgs ...types.Msg) error { +func (b *builder) setMsgs(msgs ...proto.Message) error { anys := make([]*anypb.Any, len(msgs)) for i, msg := range msgs { protoMsg, ok := msg.(protov2.Message) @@ -336,7 +251,7 @@ func (b *builder) setMsgs(msgs ...types.Msg) error { return err } anys[i] = &anypb.Any{ - TypeUrl: types.MsgTypeURL(msg), + TypeUrl: codectypes.MsgTypeURL(msg), Value: bz, } } @@ -344,18 +259,15 @@ func (b *builder) setMsgs(msgs ...types.Msg) error { return nil } -func (b *builder) SetSignatures(signatures ...signing.SignatureV2) error { +func (b *builder) SetSignatures(signatures ...SignatureV2) error { n := len(signatures) signerInfos := make([]*apitx.SignerInfo, n) rawSigs := make([][]byte, n) for i, sig := range signatures { - var mi *typestx.ModeInfo - mi, rawSigs[i] = authtx.SignatureDataToModeInfoAndSig(sig.Data) - modeinfo, err := castModeInfo(mi) - if err != nil { - return err - } + var mi *apitx.ModeInfo + mi, rawSigs[i] = b.signatureDataToModeInfoAndSig(sig.Data) + pubKey, err := codectypes.NewAnyWithValue(sig.PubKey) if err != nil { return err @@ -365,7 +277,7 @@ func (b *builder) SetSignatures(signatures ...signing.SignatureV2) error { TypeUrl: pubKey.TypeUrl, Value: pubKey.Value, }, - ModeInfo: modeinfo, + ModeInfo: mi, Sequence: sig.Sequence, } } @@ -376,27 +288,57 @@ func (b *builder) SetSignatures(signatures ...signing.SignatureV2) error { return nil } -// TODO: cast multisig -func castModeInfo(modeinfo *typestx.ModeInfo) (*apitx.ModeInfo, error) { - mi, ok := modeinfo.GetSum().(*typestx.ModeInfo_Single_) - if !ok { - return nil, errors.New("") +// SignatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature +func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.ModeInfo, []byte) { + if data == nil { + return nil, nil } - return &apitx.ModeInfo{ - Sum: &apitx.ModeInfo_Single_{ - Single: &apitx.ModeInfo_Single{ - Mode: apitxsigning.SignMode(mi.Single.Mode), + + switch data := data.(type) { + case *SingleSignatureData: + return &apitx.ModeInfo{ + Sum: &apitx.ModeInfo_Single_{ + Single: &apitx.ModeInfo_Single{Mode: data.SignMode}, }, - }, - }, nil + }, data.Signature + + // TODO: multisig + // case *signing.MultiSignatureData: + // n := len(data.Signatures) + // modeInfos := make([]*apitx.ModeInfo, n) + // sigs := make([][]byte, n) + // + // for i, d := range data.Signatures { + // modeInfos[i], sigs[i] = SignatureDataToModeInfoAndSig(d) + // } + // + // multisig := cryptotypes.MultiSignature{ + // Signatures: sigs, + // } + // sig, err := multisig.Marshal() + // if err != nil { + // panic(err) + // } + // + // return &apitx.ModeInfo{ + // Sum: &apitx.ModeInfo_Multi_{ + // Multi: &apitx.ModeInfo_Multi{ + // Bitarray: data.BitArray, + // ModeInfos: modeInfos, + // }, + // }, + // }, sig + default: + panic(fmt.Sprintf("unexpected signature data type %T", data)) + } } // modeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData -func modeInfoAndSigToSignatureData(modeInfo *apitx.ModeInfo, sig []byte) (signing.SignatureData, error) { +func modeInfoAndSigToSignatureData(modeInfo *apitx.ModeInfo, sig []byte) (SignatureData, error) { switch modeInfoType := modeInfo.Sum.(type) { case *apitx.ModeInfo_Single_: - return &signing.SingleSignatureData{ - SignMode: signing.SignMode(modeInfoType.Single.Mode), + return &SingleSignatureData{ + SignMode: modeInfoType.Single.Mode, Signature: sig, }, nil diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 675ab635650e..9bc055596770 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -25,8 +25,8 @@ func OffChain() *cobra.Command { ) cmd.PersistentFlags().String(flags.FlagOutput, "text", "Output format (text|json)") + cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") flags.AddKeyringFlags(cmd.PersistentFlags()) - return cmd } @@ -38,20 +38,20 @@ func SignFile() *cobra.Command { Long: "Sign a file using a key.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return err - } + clientCtx := client.GetClientContextFromCmd(cmd) bz, err := os.ReadFile(args[1]) if err != nil { return err } - signedTx, err := Sign(clientCtx, bz, args[0], clientCtx.SignModeStr) + signmode, _ := cmd.Flags().GetString(flags.FlagSignMode) + + signedTx, err := Sign(clientCtx, bz, args[0], signmode) if err != nil { return err } + cmd.Println(signedTx) return nil }, diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index ce67d7d098ba..646e1b07f946 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -4,14 +4,17 @@ import ( "context" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/anypb" + apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" "cosmossdk.io/client/v2/internal/offchain" authsigning "cosmossdk.io/x/auth/signing" + txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" ) @@ -31,17 +34,17 @@ func noEncoding(digest []byte) (string, error) { return string(digest), nil } -func getSignMode(signModeStr string) signing.SignMode { - signMode := signing.SignMode_SIGN_MODE_UNSPECIFIED +func getSignMode(signModeStr string) apisigning.SignMode { + signMode := apisigning.SignMode_SIGN_MODE_UNSPECIFIED switch signModeStr { case flags.SignModeDirect: - signMode = signing.SignMode_SIGN_MODE_DIRECT + signMode = apisigning.SignMode_SIGN_MODE_DIRECT case flags.SignModeLegacyAminoJSON: - signMode = signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON + signMode = apisigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON case flags.SignModeDirectAux: - signMode = signing.SignMode_SIGN_MODE_DIRECT_AUX + signMode = apisigning.SignMode_SIGN_MODE_DIRECT_AUX case flags.SignModeTextual: - signMode = signing.SignMode_SIGN_MODE_TEXTUAL + signMode = apisigning.SignMode_SIGN_MODE_TEXTUAL } return signMode } @@ -60,7 +63,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (strin return marshalOffChainTx(tx, true, " ") } -func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode) (*apitx.Tx, error) { +func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { keybase := ctx.Keyring r, err := keybase.Key(fromName) if err != nil { @@ -72,9 +75,14 @@ func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode return nil, err } + addr, err := ctx.AddressCodec.BytesToString(pubKey.Address()) + if err != nil { + return nil, err + } + msg := &offchain.MsgSignArbitraryData{ AppDomain: version.AppName, - SignerAddress: types.AccAddress(pubKey.Address()).String(), + SignerAddress: addr, Data: digest, } @@ -85,45 +93,47 @@ func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode } signerData := authsigning.SignerData{ - Address: types.AccAddress(pubKey.Address()).String(), + Address: addr, ChainID: ExpectedChainID, AccountNumber: ExpectedAccountNumber, Sequence: ExpectedSequence, PubKey: pubKey, } - sigData := signing.SingleSignatureData{ + + sigData := SingleSignatureData{ SignMode: signMode, Signature: nil, } - sig := signing.SignatureV2{ + + sig := SignatureV2{ PubKey: pubKey, Data: &sigData, Sequence: ExpectedSequence, } - sigs := []signing.SignatureV2{sig} + sigs := []SignatureV2{sig} err = txBuilder.SetSignatures(sigs...) if err != nil { return nil, err } - bytesToSign, err := authsigning.GetSignBytesAdapter( + bytesToSign, err := getSignBytes( context.Background(), ctx.TxConfig.SignModeHandler(), - signMode, signerData, txBuilder.GetTx()) + signMode, signerData, txBuilder) if err != nil { return nil, err } - signedBytes, _, err := keybase.Sign(fromName, bytesToSign, signMode) + signedBytes, _, err := keybase.Sign(fromName, bytesToSign, signing.SignMode(signMode)) if err != nil { return nil, err } - sigData = signing.SingleSignatureData{ + sigData = SingleSignatureData{ SignMode: signMode, Signature: signedBytes, } - sig = signing.SignatureV2{ + sig = SignatureV2{ PubKey: pubKey, Data: &sigData, Sequence: ExpectedSequence, @@ -136,6 +146,33 @@ func sign(ctx client.Context, fromName, digest string, signMode signing.SignMode return txBuilder.GetProtoTx(), nil } +func getSignBytes(ctx context.Context, + handlerMap *txsigning.HandlerMap, + mode apisigning.SignMode, + signerData authsigning.SignerData, + tx authsigning.V2AdaptableTx, +) ([]byte, error) { + txData := tx.GetSigningTxData() + + anyPk, err := codectypes.NewAnyWithValue(signerData.PubKey) + if err != nil { + return nil, err + } + + txSignerData := txsigning.SignerData{ + ChainID: signerData.ChainID, + AccountNumber: signerData.AccountNumber, + Sequence: signerData.Sequence, + Address: signerData.Address, + PubKey: &anypb.Any{ + TypeUrl: anyPk.TypeUrl, + Value: anyPk.Value, + }, + } + + return handlerMap.GetSignBytes(ctx, mode, txSignerData, txData) +} + func marshalOffChainTx(tx *apitx.Tx, emitUnpopulated bool, indent string) (string, error) { bytesTx, err := protojson.MarshalOptions{ EmitUnpopulated: emitUnpopulated, diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 7063ac684e2b..de6ca05fa5ed 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" + apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" "cosmossdk.io/x/auth/tx" txmodule "cosmossdk.io/x/auth/tx/config" "cosmossdk.io/x/tx/signing" @@ -66,7 +67,7 @@ func Test_sign(t *testing.T) { ctx client.Context fromName string digest string - signMode signingtypes.SignMode + signMode apitxsigning.SignMode } tests := []struct { name string @@ -76,36 +77,39 @@ func Test_sign(t *testing.T) { name: "signMode direct", args: args{ ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + AddressCodec: address.NewBech32Codec("cosmos"), }, fromName: "test", digest: "Hello world!", - signMode: signingtypes.SignMode_SIGN_MODE_DIRECT, + signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, }, }, { name: "signMode textual", args: args{ ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + AddressCodec: address.NewBech32Codec("cosmos"), }, fromName: "test", digest: "Hello world!", - signMode: signingtypes.SignMode_SIGN_MODE_TEXTUAL, + signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, }, }, { name: "signMode LegacyAmino", args: args{ ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + AddressCodec: address.NewBech32Codec("cosmos"), }, fromName: "test", digest: "Hello world!", - signMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, }, }, } diff --git a/client/v2/offchain/signature.go b/client/v2/offchain/signature.go new file mode 100644 index 000000000000..3d981e8ba593 --- /dev/null +++ b/client/v2/offchain/signature.go @@ -0,0 +1,34 @@ +package offchain + +import ( + apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +type SignatureData interface { + isSignatureData() +} + +func (m *SingleSignatureData) isSignatureData() {} + +type SingleSignatureData struct { + // SignMode represents the SignMode of the signature + SignMode apitxsigning.SignMode + + // Signature is the raw signature. + Signature []byte +} + +type SignatureV2 struct { + // PubKey is the public key to use for verifying the signature + PubKey cryptotypes.PubKey + + // Data is the actual data of the signature which includes SignMode's and + // the signatures themselves for either single or multi-signatures. + Data SignatureData + + // Sequence is the sequence of this account. Only populated in + // SIGN_MODE_DIRECT. + Sequence uint64 +} diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index d20d8c817a8d..bd57a86639df 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -15,7 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) func Verify(ctx client.Context, digest []byte) error { @@ -54,17 +54,18 @@ func verify(ctx client.Context, tx *apitx.Tx) error { } for i, sig := range sigs { - var ( - pubKey = sig.PubKey - sigAddr = types.AccAddress(pubKey.Address()) - ) - - if !bytes.Equal(sigAddr, signers[i]) { + pubKey := sig.PubKey + if !bytes.Equal(pubKey.Address(), signers[i]) { return errors.New("signature does not match its respective signer") } + addr, err := ctx.AddressCodec.BytesToString(pubKey.Address()) + if err != nil { + return err + } + signingData := authsigning.SignerData{ - Address: sigAddr.String(), + Address: addr, ChainID: ExpectedChainID, AccountNumber: ExpectedAccountNumber, Sequence: ExpectedSequence, @@ -87,10 +88,52 @@ func verify(ctx client.Context, tx *apitx.Tx) error { } txData := sigTx.GetSigningTxData() - err = authsigning.VerifySignature(context.Background(), pubKey, txSignerData, sig.Data, signModeHandler, txData) + err = verifySignature(context.Background(), pubKey, txSignerData, sig.Data, signModeHandler, txData) if err != nil { return err } } return nil } + +// verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing +// modes. It differs from VerifySignature in that it uses the new txsigning.TxData interface in x/tx. +func verifySignature( + ctx context.Context, + pubKey cryptotypes.PubKey, + signerData txsigning.SignerData, + signatureData SignatureData, + handler *txsigning.HandlerMap, + txData txsigning.TxData, +) error { + switch data := signatureData.(type) { + case *SingleSignatureData: + signBytes, err := handler.GetSignBytes(ctx, data.SignMode, signerData, txData) + if err != nil { + return err + } + if !pubKey.VerifySignature(signBytes, data.Signature) { + return fmt.Errorf("unable to verify single signer signature") + } + return nil + + //case *signing.MultiSignatureData: + // multiPK, ok := pubKey.(multisig.PubKey) + // if !ok { + // return fmt.Errorf("expected %T, got %T", (multisig.PubKey)(nil), pubKey) + // } + // err := multiPK.VerifyMultisignature(func(mode signing.SignMode) ([]byte, error) { + // signMode, err := internalSignModeToAPI(mode) + // if err != nil { + // return nil, err + // } + // return handler.GetSignBytes(ctx, signMode, signerData, txData) + // }, data) + // if err != nil { + // return err + // } + // return nil + default: + return fmt.Errorf("unexpected SignatureData %T", signatureData) + } +} diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 9718d684811d..380ed29e8f5f 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -3,13 +3,16 @@ package offchain import ( "testing" + "github.com/stretchr/testify/require" + apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/testutil/testdata" - "github.com/cosmos/cosmos-sdk/types/tx/signing" ) func Test_verify(t *testing.T) { @@ -18,7 +21,7 @@ func Test_verify(t *testing.T) { name string fromName string digest string - signMode signing.SignMode + signMode apitxsigning.SignMode ctx client.Context wantErr bool }{ @@ -26,33 +29,36 @@ func Test_verify(t *testing.T) { name: "signMode direct", fromName: "directKey", digest: "hello world", - signMode: signing.SignMode_SIGN_MODE_DIRECT, + signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + AddressCodec: address.NewBech32Codec("cosmos"), }, }, { name: "signMode textual", fromName: "textualKey", digest: "hello world", - signMode: signing.SignMode_SIGN_MODE_TEXTUAL, + signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + AddressCodec: address.NewBech32Codec("cosmos"), }, }, { name: "signMode legacyAmino", fromName: "textualKey", digest: "hello world", - signMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + AddressCodec: address.NewBech32Codec("cosmos"), }, }, } From 55c5a98b1feeade54cfacbf403f1a330a09177e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Tue, 12 Dec 2023 16:40:35 +0100 Subject: [PATCH 04/33] Update client/v2/internal/offchain/msgSignArbitraryData.proto Co-authored-by: Marko --- client/v2/internal/offchain/msgSignArbitraryData.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/v2/internal/offchain/msgSignArbitraryData.proto b/client/v2/internal/offchain/msgSignArbitraryData.proto index 1483138357b0..9ec0bd8b2d8a 100644 --- a/client/v2/internal/offchain/msgSignArbitraryData.proto +++ b/client/v2/internal/offchain/msgSignArbitraryData.proto @@ -13,7 +13,7 @@ message MsgSignArbitraryData { // AppDomain is the application requesting off-chain message signing string appDomain = 1; // Signer is the sdk.AccAddress of the message signer - string signerAddress = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string signer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // Data represents the raw bytes of the content that is signed (text, json, etc) string data = 3; } From c640e72129c50dd2a6b87fbb3b244edc8154c43e Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Wed, 13 Dec 2023 10:59:18 +0100 Subject: [PATCH 05/33] fix: use autocli keyring --- .../offchain/msgSignArbitraryData.proto | 2 +- .../offchain/msgSignArbitraryData.pulsar.go | 100 +++++++++--------- client/v2/offchain/builder.go | 2 + client/v2/offchain/sign.go | 15 ++- client/v2/offchain/sign_test.go | 8 -- client/v2/offchain/verify.go | 8 +- client/v2/offchain/verify_test.go | 1 - 7 files changed, 65 insertions(+), 71 deletions(-) diff --git a/client/v2/internal/offchain/msgSignArbitraryData.proto b/client/v2/internal/offchain/msgSignArbitraryData.proto index 9ec0bd8b2d8a..145a33b7bfd6 100644 --- a/client/v2/internal/offchain/msgSignArbitraryData.proto +++ b/client/v2/internal/offchain/msgSignArbitraryData.proto @@ -9,7 +9,7 @@ import "amino/amino.proto"; // MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message message MsgSignArbitraryData { option (amino.name) = "offchain/MsgSignArbitraryData"; - option (cosmos.msg.v1.signer) = "signerAddress"; + option (cosmos.msg.v1.signer) = "signer"; // AppDomain is the application requesting off-chain message signing string appDomain = 1; // Signer is the sdk.AccAddress of the message signer diff --git a/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go index e03470083461..af14da5a5d13 100644 --- a/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go +++ b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go @@ -16,17 +16,17 @@ import ( ) var ( - md_MsgSignArbitraryData protoreflect.MessageDescriptor - fd_MsgSignArbitraryData_appDomain protoreflect.FieldDescriptor - fd_MsgSignArbitraryData_signerAddress protoreflect.FieldDescriptor - fd_MsgSignArbitraryData_data protoreflect.FieldDescriptor + md_MsgSignArbitraryData protoreflect.MessageDescriptor + fd_MsgSignArbitraryData_appDomain protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_signer protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_data protoreflect.FieldDescriptor ) func init() { file_offchain_msgSignArbitraryData_proto_init() md_MsgSignArbitraryData = File_offchain_msgSignArbitraryData_proto.Messages().ByName("MsgSignArbitraryData") fd_MsgSignArbitraryData_appDomain = md_MsgSignArbitraryData.Fields().ByName("appDomain") - fd_MsgSignArbitraryData_signerAddress = md_MsgSignArbitraryData.Fields().ByName("signerAddress") + fd_MsgSignArbitraryData_signer = md_MsgSignArbitraryData.Fields().ByName("signer") fd_MsgSignArbitraryData_data = md_MsgSignArbitraryData.Fields().ByName("data") } @@ -101,9 +101,9 @@ func (x *fastReflection_MsgSignArbitraryData) Range(f func(protoreflect.FieldDes return } } - if x.SignerAddress != "" { - value := protoreflect.ValueOfString(x.SignerAddress) - if !f(fd_MsgSignArbitraryData_signerAddress, value) { + if x.Signer != "" { + value := protoreflect.ValueOfString(x.Signer) + if !f(fd_MsgSignArbitraryData_signer, value) { return } } @@ -130,8 +130,8 @@ func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescripto switch fd.FullName() { case "offchain.MsgSignArbitraryData.appDomain": return x.AppDomain != "" - case "offchain.MsgSignArbitraryData.signerAddress": - return x.SignerAddress != "" + case "offchain.MsgSignArbitraryData.signer": + return x.Signer != "" case "offchain.MsgSignArbitraryData.data": return x.Data != "" default: @@ -152,8 +152,8 @@ func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescrip switch fd.FullName() { case "offchain.MsgSignArbitraryData.appDomain": x.AppDomain = "" - case "offchain.MsgSignArbitraryData.signerAddress": - x.SignerAddress = "" + case "offchain.MsgSignArbitraryData.signer": + x.Signer = "" case "offchain.MsgSignArbitraryData.data": x.Data = "" default: @@ -175,8 +175,8 @@ func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldD case "offchain.MsgSignArbitraryData.appDomain": value := x.AppDomain return protoreflect.ValueOfString(value) - case "offchain.MsgSignArbitraryData.signerAddress": - value := x.SignerAddress + case "offchain.MsgSignArbitraryData.signer": + value := x.Signer return protoreflect.ValueOfString(value) case "offchain.MsgSignArbitraryData.data": value := x.Data @@ -203,8 +203,8 @@ func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescripto switch fd.FullName() { case "offchain.MsgSignArbitraryData.appDomain": x.AppDomain = value.Interface().(string) - case "offchain.MsgSignArbitraryData.signerAddress": - x.SignerAddress = value.Interface().(string) + case "offchain.MsgSignArbitraryData.signer": + x.Signer = value.Interface().(string) case "offchain.MsgSignArbitraryData.data": x.Data = value.Interface().(string) default: @@ -229,8 +229,8 @@ func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescr switch fd.FullName() { case "offchain.MsgSignArbitraryData.appDomain": panic(fmt.Errorf("field appDomain of message offchain.MsgSignArbitraryData is not mutable")) - case "offchain.MsgSignArbitraryData.signerAddress": - panic(fmt.Errorf("field signerAddress of message offchain.MsgSignArbitraryData is not mutable")) + case "offchain.MsgSignArbitraryData.signer": + panic(fmt.Errorf("field signer of message offchain.MsgSignArbitraryData is not mutable")) case "offchain.MsgSignArbitraryData.data": panic(fmt.Errorf("field data of message offchain.MsgSignArbitraryData is not mutable")) default: @@ -248,7 +248,7 @@ func (x *fastReflection_MsgSignArbitraryData) NewField(fd protoreflect.FieldDesc switch fd.FullName() { case "offchain.MsgSignArbitraryData.appDomain": return protoreflect.ValueOfString("") - case "offchain.MsgSignArbitraryData.signerAddress": + case "offchain.MsgSignArbitraryData.signer": return protoreflect.ValueOfString("") case "offchain.MsgSignArbitraryData.data": return protoreflect.ValueOfString("") @@ -325,7 +325,7 @@ func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } - l = len(x.SignerAddress) + l = len(x.Signer) if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } @@ -369,10 +369,10 @@ func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods i-- dAtA[i] = 0x1a } - if len(x.SignerAddress) > 0 { - i -= len(x.SignerAddress) - copy(dAtA[i:], x.SignerAddress) - i = runtime.EncodeVarint(dAtA, i, uint64(len(x.SignerAddress))) + if len(x.Signer) > 0 { + i -= len(x.Signer) + copy(dAtA[i:], x.Signer) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Signer))) i-- dAtA[i] = 0x12 } @@ -466,7 +466,7 @@ func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods iNdEx = postIndex case 2: if wireType != 2 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SignerAddress", wireType) + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -494,7 +494,7 @@ func (x *fastReflection_MsgSignArbitraryData) ProtoMethods() *protoiface.Methods if postIndex > l { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF } - x.SignerAddress = string(dAtA[iNdEx:postIndex]) + x.Signer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { @@ -585,7 +585,7 @@ type MsgSignArbitraryData struct { // AppDomain is the application requesting off-chain message signing AppDomain string `protobuf:"bytes,1,opt,name=appDomain,proto3" json:"appDomain,omitempty"` // Signer is the sdk.AccAddress of the message signer - SignerAddress string `protobuf:"bytes,2,opt,name=signerAddress,proto3" json:"signerAddress,omitempty"` + Signer string `protobuf:"bytes,2,opt,name=signer,proto3" json:"signer,omitempty"` // Data represents the raw bytes of the content that is signed (text, json, etc) Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` } @@ -617,9 +617,9 @@ func (x *MsgSignArbitraryData) GetAppDomain() string { return "" } -func (x *MsgSignArbitraryData) GetSignerAddress() string { +func (x *MsgSignArbitraryData) GetSigner() string { if x != nil { - return x.SignerAddress + return x.Signer } return "" } @@ -641,30 +641,28 @@ var file_offchain_msgSignArbitraryData_proto_rawDesc = []byte{ 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x01, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x53, 0x69, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x01, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3e, 0x0a, - 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x0d, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x3a, 0x34, 0x82, 0xe7, 0xb0, 0x2a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x2f, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, - 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, - 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, - 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, - 0x73, 0x64, 0x6b, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xa2, - 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0xca, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, - 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, + 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, + 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x3a, 0x2d, 0x82, 0xe7, 0xb0, 0x2a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, + 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x4d, 0x73, + 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, + 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, + 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, + 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xca, 0x02, 0x08, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index 701db1e481a8..fbe093654cd0 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -1,5 +1,7 @@ package offchain +// TODO: remove custom off-chain builder once v2 tx builder is developed. + import ( "errors" "fmt" diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 646e1b07f946..d00f143946be 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -2,6 +2,7 @@ package offchain import ( "context" + cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/anypb" @@ -15,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" ) @@ -64,13 +64,12 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (strin } func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { - keybase := ctx.Keyring - r, err := keybase.Key(fromName) + keybase, err := cryptokeyring.NewAutoCLIKeyring(ctx.Keyring) if err != nil { return nil, err } - pubKey, err := r.GetPubKey() + pubKey, err := keybase.GetPubKey(fromName) if err != nil { return nil, err } @@ -81,9 +80,9 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM } msg := &offchain.MsgSignArbitraryData{ - AppDomain: version.AppName, - SignerAddress: addr, - Data: digest, + AppDomain: version.AppName, + Signer: addr, + Data: digest, } txBuilder := newBuilder(ctx.Codec) @@ -124,7 +123,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM return nil, err } - signedBytes, _, err := keybase.Sign(fromName, bytesToSign, signing.SignMode(signMode)) + signedBytes, err := keybase.Sign(fromName, bytesToSign, signMode) if err != nil { return nil, err } diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index de6ca05fa5ed..81c391eb043a 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -20,7 +20,6 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/testutil/testdata" - signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" ) func getCodec() codec.Codec { @@ -31,15 +30,8 @@ func getCodec() codec.Codec { } func MakeTestTxConfig() client.TxConfig { - enabledSignModes := []signingtypes.SignMode{ - signingtypes.SignMode_SIGN_MODE_DIRECT, - signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, - signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - signingtypes.SignMode_SIGN_MODE_TEXTUAL, - } initClientCtx := client.Context{} txConfigOpts := tx.ConfigOptions{ - EnabledSignModes: enabledSignModes, TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), } ir, err := codectypes.NewInterfaceRegistryWithOptions(codectypes.InterfaceRegistryOptions{ diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index bd57a86639df..9644f088e323 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -20,14 +20,17 @@ import ( func Verify(ctx client.Context, digest []byte) error { tx := &apitx.Tx{} + err := protojson.Unmarshal(digest, tx) if err != nil { return err } + err = verify(ctx, tx) if err != nil { return err } + fmt.Printf("Verification OK") return nil } @@ -37,8 +40,9 @@ func verify(ctx client.Context, tx *apitx.Tx) error { cdc: ctx.Codec, tx: tx, } - txConfig := ctx.TxConfig // TODO? - signModeHandler := txConfig.SignModeHandler() + + signModeHandler := ctx.TxConfig.SignModeHandler() + signers, err := sigTx.GetSigners() if err != nil { return err diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 380ed29e8f5f..7bb77d0c3028 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -3,7 +3,6 @@ package offchain import ( "testing" - "github.com/stretchr/testify/require" apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" From 4742f2a85537c128b15cf71b291f4e1805e54853 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Wed, 13 Dec 2023 12:10:44 +0100 Subject: [PATCH 06/33] offchain sig --- client/v2/offchain/builder.go | 10 +++++----- client/v2/offchain/sign.go | 10 +++++----- client/v2/offchain/sign_test.go | 17 ++++++++++++----- client/v2/offchain/signature.go | 2 +- client/v2/offchain/verify.go | 3 ++- client/v2/offchain/verify_test.go | 7 ++++--- 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index fbe093654cd0..cb4d6e06ee45 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -157,7 +157,7 @@ func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer alre return pks, nil } -func (b *builder) GetSignaturesV2() ([]SignatureV2, error) { +func (b *builder) GetSignatures() ([]OffchainSignature, error) { signerInfos := b.tx.AuthInfo.SignerInfos sigs := b.tx.Signatures pubKeys, err := b.GetPubKeys() @@ -165,12 +165,12 @@ func (b *builder) GetSignaturesV2() ([]SignatureV2, error) { return nil, err } n := len(signerInfos) - res := make([]SignatureV2, n) + res := make([]OffchainSignature, n) for i, si := range signerInfos { // handle nil signatures (in case of simulation) if si.ModeInfo == nil { - res[i] = SignatureV2{ + res[i] = OffchainSignature{ PubKey: pubKeys[i], } } else { @@ -181,7 +181,7 @@ func (b *builder) GetSignaturesV2() ([]SignatureV2, error) { } // sequence number is functionally a transaction nonce and referred to as such in the SDK nonce := si.GetSequence() - res[i] = SignatureV2{ + res[i] = OffchainSignature{ PubKey: pubKeys[i], Data: sigData, Sequence: nonce, @@ -261,7 +261,7 @@ func (b *builder) setMsgs(msgs ...proto.Message) error { return nil } -func (b *builder) SetSignatures(signatures ...SignatureV2) error { +func (b *builder) SetSignatures(signatures ...OffchainSignature) error { n := len(signatures) signerInfos := make([]*apitx.SignerInfo, n) rawSigs := make([][]byte, n) diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index d00f143946be..23984c6c63da 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -2,7 +2,6 @@ package offchain import ( "context" - cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/anypb" @@ -16,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/version" ) @@ -64,7 +64,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (strin } func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { - keybase, err := cryptokeyring.NewAutoCLIKeyring(ctx.Keyring) + keybase, err := keyring.NewAutoCLIKeyring(ctx.Keyring) if err != nil { return nil, err } @@ -104,13 +104,13 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM Signature: nil, } - sig := SignatureV2{ + sig := OffchainSignature{ PubKey: pubKey, Data: &sigData, Sequence: ExpectedSequence, } - sigs := []SignatureV2{sig} + sigs := []OffchainSignature{sig} err = txBuilder.SetSignatures(sigs...) if err != nil { return nil, err @@ -132,7 +132,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM SignMode: signMode, Signature: signedBytes, } - sig = SignatureV2{ + sig = OffchainSignature{ PubKey: pubKey, Data: &sigData, Sequence: ExpectedSequence, diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 81c391eb043a..f7c07d5fef22 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -19,7 +19,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/testutil/testdata" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" // TODO: needed as textual is not enabled by default ) func getCodec() codec.Codec { @@ -30,8 +30,15 @@ func getCodec() codec.Codec { } func MakeTestTxConfig() client.TxConfig { + enabledSignModes := []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT, + signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signingtypes.SignMode_SIGN_MODE_TEXTUAL, + } initClientCtx := client.Context{} txConfigOpts := tx.ConfigOptions{ + EnabledSignModes: enabledSignModes, TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), } ir, err := codectypes.NewInterfaceRegistryWithOptions(codectypes.InterfaceRegistryOptions{ @@ -73,7 +80,7 @@ func Test_sign(t *testing.T) { TxConfig: MakeTestTxConfig(), AddressCodec: address.NewBech32Codec("cosmos"), }, - fromName: "test", + fromName: "direct", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, }, @@ -86,7 +93,7 @@ func Test_sign(t *testing.T) { TxConfig: MakeTestTxConfig(), AddressCodec: address.NewBech32Codec("cosmos"), }, - fromName: "test", + fromName: "textual", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, }, @@ -99,7 +106,7 @@ func Test_sign(t *testing.T) { TxConfig: MakeTestTxConfig(), AddressCodec: address.NewBech32Codec("cosmos"), }, - fromName: "test", + fromName: "legacy", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, }, @@ -107,7 +114,7 @@ func Test_sign(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := k.NewAccount(tt.args.fromName, testdata.TestMnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) + _, err := k.NewAccount(tt.args.fromName, mnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) require.NoError(t, err) got, err := sign(tt.args.ctx, tt.args.fromName, tt.args.digest, tt.args.signMode) diff --git a/client/v2/offchain/signature.go b/client/v2/offchain/signature.go index 3d981e8ba593..d7b9769de983 100644 --- a/client/v2/offchain/signature.go +++ b/client/v2/offchain/signature.go @@ -20,7 +20,7 @@ type SingleSignatureData struct { Signature []byte } -type SignatureV2 struct { +type OffchainSignature struct { // PubKey is the public key to use for verifying the signature PubKey cryptotypes.PubKey diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 9644f088e323..8f51382d638d 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -48,7 +48,7 @@ func verify(ctx client.Context, tx *apitx.Tx) error { return err } - sigs, err := sigTx.GetSignaturesV2() + sigs, err := sigTx.GetSignatures() if err != nil { return err } @@ -80,6 +80,7 @@ func verify(ctx client.Context, tx *apitx.Tx) error { if err != nil { return err } + txSignerData := txsigning.SignerData{ ChainID: signingData.ChainID, AccountNumber: signingData.AccountNumber, diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 7bb77d0c3028..bbb0462b0e77 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -11,9 +11,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/testutil/testdata" ) +const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" + func Test_verify(t *testing.T) { k := keyring.NewInMemory(getCodec()) tests := []struct { @@ -50,7 +51,7 @@ func Test_verify(t *testing.T) { }, { name: "signMode legacyAmino", - fromName: "textualKey", + fromName: "legacyKey", digest: "hello world", signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, ctx: client.Context{ @@ -63,7 +64,7 @@ func Test_verify(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := k.NewAccount(tt.fromName, testdata.TestMnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) + _, err := k.NewAccount(tt.fromName, mnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) require.NoError(t, err) tx, err := sign(tt.ctx, tt.fromName, tt.digest, tt.signMode) From 73becd58f9cab9e9e1cbb46f107629bf142c1dc6 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Wed, 13 Dec 2023 17:53:24 +0100 Subject: [PATCH 07/33] update: encoding, flags --- client/v2/offchain/builder.go | 41 -------------------- client/v2/offchain/cli.go | 25 +++++++++++-- client/v2/offchain/encode.go | 28 ++++++++++++++ client/v2/offchain/encode_test.go | 62 +++++++++++++++++++++++++++++++ client/v2/offchain/sign.go | 27 ++++---------- client/v2/offchain/sign_test.go | 42 +++++++++++++++++++++ client/v2/offchain/verify.go | 36 +++++------------- 7 files changed, 169 insertions(+), 92 deletions(-) create mode 100644 client/v2/offchain/encode.go create mode 100644 client/v2/offchain/encode_test.go diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index cb4d6e06ee45..e5e75420c14b 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -223,20 +223,6 @@ func (b *builder) getSigners() ([][]byte, []protov2.Message, error) { } } - // ensure any specified fee payer is included in the required signers (at the end) - feePayer := b.tx.AuthInfo.Fee.Payer - var feePayerAddr []byte - if feePayer != "" { - var err error - if err != nil { - return nil, nil, err - } - } - if feePayerAddr != nil && !seen[string(feePayerAddr)] { - signers = append(signers, feePayerAddr) - seen[string(feePayerAddr)] = true - } - return signers, msgsv2, nil } @@ -303,33 +289,6 @@ func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.Mode Single: &apitx.ModeInfo_Single{Mode: data.SignMode}, }, }, data.Signature - - // TODO: multisig - // case *signing.MultiSignatureData: - // n := len(data.Signatures) - // modeInfos := make([]*apitx.ModeInfo, n) - // sigs := make([][]byte, n) - // - // for i, d := range data.Signatures { - // modeInfos[i], sigs[i] = SignatureDataToModeInfoAndSig(d) - // } - // - // multisig := cryptotypes.MultiSignature{ - // Signatures: sigs, - // } - // sig, err := multisig.Marshal() - // if err != nil { - // panic(err) - // } - // - // return &apitx.ModeInfo{ - // Sum: &apitx.ModeInfo_Multi_{ - // Multi: &apitx.ModeInfo_Multi{ - // Bitarray: data.BitArray, - // ModeInfos: modeInfos, - // }, - // }, - // }, sig default: panic(fmt.Sprintf("unexpected signature data type %T", data)) } diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 9bc055596770..ef8d268e3d3b 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -11,6 +11,12 @@ import ( // TODO: auto-cli +const ( + flagNotEmitUnpopulated = "notEmitUnpopulated" + flagIndent = "indent" + flagEncoding = "encoding" +) + // OffChain off-chain utilities func OffChain() *cobra.Command { cmd := &cobra.Command{ @@ -24,7 +30,6 @@ func OffChain() *cobra.Command { VerifyFile(), ) - cmd.PersistentFlags().String(flags.FlagOutput, "text", "Output format (text|json)") cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") flags.AddKeyringFlags(cmd.PersistentFlags()) return cmd @@ -32,7 +37,7 @@ func OffChain() *cobra.Command { // SignFile sign a file with a key func SignFile() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "sign ", Short: "Sign a file", Long: "Sign a file using a key.", @@ -46,8 +51,11 @@ func SignFile() *cobra.Command { } signmode, _ := cmd.Flags().GetString(flags.FlagSignMode) + notEmitUnpopulated, _ := cmd.Flags().GetBool(flagNotEmitUnpopulated) + indent, _ := cmd.Flags().GetString(flagIndent) + encoding, _ := cmd.Flags().GetString(flagEncoding) - signedTx, err := Sign(clientCtx, bz, args[0], signmode) + signedTx, err := Sign(clientCtx, bz, args[0], signmode, indent, encoding, !notEmitUnpopulated) if err != nil { return err } @@ -56,6 +64,11 @@ func SignFile() *cobra.Command { return nil }, } + + cmd.PersistentFlags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") + cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx. Default ' ' (two spaces)") + cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64). Default no-encoding") + return cmd } // VerifyFile sign a file with a key @@ -76,7 +89,11 @@ func VerifyFile() *cobra.Command { return err } - return Verify(clientCtx, bz) + err = Verify(clientCtx, bz) + if err == nil { + cmd.Println("Verification OK!") + } + return err }, } } diff --git a/client/v2/offchain/encode.go b/client/v2/offchain/encode.go new file mode 100644 index 000000000000..26f5ac37a4b4 --- /dev/null +++ b/client/v2/offchain/encode.go @@ -0,0 +1,28 @@ +package offchain + +import "encoding/base64" + +const ( + noEncoder = "no-encoding" + b64Encoder = "base64" +) + +type encodingFunc = func([]byte) (string, error) + +func noEncoding(digest []byte) (string, error) { + return string(digest), nil +} + +func base64Encoding(digest []byte) (string, error) { + return base64.StdEncoding.EncodeToString(digest), nil +} + +func getEncoder(encoder string) encodingFunc { + switch encoder { + case noEncoder: + return noEncoding + case b64Encoder: + return base64Encoding + } + return noEncoding +} diff --git a/client/v2/offchain/encode_test.go b/client/v2/offchain/encode_test.go new file mode 100644 index 000000000000..ffce2ed0dc1e --- /dev/null +++ b/client/v2/offchain/encode_test.go @@ -0,0 +1,62 @@ +package offchain + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_EncodingFuncs(t *testing.T) { + tests := []struct { + name string + encodeFunc encodingFunc + digest []byte + want string + }{ + { + name: "No encoding", + encodeFunc: noEncoding, + digest: []byte("Hello!"), + want: "Hello!", + }, + { + name: "base64 encoding", + encodeFunc: base64Encoding, + digest: []byte("Hello!"), + want: "SGVsbG8h", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.encodeFunc(tt.digest) + require.NoError(t, err) + require.Equal(t, got, tt.want) + }) + } +} + +func Test_getEncoder(t *testing.T) { + tests := []struct { + name string + encoder string + want encodingFunc + }{ + { + name: "no encoding", + encoder: "no-encoding", + want: noEncoding, + }, + { + name: "base64", + encoder: "base64", + want: base64Encoding, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getEncoder(tt.encoder) + require.Equal(t, reflect.ValueOf(got).Pointer(), reflect.ValueOf(tt.want).Pointer()) + }) + } +} diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 23984c6c63da..777a47375f06 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -28,12 +28,6 @@ const ( ExpectedSequence = 0 ) -type encodingFunc = func([]byte) (string, error) - -func noEncoding(digest []byte) (string, error) { - return string(digest), nil -} - func getSignMode(signModeStr string) apisigning.SignMode { signMode := apisigning.SignMode_SIGN_MODE_UNSPECIFIED switch signModeStr { @@ -49,8 +43,8 @@ func getSignMode(signModeStr string) apisigning.SignMode { return signMode } -func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (string, error) { - digest, err := noEncoding(rawBytes) +func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding string, emitUnpopulated bool) (string, error) { + digest, err := getEncoder(encoding)(rawBytes) if err != nil { return "", err } @@ -60,7 +54,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode string) (strin return "", err } - return marshalOffChainTx(tx, true, " ") + return marshalOffChainTx(tx, emitUnpopulated, indent) } func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { @@ -99,14 +93,14 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM PubKey: pubKey, } - sigData := SingleSignatureData{ + sigData := &SingleSignatureData{ SignMode: signMode, Signature: nil, } sig := OffchainSignature{ PubKey: pubKey, - Data: &sigData, + Data: sigData, Sequence: ExpectedSequence, } @@ -128,15 +122,8 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM return nil, err } - sigData = SingleSignatureData{ - SignMode: signMode, - Signature: signedBytes, - } - sig = OffchainSignature{ - PubKey: pubKey, - Data: &sigData, - Sequence: ExpectedSequence, - } + sigData.Signature = signedBytes + err = txBuilder.SetSignatures(sig) if err != nil { return nil, err diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index f7c07d5fef22..0d6f0e3ace33 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -3,6 +3,7 @@ package offchain import ( "testing" + "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" @@ -12,6 +13,7 @@ import ( "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/codec/testutil" @@ -60,6 +62,46 @@ func MakeTestTxConfig() client.TxConfig { return txConfig } +func Test_getSignMode(t *testing.T) { + tests := []struct { + name string + signModeStr string + want apitxsigning.SignMode + }{ + { + name: "direct", + signModeStr: flags.SignModeDirect, + want: apitxsigning.SignMode_SIGN_MODE_DIRECT, + }, + { + name: "legacy Amino JSON", + signModeStr: flags.SignModeLegacyAminoJSON, + want: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + }, + { + name: "direct Aux", + signModeStr: flags.SignModeDirectAux, + want: apitxsigning.SignMode_SIGN_MODE_DIRECT_AUX, + }, + { + name: "textual", + signModeStr: flags.SignModeTextual, + want: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, + }, + { + name: "unspecified", + signModeStr: "", + want: apitxsigning.SignMode_SIGN_MODE_UNSPECIFIED, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getSignMode(tt.signModeStr) + require.Equal(t, got, tt.want) + }) + } +} + func Test_sign(t *testing.T) { k := keyring.NewInMemory(getCodec()) type args struct { diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 8f51382d638d..6def8442ed7d 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -19,20 +19,12 @@ import ( ) func Verify(ctx client.Context, digest []byte) error { - tx := &apitx.Tx{} - - err := protojson.Unmarshal(digest, tx) - if err != nil { - return err - } - - err = verify(ctx, tx) + tx, err := unmarshall(digest) if err != nil { return err } - fmt.Printf("Verification OK") - return nil + return verify(ctx, tx) } func verify(ctx client.Context, tx *apitx.Tx) error { @@ -101,6 +93,13 @@ func verify(ctx client.Context, tx *apitx.Tx) error { return nil } +// unmarshall a digest to a Tx using protobuf protojson +func unmarshall(digest []byte) (*apitx.Tx, error) { + tx := &apitx.Tx{} + err := protojson.Unmarshal(digest, tx) + return tx, err +} + // verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing // modes. It differs from VerifySignature in that it uses the new txsigning.TxData interface in x/tx. func verifySignature( @@ -121,23 +120,6 @@ func verifySignature( return fmt.Errorf("unable to verify single signer signature") } return nil - - //case *signing.MultiSignatureData: - // multiPK, ok := pubKey.(multisig.PubKey) - // if !ok { - // return fmt.Errorf("expected %T, got %T", (multisig.PubKey)(nil), pubKey) - // } - // err := multiPK.VerifyMultisignature(func(mode signing.SignMode) ([]byte, error) { - // signMode, err := internalSignModeToAPI(mode) - // if err != nil { - // return nil, err - // } - // return handler.GetSignBytes(ctx, signMode, signerData, txData) - // }, data) - // if err != nil { - // return err - // } - // return nil default: return fmt.Errorf("unexpected SignatureData %T", signatureData) } From 08f459a2999348d9af5ac988d19d6bd5fed9cdb1 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 14 Dec 2023 13:19:12 +0100 Subject: [PATCH 08/33] add: tests --- client/v2/offchain/builder.go | 2 +- client/v2/offchain/cli.go | 28 +++++----- client/v2/offchain/sign_test.go | 33 +++++++++-- client/v2/offchain/verify.go | 5 +- client/v2/offchain/verify_test.go | 91 ++++++++++++++++++------------- 5 files changed, 97 insertions(+), 62 deletions(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index e5e75420c14b..831bbd3fa2de 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -276,7 +276,7 @@ func (b *builder) SetSignatures(signatures ...OffchainSignature) error { return nil } -// SignatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature +// signatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.ModeInfo, []byte) { if data == nil { return nil, nil diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index ef8d268e3d3b..f9a7bf3378ba 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -9,8 +9,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" ) -// TODO: auto-cli - const ( flagNotEmitUnpopulated = "notEmitUnpopulated" flagIndent = "indent" @@ -21,8 +19,8 @@ const ( func OffChain() *cobra.Command { cmd := &cobra.Command{ Use: "offchain", - Short: "offchain msg utilities", - Long: ``, + Short: "Off-chain utilities.", + Long: `Utilities for off-chain data.`, } cmd.AddCommand( @@ -30,17 +28,16 @@ func OffChain() *cobra.Command { VerifyFile(), ) - cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") flags.AddKeyringFlags(cmd.PersistentFlags()) return cmd } -// SignFile sign a file with a key +// SignFile signs a file with a key func SignFile() *cobra.Command { cmd := &cobra.Command{ - Use: "sign ", - Short: "Sign a file", - Long: "Sign a file using a key.", + Use: "sign-file ", + Short: "Sign a file.", + Long: "Sign a file using a given key.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) @@ -65,18 +62,19 @@ func SignFile() *cobra.Command { }, } + cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx") cmd.PersistentFlags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") - cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx. Default ' ' (two spaces)") - cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64). Default no-encoding") + cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") + cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") return cmd } -// VerifyFile sign a file with a key +// VerifyFile verifies given file with given key func VerifyFile() *cobra.Command { return &cobra.Command{ - Use: "verify ", - Short: "", - Long: "", + Use: "verify-file ", + Short: "Verify a file.", + Long: "Verify a previously signed file with the given key.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 0d6f0e3ace33..7af35cdae0ab 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -3,7 +3,6 @@ package offchain import ( "testing" - "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" @@ -111,8 +110,9 @@ func Test_sign(t *testing.T) { signMode apitxsigning.SignMode } tests := []struct { - name string - args args + name string + args args + wantErr bool }{ { name: "signMode direct", @@ -126,6 +126,7 @@ func Test_sign(t *testing.T) { digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, }, + wantErr: false, }, { name: "signMode textual", @@ -139,9 +140,10 @@ func Test_sign(t *testing.T) { digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, }, + wantErr: false, }, { - name: "signMode LegacyAmino", + name: "signMode legacyAmino", args: args{ ctx: client.Context{ Keyring: k, @@ -152,6 +154,21 @@ func Test_sign(t *testing.T) { digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, }, + wantErr: false, + }, + { + name: "signMode direct aux", + args: args{ + ctx: client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + AddressCodec: address.NewBech32Codec("cosmos"), + }, + fromName: "direct-aux", + digest: "Hello world!", + signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT_AUX, + }, + wantErr: true, }, } for _, tt := range tests { @@ -160,8 +177,12 @@ func Test_sign(t *testing.T) { require.NoError(t, err) got, err := sign(tt.args.ctx, tt.args.fromName, tt.args.digest, tt.args.signMode) - require.NoError(t, err) - require.NotNil(t, got) + if !tt.wantErr { + require.NoError(t, err) + require.NotNil(t, got) + } else { + require.Error(t, err) + } }) } } diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 6def8442ed7d..f63a6f218b26 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -93,15 +93,14 @@ func verify(ctx client.Context, tx *apitx.Tx) error { return nil } -// unmarshall a digest to a Tx using protobuf protojson +// unmarshall unmarshalls a digest to a Tx using protobuf protojson func unmarshall(digest []byte) (*apitx.Tx, error) { tx := &apitx.Tx{} err := protojson.Unmarshal(digest, tx) return tx, err } -// verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing -// modes. It differs from VerifySignature in that it uses the new txsigning.TxData interface in x/tx. +// verifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes. func verifySignature( ctx context.Context, pubKey cryptotypes.PubKey, diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index bbb0462b0e77..fcc871144db0 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -5,8 +5,7 @@ import ( "github.com/stretchr/testify/require" - apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" - + _ "cosmossdk.io/api/cosmos/crypto/secp256k1" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -15,63 +14,81 @@ import ( const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" -func Test_verify(t *testing.T) { +func Test_Verify(t *testing.T) { + fromName := "verifyTest" + k := keyring.NewInMemory(getCodec()) + _, err := k.NewAccount(fromName, mnemonic, "", "m/44'/118'/0'/0/0", hd.Secp256k1) + require.NoError(t, err) + + ctx := client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + Codec: getCodec(), + AddressCodec: address.NewBech32Codec("cosmos"), + } + tests := []struct { name string fromName string - digest string - signMode apitxsigning.SignMode + digest []byte ctx client.Context wantErr bool }{ { name: "signMode direct", - fromName: "directKey", - digest: "hello world", - signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + fromName: fromName, + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + ctx: ctx, }, { name: "signMode textual", - fromName: "textualKey", - digest: "hello world", - signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + fromName: fromName, + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"sfJ8ymLx+rA4BUlSoWOpO0pLAphvPwif5ztHqJVHdlQ3MKp+N5SXfgAPEEhBRyitS8mi/Y7NBr9TIEpjHFr12A==\"]}"), + ctx: ctx, }, { name: "signMode legacyAmino", - fromName: "legacyKey", - digest: "hello world", - signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - Codec: getCodec(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + fromName: fromName, + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"jn/RsMB79ZLSYPJaXVpZLZDt/gJ+4bNLLfAU0Vspj35+/DXBTj+GFdMvWWrc3emeIbYVgbgwaxghNnlim6JYNA==\"]}"), + ctx: ctx, + }, + { + name: "wrong signer", + fromName: fromName, + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + ctx: ctx, + wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := k.NewAccount(tt.fromName, mnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) - require.NoError(t, err) - tx, err := sign(tt.ctx, tt.fromName, tt.digest, tt.signMode) - require.NoError(t, err) + err = Verify(tt.ctx, tt.digest) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} - err = verify(tt.ctx, tx) +func Test_unmarshall(t *testing.T) { + tests := []struct { + name string + digest []byte + }{ + { + name: "check", + digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData","appDomain":"simd","signer":"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped","data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}],"memo":"","timeoutHeight":"0","extensionOptions":[],"nonCriticalExtensionOptions":[]},"authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A06XPD6ML7BHSWHsc2u5EtkCXzsCNmhgPaDdNCp5nPF2"},"modeInfo":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"0"}],"fee":{"amount":[],"gasLimit":"0","payer":"","granter":""},"tip":null},"signatures":["hx8Qo6xZ/Ie0d1TFtiVxSK1rUsRKDEiv1IdcgbkSGYgePYZl6aHJxpSxQDXdIeoZiPeIdrsTkkgjmH4wv2BBdw=="]}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := unmarshall(tt.digest) require.NoError(t, err) + require.NotNil(t, got) }) } } From 22866b64e87e3c5efc84e13075af45e931717f82 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 14 Dec 2023 16:33:11 +0100 Subject: [PATCH 09/33] update: docs --- client/v2/offchain/cli.go | 6 +++--- client/v2/offchain/sign.go | 5 +++++ client/v2/offchain/verify.go | 8 +++++--- client/v2/offchain/verify_test.go | 6 +++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index f9a7bf3378ba..85a611512622 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -15,7 +15,7 @@ const ( flagEncoding = "encoding" ) -// OffChain off-chain utilities +// OffChain off-chain utilities. func OffChain() *cobra.Command { cmd := &cobra.Command{ Use: "offchain", @@ -32,7 +32,7 @@ func OffChain() *cobra.Command { return cmd } -// SignFile signs a file with a key +// SignFile signs a file with a key. func SignFile() *cobra.Command { cmd := &cobra.Command{ Use: "sign-file ", @@ -69,7 +69,7 @@ func SignFile() *cobra.Command { return cmd } -// VerifyFile verifies given file with given key +// VerifyFile verifies given file with given key. func VerifyFile() *cobra.Command { return &cobra.Command{ Use: "verify-file ", diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 777a47375f06..16f0f7cf94d0 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -28,6 +28,7 @@ const ( ExpectedSequence = 0 ) +// getSignMode returns the expected SignMode. func getSignMode(signModeStr string) apisigning.SignMode { signMode := apisigning.SignMode_SIGN_MODE_UNSPECIFIED switch signModeStr { @@ -43,6 +44,7 @@ func getSignMode(signModeStr string) apisigning.SignMode { return signMode } +// Sign signs given bytes using the specified encoder and SignMode. func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding string, emitUnpopulated bool) (string, error) { digest, err := getEncoder(encoding)(rawBytes) if err != nil { @@ -57,6 +59,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encod return marshalOffChainTx(tx, emitUnpopulated, indent) } +// sign signs a digest with provided key and SignMode. func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { keybase, err := keyring.NewAutoCLIKeyring(ctx.Keyring) if err != nil { @@ -132,6 +135,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM return txBuilder.GetProtoTx(), nil } +// getSignBytes gets the bytes to be signed for the given Tx and SignMode. func getSignBytes(ctx context.Context, handlerMap *txsigning.HandlerMap, mode apisigning.SignMode, @@ -159,6 +163,7 @@ func getSignBytes(ctx context.Context, return handlerMap.GetSignBytes(ctx, mode, txSignerData, txData) } +// marshalOffChainTx marshals a Tx using protobuf protojson. func marshalOffChainTx(tx *apitx.Tx, emitUnpopulated bool, indent string) (string, error) { bytesTx, err := protojson.MarshalOptions{ EmitUnpopulated: emitUnpopulated, diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index f63a6f218b26..676104450832 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -18,8 +18,9 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) +// Verify verifies a digest after unmarshalling it. func Verify(ctx client.Context, digest []byte) error { - tx, err := unmarshall(digest) + tx, err := unmarshal(digest) if err != nil { return err } @@ -27,6 +28,7 @@ func Verify(ctx client.Context, digest []byte) error { return verify(ctx, tx) } +// verify verifies given Tx. func verify(ctx client.Context, tx *apitx.Tx) error { sigTx := builder{ cdc: ctx.Codec, @@ -93,8 +95,8 @@ func verify(ctx client.Context, tx *apitx.Tx) error { return nil } -// unmarshall unmarshalls a digest to a Tx using protobuf protojson -func unmarshall(digest []byte) (*apitx.Tx, error) { +// unmarshal unmarshalls a digest to a Tx using protobuf protojson. +func unmarshal(digest []byte) (*apitx.Tx, error) { tx := &apitx.Tx{} err := protojson.Unmarshal(digest, tx) return tx, err diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index fcc871144db0..b8657eb1dbe8 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" _ "cosmossdk.io/api/cosmos/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -63,7 +64,6 @@ func Test_Verify(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err = Verify(tt.ctx, tt.digest) if tt.wantErr { require.Error(t, err) @@ -74,7 +74,7 @@ func Test_Verify(t *testing.T) { } } -func Test_unmarshall(t *testing.T) { +func Test_unmarshal(t *testing.T) { tests := []struct { name string digest []byte @@ -86,7 +86,7 @@ func Test_unmarshall(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := unmarshall(tt.digest) + got, err := unmarshal(tt.digest) require.NoError(t, err) require.NotNil(t, got) }) From 029bcc2c3b8cbfc05e4236dc4bffc745dc46564f Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 12:22:07 +0100 Subject: [PATCH 10/33] add: output format --- client/v2/offchain/cli.go | 16 ++++- client/v2/offchain/encode.go | 3 + client/v2/offchain/marshal.go | 43 ++++++++++++++ client/v2/offchain/sign.go | 23 +++----- client/v2/offchain/sign_test.go | 31 ++++------ client/v2/offchain/verify.go | 16 +++-- client/v2/offchain/verify_test.go | 97 +++++++++++++++++++------------ 7 files changed, 149 insertions(+), 80 deletions(-) create mode 100644 client/v2/offchain/marshal.go diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 85a611512622..ff6009a84cbe 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -5,6 +5,8 @@ import ( "github.com/spf13/cobra" + v2flags "cosmossdk.io/client/v2/internal/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" ) @@ -13,6 +15,7 @@ const ( flagNotEmitUnpopulated = "notEmitUnpopulated" flagIndent = "indent" flagEncoding = "encoding" + flagFileFormat = "file-format" ) // OffChain off-chain utilities. @@ -51,8 +54,9 @@ func SignFile() *cobra.Command { notEmitUnpopulated, _ := cmd.Flags().GetBool(flagNotEmitUnpopulated) indent, _ := cmd.Flags().GetString(flagIndent) encoding, _ := cmd.Flags().GetString(flagEncoding) + outputFormat, _ := cmd.Flags().GetString(v2flags.FlagOutput) - signedTx, err := Sign(clientCtx, bz, args[0], signmode, indent, encoding, !notEmitUnpopulated) + signedTx, err := Sign(clientCtx, bz, args[0], signmode, indent, encoding, outputFormat, !notEmitUnpopulated) if err != nil { return err } @@ -63,6 +67,7 @@ func SignFile() *cobra.Command { } cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx") + cmd.PersistentFlags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") cmd.PersistentFlags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") @@ -71,7 +76,7 @@ func SignFile() *cobra.Command { // VerifyFile verifies given file with given key. func VerifyFile() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "verify-file ", Short: "Verify a file.", Long: "Verify a previously signed file with the given key.", @@ -87,11 +92,16 @@ func VerifyFile() *cobra.Command { return err } - err = Verify(clientCtx, bz) + fileFormat, _ := cmd.Flags().GetString(flagFileFormat) + + err = Verify(clientCtx, bz, fileFormat) if err == nil { cmd.Println("Verification OK!") } return err }, } + + cmd.PersistentFlags().String(flagFileFormat, "json", "Choose whats the file format to be verified (json|text)") + return cmd } diff --git a/client/v2/offchain/encode.go b/client/v2/offchain/encode.go index 26f5ac37a4b4..dc7beea866ed 100644 --- a/client/v2/offchain/encode.go +++ b/client/v2/offchain/encode.go @@ -9,14 +9,17 @@ const ( type encodingFunc = func([]byte) (string, error) +// noEncoding returns a byte slice as a string func noEncoding(digest []byte) (string, error) { return string(digest), nil } +// base64Encoding returns a byte slice as a b64 encoded string func base64Encoding(digest []byte) (string, error) { return base64.StdEncoding.EncodeToString(digest), nil } +// getEncoder returns a encodingFunc bases on the encoder id provided func getEncoder(encoder string) encodingFunc { switch encoder { case noEncoder: diff --git a/client/v2/offchain/marshal.go b/client/v2/offchain/marshal.go new file mode 100644 index 000000000000..ae509f52b4e8 --- /dev/null +++ b/client/v2/offchain/marshal.go @@ -0,0 +1,43 @@ +package offchain + +import ( + "errors" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + + apitx "cosmossdk.io/api/cosmos/tx/v1beta1" + v2flags "cosmossdk.io/client/v2/internal/flags" +) + +// marshaller marshals Messages. +type marshaller interface { + Marshal(message proto.Message) ([]byte, error) +} + +// getMarshaller returns the marshaller for the given marshaller id. +func getMarshaller(marshallerId, indent string, emitUnpopulated bool) (marshaller, error) { + switch marshallerId { + case v2flags.OutputFormatJSON: + return protojson.MarshalOptions{ + Indent: indent, + EmitUnpopulated: emitUnpopulated, + }, nil + case v2flags.OutputFormatText: + return prototext.MarshalOptions{ + Indent: indent, + EmitUnknown: emitUnpopulated, + }, nil + } + return nil, errors.New("marshaller not identified") +} + +// marshalOffChainTx marshals a Tx using given marshaller. +func marshalOffChainTx(tx *apitx.Tx, marshaller marshaller) (string, error) { + bytesTx, err := marshaller.Marshal(tx) + if err != nil { + return "", err + } + return string(bytesTx), nil +} diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 16f0f7cf94d0..917835041a35 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -2,8 +2,6 @@ package offchain import ( "context" - - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/anypb" apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" @@ -45,7 +43,7 @@ func getSignMode(signModeStr string) apisigning.SignMode { } // Sign signs given bytes using the specified encoder and SignMode. -func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding string, emitUnpopulated bool) (string, error) { +func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding, output string, emitUnpopulated bool) (string, error) { digest, err := getEncoder(encoding)(rawBytes) if err != nil { return "", err @@ -56,7 +54,12 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encod return "", err } - return marshalOffChainTx(tx, emitUnpopulated, indent) + txMarshaller, err := getMarshaller(output, indent, emitUnpopulated) + if err != nil { + return "", err + } + + return marshalOffChainTx(tx, txMarshaller) } // sign signs a digest with provided key and SignMode. @@ -162,15 +165,3 @@ func getSignBytes(ctx context.Context, return handlerMap.GetSignBytes(ctx, mode, txSignerData, txData) } - -// marshalOffChainTx marshals a Tx using protobuf protojson. -func marshalOffChainTx(tx *apitx.Tx, emitUnpopulated bool, indent string) (string, error) { - bytesTx, err := protojson.MarshalOptions{ - EmitUnpopulated: emitUnpopulated, - Indent: indent, - }.Marshal(tx) - if err != nil { - return "", err - } - return string(bytesTx), nil -} diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 7af35cdae0ab..ea2c44992d21 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -103,6 +103,13 @@ func Test_getSignMode(t *testing.T) { func Test_sign(t *testing.T) { k := keyring.NewInMemory(getCodec()) + + ctx := client.Context{ + Keyring: k, + TxConfig: MakeTestTxConfig(), + AddressCodec: address.NewBech32Codec("cosmos"), + } + type args struct { ctx client.Context fromName string @@ -117,11 +124,7 @@ func Test_sign(t *testing.T) { { name: "signMode direct", args: args{ - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + ctx: ctx, fromName: "direct", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, @@ -131,11 +134,7 @@ func Test_sign(t *testing.T) { { name: "signMode textual", args: args{ - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + ctx: ctx, fromName: "textual", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, @@ -145,11 +144,7 @@ func Test_sign(t *testing.T) { { name: "signMode legacyAmino", args: args{ - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + ctx: ctx, fromName: "legacy", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, @@ -159,11 +154,7 @@ func Test_sign(t *testing.T) { { name: "signMode direct aux", args: args{ - ctx: client.Context{ - Keyring: k, - TxConfig: MakeTestTxConfig(), - AddressCodec: address.NewBech32Codec("cosmos"), - }, + ctx: ctx, fromName: "direct-aux", digest: "Hello world!", signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT_AUX, diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 676104450832..568a2751bc40 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -3,8 +3,10 @@ package offchain import ( "bytes" "context" + v2flags "cosmossdk.io/client/v2/internal/flags" "errors" "fmt" + "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/anypb" @@ -19,8 +21,8 @@ import ( ) // Verify verifies a digest after unmarshalling it. -func Verify(ctx client.Context, digest []byte) error { - tx, err := unmarshal(digest) +func Verify(ctx client.Context, digest []byte, fileFormat string) error { + tx, err := unmarshal(digest, fileFormat) if err != nil { return err } @@ -96,9 +98,15 @@ func verify(ctx client.Context, tx *apitx.Tx) error { } // unmarshal unmarshalls a digest to a Tx using protobuf protojson. -func unmarshal(digest []byte) (*apitx.Tx, error) { +func unmarshal(digest []byte, fileFormat string) (*apitx.Tx, error) { + var err error tx := &apitx.Tx{} - err := protojson.Unmarshal(digest, tx) + switch fileFormat { + case v2flags.OutputFormatJSON: + err = protojson.Unmarshal(digest, tx) + case v2flags.OutputFormatText: + err = prototext.Unmarshal(digest, tx) + } return tx, err } diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index b8657eb1dbe8..5a043a2566ab 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -9,62 +9,78 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" ) const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" func Test_Verify(t *testing.T) { - fromName := "verifyTest" - - k := keyring.NewInMemory(getCodec()) - _, err := k.NewAccount(fromName, mnemonic, "", "m/44'/118'/0'/0/0", hd.Secp256k1) - require.NoError(t, err) - ctx := client.Context{ - Keyring: k, TxConfig: MakeTestTxConfig(), Codec: getCodec(), AddressCodec: address.NewBech32Codec("cosmos"), } tests := []struct { - name string - fromName string - digest []byte - ctx client.Context - wantErr bool + name string + digest []byte + fileFormat string + ctx client.Context + wantErr bool }{ { - name: "signMode direct", - fromName: fromName, - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), - ctx: ctx, + name: "signMode direct json", + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + fileFormat: "json", + ctx: ctx, + }, + { + name: "signMode textual json", + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"sfJ8ymLx+rA4BUlSoWOpO0pLAphvPwif5ztHqJVHdlQ3MKp+N5SXfgAPEEhBRyitS8mi/Y7NBr9TIEpjHFr12A==\"]}"), + fileFormat: "json", + ctx: ctx, + }, + { + name: "signMode legacyAmino json", + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"jn/RsMB79ZLSYPJaXVpZLZDt/gJ+4bNLLfAU0Vspj35+/DXBTj+GFdMvWWrc3emeIbYVgbgwaxghNnlim6JYNA==\"]}"), + fileFormat: "json", + ctx: ctx, }, { - name: "signMode textual", - fromName: fromName, - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"sfJ8ymLx+rA4BUlSoWOpO0pLAphvPwif5ztHqJVHdlQ3MKp+N5SXfgAPEEhBRyitS8mi/Y7NBr9TIEpjHFr12A==\"]}"), - ctx: ctx, + name: "wrong signer json", + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + fileFormat: "json", + ctx: ctx, + wantErr: true, }, { - name: "signMode legacyAmino", - fromName: fromName, - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"jn/RsMB79ZLSYPJaXVpZLZDt/gJ+4bNLLfAU0Vspj35+/DXBTj+GFdMvWWrc3emeIbYVgbgwaxghNnlim6JYNA==\"]}"), - ctx: ctx, + name: "signMode direct text", + digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0 Date: Fri, 15 Dec 2023 12:30:43 +0100 Subject: [PATCH 11/33] fix: lint --- client/v2/offchain/sign.go | 1 + client/v2/offchain/verify.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 917835041a35..a35b42f41aee 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -2,6 +2,7 @@ package offchain import ( "context" + "google.golang.org/protobuf/types/known/anypb" apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 568a2751bc40..513539503814 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -3,12 +3,13 @@ package offchain import ( "bytes" "context" - v2flags "cosmossdk.io/client/v2/internal/flags" "errors" "fmt" - "google.golang.org/protobuf/encoding/prototext" + + v2flags "cosmossdk.io/client/v2/internal/flags" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/types/known/anypb" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" From f0289a560288680b98f01ffef9b44a7014a7fdb8 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 12:44:02 +0100 Subject: [PATCH 12/33] fix --- client/v2/offchain/verify.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 513539503814..d89e53cd9cee 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -6,13 +6,12 @@ import ( "errors" "fmt" - v2flags "cosmossdk.io/client/v2/internal/flags" - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/types/known/anypb" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" + v2flags "cosmossdk.io/client/v2/internal/flags" authsigning "cosmossdk.io/x/auth/signing" txsigning "cosmossdk.io/x/tx/signing" From 8af522f8b4243259a84c17cd38016097c26a060a Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 13:03:51 +0100 Subject: [PATCH 13/33] add: changelog --- client/v2/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/client/v2/CHANGELOG.md b/client/v2/CHANGELOG.md index 2287e6bd07cc..090ff67e362e 100644 --- a/client/v2/CHANGELOG.md +++ b/client/v2/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [#18626](https://github.com/cosmos/cosmos-sdk/pull/18626) Support for off-chain signing and verification of a file. * [#18461](https://github.com/cosmos/cosmos-sdk/pull/18461) Support governance proposals. ### API Breaking Changes From edeb06bb4920c682d19c39f936b90308296af697 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 16:48:33 +0100 Subject: [PATCH 14/33] add: rabbit suggestions --- client/v2/offchain/builder.go | 24 ++++++++++++++---------- client/v2/offchain/encode.go | 14 +++++++++----- client/v2/offchain/encode_test.go | 3 ++- client/v2/offchain/marshal.go | 4 ++-- client/v2/offchain/sign.go | 14 +++++++++++--- client/v2/offchain/sign_test.go | 14 ++++++-------- client/v2/offchain/verify.go | 8 +++++++- client/v2/offchain/verify_test.go | 6 +++--- 8 files changed, 54 insertions(+), 33 deletions(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index 831bbd3fa2de..b9ed211bf227 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -48,7 +48,7 @@ func (b *builder) GetProtoTx() *apitx.Tx { return b.tx } -func (b *builder) GetSigningTxData() txsigning.TxData { +func (b *builder) GetSigningTxData() (txsigning.TxData, error) { body := b.tx.Body authInfo := b.tx.AuthInfo @@ -117,7 +117,7 @@ func (b *builder) GetSigningTxData() txsigning.TxData { } authInfoBz, err := protov2.Marshal(b.tx.AuthInfo) if err != nil { - panic(err) + return txsigning.TxData{}, err } bodyBz, err := protov2.Marshal(b.tx.Body) if err != nil { @@ -129,7 +129,7 @@ func (b *builder) GetSigningTxData() txsigning.TxData { Body: txBody, BodyBytes: bodyBz, } - return txData + return txData, nil } func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer already has pubkey in context, this list will have nil in its place @@ -231,7 +231,7 @@ func (b *builder) setMsgs(msgs ...proto.Message) error { for i, msg := range msgs { protoMsg, ok := msg.(protov2.Message) if !ok { - return errors.New("") + return errors.New("message is not a proto.Message") } protov2MarshalOpts := protov2.MarshalOptions{Deterministic: true} bz, err := protov2MarshalOpts.Marshal(protoMsg) @@ -251,15 +251,19 @@ func (b *builder) SetSignatures(signatures ...OffchainSignature) error { n := len(signatures) signerInfos := make([]*apitx.SignerInfo, n) rawSigs := make([][]byte, n) - + var err error for i, sig := range signatures { var mi *apitx.ModeInfo - mi, rawSigs[i] = b.signatureDataToModeInfoAndSig(sig.Data) + mi, rawSigs[i], err = b.signatureDataToModeInfoAndSig(sig.Data) + if err != nil { + return err + } pubKey, err := codectypes.NewAnyWithValue(sig.PubKey) if err != nil { return err } + signerInfos[i] = &apitx.SignerInfo{ PublicKey: &anypb.Any{ TypeUrl: pubKey.TypeUrl, @@ -277,9 +281,9 @@ func (b *builder) SetSignatures(signatures ...OffchainSignature) error { } // signatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature -func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.ModeInfo, []byte) { +func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.ModeInfo, []byte, error) { if data == nil { - return nil, nil + return nil, nil, errors.New("empty SignatureData") } switch data := data.(type) { @@ -288,9 +292,9 @@ func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.Mode Sum: &apitx.ModeInfo_Single_{ Single: &apitx.ModeInfo_Single{Mode: data.SignMode}, }, - }, data.Signature + }, data.Signature, nil default: - panic(fmt.Sprintf("unexpected signature data type %T", data)) + return nil, nil, fmt.Errorf("unexpected signature data type %T", data) } } diff --git a/client/v2/offchain/encode.go b/client/v2/offchain/encode.go index dc7beea866ed..835de603ae6f 100644 --- a/client/v2/offchain/encode.go +++ b/client/v2/offchain/encode.go @@ -1,6 +1,9 @@ package offchain -import "encoding/base64" +import ( + "encoding/base64" + "fmt" +) const ( noEncoder = "no-encoding" @@ -20,12 +23,13 @@ func base64Encoding(digest []byte) (string, error) { } // getEncoder returns a encodingFunc bases on the encoder id provided -func getEncoder(encoder string) encodingFunc { +func getEncoder(encoder string) (encodingFunc, error) { switch encoder { case noEncoder: - return noEncoding + return noEncoding, nil case b64Encoder: - return base64Encoding + return base64Encoding, nil + default: + return nil, fmt.Errorf("unknown encoder: %s", encoder) } - return noEncoding } diff --git a/client/v2/offchain/encode_test.go b/client/v2/offchain/encode_test.go index ffce2ed0dc1e..b68760abfd11 100644 --- a/client/v2/offchain/encode_test.go +++ b/client/v2/offchain/encode_test.go @@ -55,7 +55,8 @@ func Test_getEncoder(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := getEncoder(tt.encoder) + got, err := getEncoder(tt.encoder) + require.NoError(t, err) require.Equal(t, reflect.ValueOf(got).Pointer(), reflect.ValueOf(tt.want).Pointer()) }) } diff --git a/client/v2/offchain/marshal.go b/client/v2/offchain/marshal.go index ae509f52b4e8..8f90b9830d3f 100644 --- a/client/v2/offchain/marshal.go +++ b/client/v2/offchain/marshal.go @@ -1,7 +1,7 @@ package offchain import ( - "errors" + "fmt" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/prototext" @@ -30,7 +30,7 @@ func getMarshaller(marshallerId, indent string, emitUnpopulated bool) (marshalle EmitUnknown: emitUnpopulated, }, nil } - return nil, errors.New("marshaller not identified") + return nil, fmt.Errorf("marshaller with id '%s' not identified", marshallerId) } // marshalOffChainTx marshals a Tx using given marshaller. diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index a35b42f41aee..7611250df780 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -45,7 +45,12 @@ func getSignMode(signModeStr string) apisigning.SignMode { // Sign signs given bytes using the specified encoder and SignMode. func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding, output string, emitUnpopulated bool) (string, error) { - digest, err := getEncoder(encoding)(rawBytes) + encoder, err := getEncoder(encoding) + if err != nil { + return "", err + } + + digest, err := encoder(rawBytes) if err != nil { return "", err } @@ -144,9 +149,12 @@ func getSignBytes(ctx context.Context, handlerMap *txsigning.HandlerMap, mode apisigning.SignMode, signerData authsigning.SignerData, - tx authsigning.V2AdaptableTx, + tx *builder, ) ([]byte, error) { - txData := tx.GetSigningTxData() + txData, err := tx.GetSigningTxData() + if err != nil { + return nil, err + } anyPk, err := codectypes.NewAnyWithValue(signerData.PubKey) if err != nil { diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index ea2c44992d21..93faa2c70925 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -30,7 +30,7 @@ func getCodec() codec.Codec { return codec.NewProtoCodec(registry) } -func MakeTestTxConfig() client.TxConfig { +func MakeTestTxConfig(t *testing.T) client.TxConfig { enabledSignModes := []signingtypes.SignMode{ signingtypes.SignMode_SIGN_MODE_DIRECT, signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, @@ -49,15 +49,13 @@ func MakeTestTxConfig() client.TxConfig { ValidatorAddressCodec: address.NewBech32Codec("cosmosvaloper"), }, }) - if err != nil { - panic(err) - } + require.NoError(t, err) + cryptocodec.RegisterInterfaces(ir) cdc := codec.NewProtoCodec(ir) txConfig, err := tx.NewTxConfigWithOptions(cdc, txConfigOpts) - if err != nil { - panic(err) - } + require.NoError(t, err) + return txConfig } @@ -106,7 +104,7 @@ func Test_sign(t *testing.T) { ctx := client.Context{ Keyring: k, - TxConfig: MakeTestTxConfig(), + TxConfig: MakeTestTxConfig(t), AddressCodec: address.NewBech32Codec("cosmos"), } diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index d89e53cd9cee..32847422a477 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -88,7 +88,11 @@ func verify(ctx client.Context, tx *apitx.Tx) error { }, } - txData := sigTx.GetSigningTxData() + txData, err := sigTx.GetSigningTxData() + if err != nil { + return err + } + err = verifySignature(context.Background(), pubKey, txSignerData, sig.Data, signModeHandler, txData) if err != nil { return err @@ -106,6 +110,8 @@ func unmarshal(digest []byte, fileFormat string) (*apitx.Tx, error) { err = protojson.Unmarshal(digest, tx) case v2flags.OutputFormatText: err = prototext.Unmarshal(digest, tx) + default: + return nil, fmt.Errorf("unsupported file format: %s", fileFormat) } return tx, err } diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 5a043a2566ab..cce7e5f8355f 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -15,7 +15,7 @@ const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil aw func Test_Verify(t *testing.T) { ctx := client.Context{ - TxConfig: MakeTestTxConfig(), + TxConfig: MakeTestTxConfig(t), Codec: getCodec(), AddressCodec: address.NewBech32Codec("cosmos"), } @@ -97,12 +97,12 @@ func Test_unmarshal(t *testing.T) { fileFormat string }{ { - name: "check", + name: "json test", digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData","appDomain":"simd","signer":"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped","data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}],"memo":"","timeoutHeight":"0","extensionOptions":[],"nonCriticalExtensionOptions":[]},"authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A06XPD6ML7BHSWHsc2u5EtkCXzsCNmhgPaDdNCp5nPF2"},"modeInfo":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"0"}],"fee":{"amount":[],"gasLimit":"0","payer":"","granter":""},"tip":null},"signatures":["hx8Qo6xZ/Ie0d1TFtiVxSK1rUsRKDEiv1IdcgbkSGYgePYZl6aHJxpSxQDXdIeoZiPeIdrsTkkgjmH4wv2BBdw=="]}`), fileFormat: "json", }, { - name: "signMode direct text", + name: "text test", digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0 Date: Fri, 15 Dec 2023 16:52:20 +0100 Subject: [PATCH 15/33] fix: panic --- client/v2/offchain/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index b9ed211bf227..97ad52d336dc 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -121,7 +121,7 @@ func (b *builder) GetSigningTxData() (txsigning.TxData, error) { } bodyBz, err := protov2.Marshal(b.tx.Body) if err != nil { - panic(err) + return txsigning.TxData{}, err } txData := txsigning.TxData{ AuthInfo: txAuthInfo, From fa7fe5b729dcba6fe963a7d1643625955f4c7616 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 16:57:10 +0100 Subject: [PATCH 16/33] fix: panic --- client/v2/offchain/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index 97ad52d336dc..4d3e025261be 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -308,6 +308,6 @@ func modeInfoAndSigToSignatureData(modeInfo *apitx.ModeInfo, sig []byte) (Signat }, nil default: - panic(fmt.Errorf("unexpected ModeInfo data type %T", modeInfo)) + return nil, fmt.Errorf("unexpected ModeInfo data type %T", modeInfo) } } From 61db8664402654bf4689d715be1eda71fdb0d311 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 15 Dec 2023 17:12:39 +0100 Subject: [PATCH 17/33] fix: empty error --- client/v2/offchain/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 32847422a477..5ab44d822639 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -50,7 +50,7 @@ func verify(ctx client.Context, tx *apitx.Tx) error { } if len(sigs) != len(signers) { - return errors.New("") + return errors.New("mismatch between the number of signatures and signers") } for i, sig := range sigs { From ea790a7f4ee7a306a282fec658f719d9fd6d6c4c Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 19 Dec 2023 09:00:36 +0100 Subject: [PATCH 18/33] fix: t.Helper() --- client/v2/offchain/sign_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 93faa2c70925..58f14965eadf 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -31,6 +31,7 @@ func getCodec() codec.Codec { } func MakeTestTxConfig(t *testing.T) client.TxConfig { + t.Helper() enabledSignModes := []signingtypes.SignMode{ signingtypes.SignMode_SIGN_MODE_DIRECT, signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, From 9f33e1062dbe07518694f995319f77004298e376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Mon, 8 Jan 2024 09:25:35 +0100 Subject: [PATCH 19/33] Update client/v2/offchain/builder.go Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com> --- client/v2/offchain/builder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index 4d3e025261be..70bb779daf6d 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -22,7 +22,6 @@ import ( type builder struct { cdc codec.Codec - tx *apitx.Tx } From 5654037a7c227bbe5128ad0c39ae3ac1e35331f2 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 8 Jan 2024 09:36:21 +0100 Subject: [PATCH 20/33] godoc --- client/v2/offchain/builder.go | 15 ++++++++++----- client/v2/offchain/encode.go | 6 +++--- client/v2/offchain/sign.go | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/client/v2/offchain/builder.go b/client/v2/offchain/builder.go index 70bb779daf6d..c3a8f924ed01 100644 --- a/client/v2/offchain/builder.go +++ b/client/v2/offchain/builder.go @@ -22,7 +22,7 @@ import ( type builder struct { cdc codec.Codec - tx *apitx.Tx + tx *apitx.Tx } func newBuilder(cdc codec.Codec) *builder { @@ -43,10 +43,12 @@ func newBuilder(cdc codec.Codec) *builder { } } -func (b *builder) GetProtoTx() *apitx.Tx { +// GetTx returns the tx. +func (b *builder) GetTx() *apitx.Tx { return b.tx } +// GetSigningTxData returns the necessary data to generate sign bytes. func (b *builder) GetSigningTxData() (txsigning.TxData, error) { body := b.tx.Body authInfo := b.tx.AuthInfo @@ -131,6 +133,7 @@ func (b *builder) GetSigningTxData() (txsigning.TxData, error) { return txData, nil } +// GetPubKeys returns the pubKeys of the tx. func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer already has pubkey in context, this list will have nil in its place signerInfos := b.tx.AuthInfo.SignerInfos pks := make([]cryptotypes.PubKey, len(signerInfos)) @@ -156,6 +159,7 @@ func (b *builder) GetPubKeys() ([]cryptotypes.PubKey, error) { // If signer alre return pks, nil } +// GetSignatures returns the signatures of the tx. func (b *builder) GetSignatures() ([]OffchainSignature, error) { signerInfos := b.tx.AuthInfo.SignerInfos sigs := b.tx.Signatures @@ -185,13 +189,13 @@ func (b *builder) GetSignatures() ([]OffchainSignature, error) { Data: sigData, Sequence: nonce, } - } } return res, nil } +// GetSigners returns the signers of the tx. func (b *builder) GetSigners() ([][]byte, error) { signers, _, err := b.getSigners() return signers, err @@ -246,6 +250,7 @@ func (b *builder) setMsgs(msgs ...proto.Message) error { return nil } +// SetSignatures set the signatures of the tx. func (b *builder) SetSignatures(signatures ...OffchainSignature) error { n := len(signatures) signerInfos := make([]*apitx.SignerInfo, n) @@ -279,7 +284,7 @@ func (b *builder) SetSignatures(signatures ...OffchainSignature) error { return nil } -// signatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature +// signatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature. func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.ModeInfo, []byte, error) { if data == nil { return nil, nil, errors.New("empty SignatureData") @@ -297,7 +302,7 @@ func (b *builder) signatureDataToModeInfoAndSig(data SignatureData) (*apitx.Mode } } -// modeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData +// modeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData. func modeInfoAndSigToSignatureData(modeInfo *apitx.ModeInfo, sig []byte) (SignatureData, error) { switch modeInfoType := modeInfo.Sum.(type) { case *apitx.ModeInfo_Single_: diff --git a/client/v2/offchain/encode.go b/client/v2/offchain/encode.go index 835de603ae6f..0de885368a46 100644 --- a/client/v2/offchain/encode.go +++ b/client/v2/offchain/encode.go @@ -12,17 +12,17 @@ const ( type encodingFunc = func([]byte) (string, error) -// noEncoding returns a byte slice as a string +// noEncoding returns a byte slice as a string. func noEncoding(digest []byte) (string, error) { return string(digest), nil } -// base64Encoding returns a byte slice as a b64 encoded string +// base64Encoding returns a byte slice as a b64 encoded string. func base64Encoding(digest []byte) (string, error) { return base64.StdEncoding.EncodeToString(digest), nil } -// getEncoder returns a encodingFunc bases on the encoder id provided +// getEncoder returns a encodingFunc bases on the encoder id provided. func getEncoder(encoder string) (encodingFunc, error) { switch encoder { case noEncoder: diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 7611250df780..fa7d475aebbc 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -141,7 +141,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM return nil, err } - return txBuilder.GetProtoTx(), nil + return txBuilder.GetTx(), nil } // getSignBytes gets the bytes to be signed for the given Tx and SignMode. From 25815649a9991bb39e0abdc3893471b7435cae56 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 8 Jan 2024 11:02:39 +0100 Subject: [PATCH 21/33] fix: snake case in proto --- .../offchain/msgSignArbitraryData.proto | 2 +- .../offchain/msgSignArbitraryData.pulsar.go | 71 ++++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/client/v2/internal/offchain/msgSignArbitraryData.proto b/client/v2/internal/offchain/msgSignArbitraryData.proto index 145a33b7bfd6..4baa453f2355 100644 --- a/client/v2/internal/offchain/msgSignArbitraryData.proto +++ b/client/v2/internal/offchain/msgSignArbitraryData.proto @@ -11,7 +11,7 @@ message MsgSignArbitraryData { option (amino.name) = "offchain/MsgSignArbitraryData"; option (cosmos.msg.v1.signer) = "signer"; // AppDomain is the application requesting off-chain message signing - string appDomain = 1; + string app_domain = 1; // Signer is the sdk.AccAddress of the message signer string signer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // Data represents the raw bytes of the content that is signed (text, json, etc) diff --git a/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go index af14da5a5d13..d05e1b45f4c0 100644 --- a/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go +++ b/client/v2/internal/offchain/msgSignArbitraryData.pulsar.go @@ -16,16 +16,16 @@ import ( ) var ( - md_MsgSignArbitraryData protoreflect.MessageDescriptor - fd_MsgSignArbitraryData_appDomain protoreflect.FieldDescriptor - fd_MsgSignArbitraryData_signer protoreflect.FieldDescriptor - fd_MsgSignArbitraryData_data protoreflect.FieldDescriptor + md_MsgSignArbitraryData protoreflect.MessageDescriptor + fd_MsgSignArbitraryData_app_domain protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_signer protoreflect.FieldDescriptor + fd_MsgSignArbitraryData_data protoreflect.FieldDescriptor ) func init() { file_offchain_msgSignArbitraryData_proto_init() md_MsgSignArbitraryData = File_offchain_msgSignArbitraryData_proto.Messages().ByName("MsgSignArbitraryData") - fd_MsgSignArbitraryData_appDomain = md_MsgSignArbitraryData.Fields().ByName("appDomain") + fd_MsgSignArbitraryData_app_domain = md_MsgSignArbitraryData.Fields().ByName("app_domain") fd_MsgSignArbitraryData_signer = md_MsgSignArbitraryData.Fields().ByName("signer") fd_MsgSignArbitraryData_data = md_MsgSignArbitraryData.Fields().ByName("data") } @@ -97,7 +97,7 @@ func (x *fastReflection_MsgSignArbitraryData) Interface() protoreflect.ProtoMess func (x *fastReflection_MsgSignArbitraryData) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { if x.AppDomain != "" { value := protoreflect.ValueOfString(x.AppDomain) - if !f(fd_MsgSignArbitraryData_appDomain, value) { + if !f(fd_MsgSignArbitraryData_app_domain, value) { return } } @@ -128,7 +128,7 @@ func (x *fastReflection_MsgSignArbitraryData) Range(f func(protoreflect.FieldDes // a repeated field is populated if it is non-empty. func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescriptor) bool { switch fd.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.app_domain": return x.AppDomain != "" case "offchain.MsgSignArbitraryData.signer": return x.Signer != "" @@ -150,7 +150,7 @@ func (x *fastReflection_MsgSignArbitraryData) Has(fd protoreflect.FieldDescripto // Clear is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescriptor) { switch fd.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.app_domain": x.AppDomain = "" case "offchain.MsgSignArbitraryData.signer": x.Signer = "" @@ -172,7 +172,7 @@ func (x *fastReflection_MsgSignArbitraryData) Clear(fd protoreflect.FieldDescrip // of the value; to obtain a mutable reference, use Mutable. func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { switch descriptor.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.app_domain": value := x.AppDomain return protoreflect.ValueOfString(value) case "offchain.MsgSignArbitraryData.signer": @@ -201,7 +201,7 @@ func (x *fastReflection_MsgSignArbitraryData) Get(descriptor protoreflect.FieldD // Set is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { switch fd.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.app_domain": x.AppDomain = value.Interface().(string) case "offchain.MsgSignArbitraryData.signer": x.Signer = value.Interface().(string) @@ -227,8 +227,8 @@ func (x *fastReflection_MsgSignArbitraryData) Set(fd protoreflect.FieldDescripto // Mutable is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": - panic(fmt.Errorf("field appDomain of message offchain.MsgSignArbitraryData is not mutable")) + case "offchain.MsgSignArbitraryData.app_domain": + panic(fmt.Errorf("field app_domain of message offchain.MsgSignArbitraryData is not mutable")) case "offchain.MsgSignArbitraryData.signer": panic(fmt.Errorf("field signer of message offchain.MsgSignArbitraryData is not mutable")) case "offchain.MsgSignArbitraryData.data": @@ -246,7 +246,7 @@ func (x *fastReflection_MsgSignArbitraryData) Mutable(fd protoreflect.FieldDescr // For lists, maps, and messages, this returns a new, empty, mutable value. func (x *fastReflection_MsgSignArbitraryData) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "offchain.MsgSignArbitraryData.appDomain": + case "offchain.MsgSignArbitraryData.app_domain": return protoreflect.ValueOfString("") case "offchain.MsgSignArbitraryData.signer": return protoreflect.ValueOfString("") @@ -583,7 +583,7 @@ type MsgSignArbitraryData struct { unknownFields protoimpl.UnknownFields // AppDomain is the application requesting off-chain message signing - AppDomain string `protobuf:"bytes,1,opt,name=appDomain,proto3" json:"appDomain,omitempty"` + AppDomain string `protobuf:"bytes,1,opt,name=app_domain,json=appDomain,proto3" json:"app_domain,omitempty"` // Signer is the sdk.AccAddress of the message signer Signer string `protobuf:"bytes,2,opt,name=signer,proto3" json:"signer,omitempty"` // Data represents the raw bytes of the content that is signed (text, json, etc) @@ -641,28 +641,29 @@ var file_offchain_msgSignArbitraryData_proto_rawDesc = []byte{ 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x01, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x53, 0x69, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, - 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, - 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x3a, 0x2d, 0x82, 0xe7, 0xb0, 0x2a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, - 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x4d, 0x73, - 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, - 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, - 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, - 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xca, 0x02, 0x08, 0x4f, 0x66, - 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, - 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1d, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x70, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, + 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, + 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x3a, 0x2d, 0x82, 0xe7, 0xb0, 0x2a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x72, 0x8a, 0xe7, 0xb0, 0x2a, 0x1d, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x4d, + 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, + 0x61, 0x74, 0x61, 0x42, 0xa3, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x66, 0x66, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x42, 0x19, 0x4d, 0x73, 0x67, 0x53, 0x69, 0x67, 0x6e, 0x41, 0x72, 0x62, + 0x69, 0x74, 0x72, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, + 0x58, 0xaa, 0x02, 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xca, 0x02, 0x08, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0xe2, 0x02, 0x14, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x08, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( From 4010f9fe68a10929c4d6312460502b03135df16d Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 8 Jan 2024 11:39:40 +0100 Subject: [PATCH 22/33] don't use x/auth --- client/v2/offchain/verify.go | 17 ++++------------- client/v2/offchain/verify_test.go | 16 ++++++++-------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/client/v2/offchain/verify.go b/client/v2/offchain/verify.go index 5ab44d822639..303a086022a7 100644 --- a/client/v2/offchain/verify.go +++ b/client/v2/offchain/verify.go @@ -12,7 +12,6 @@ import ( apitx "cosmossdk.io/api/cosmos/tx/v1beta1" v2flags "cosmossdk.io/client/v2/internal/flags" - authsigning "cosmossdk.io/x/auth/signing" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" @@ -64,24 +63,16 @@ func verify(ctx client.Context, tx *apitx.Tx) error { return err } - signingData := authsigning.SignerData{ - Address: addr, - ChainID: ExpectedChainID, - AccountNumber: ExpectedAccountNumber, - Sequence: ExpectedSequence, - PubKey: pubKey, - } - anyPk, err := codectypes.NewAnyWithValue(pubKey) if err != nil { return err } txSignerData := txsigning.SignerData{ - ChainID: signingData.ChainID, - AccountNumber: signingData.AccountNumber, - Sequence: signingData.Sequence, - Address: signingData.Address, + ChainID: ExpectedChainID, + AccountNumber: ExpectedAccountNumber, + Sequence: ExpectedSequence, + Address: addr, PubKey: &anypb.Any{ TypeUrl: anyPk.TypeUrl, Value: anyPk.Value, diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index cce7e5f8355f..2f0c9fe97c5c 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -29,50 +29,50 @@ func Test_Verify(t *testing.T) { }{ { name: "signMode direct json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw==\"]}"), fileFormat: "json", ctx: ctx, }, { name: "signMode textual json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"sfJ8ymLx+rA4BUlSoWOpO0pLAphvPwif5ztHqJVHdlQ3MKp+N5SXfgAPEEhBRyitS8mi/Y7NBr9TIEpjHFr12A==\"]}"), + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"vAGdBHxFWQUtkHg0zZFgXnYZmUCIIBfdAKVGy7NGaaA8Wyz5E6qpmrDW6929U+L/pKU3u1kZ1JKSYsWgcgPzpw==\"]}"), fileFormat: "json", ctx: ctx, }, { name: "signMode legacyAmino json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"jn/RsMB79ZLSYPJaXVpZLZDt/gJ+4bNLLfAU0Vspj35+/DXBTj+GFdMvWWrc3emeIbYVgbgwaxghNnlim6JYNA==\"]}"), + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"83vXwdnwHK4EhA6d8ynRwpee/tE5CBAr3t5tsW/DMLpjxSD1pSmqHpBeB+t19WOK/plBv2ODLMQKZlz/kMdcjg==\"]}"), fileFormat: "json", ctx: ctx, }, { name: "wrong signer json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6g1szfak0/WeLWfsYOSYBv3UdA8dt/EfYLLHkf1Y5yt\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"Bax6+jG6pGTI+txUqEI3pMfppKNqZu4e7HAH/rYxIkZkGXqJKDgm8Eri3SJD6H9mSDUN2EW1VEiAdp1OZXc6Aw==\"]}"), + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74gmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw==\"]}"), fileFormat: "json", ctx: ctx, wantErr: true, }, { name: "signMode direct text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), fileFormat: "text", ctx: ctx, }, { name: "signMode textual text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_TEXTUAL}}} fee:{}} signatures:\"\\xbc\\x01\\x9d\\x04|EY\\x05-\\x90x4͑`^v\\x19\\x99@\\x88 \\x17\\xdd\\x00\\xa5F˳Fi\\xa0<[,\\xf9\\x13\\xaa\\xa9\\x9a\\xb0\\xd6\\xebݽS\\xe2\\xff\\xa4\\xa57\\xbbY\\x19Ԓ\\x92bŠr\\x03\\xf3\\xa7\""), fileFormat: "text", ctx: ctx, }, { name: "signMode legacyAmino text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_LEGACY_AMINO_JSON}}} fee:{}} signatures:\"\\xf3{\\xd7\\xc1\\xd9\\xf0\\x1c\\xae\\x04\\x84\\x0e\\x9d\\xf3)\\xd1\\u0097\\x9e\\xfe\\xd19\\x08\\x10+\\xde\\xdem\\xb1o\\xc30\\xbac\\xc5 \\xf5\\xa5)\\xaa\\x1e\\x90^\\x07\\xebu\\xf5c\\x8a\\xfe\\x99A\\xbfc\\x83,\\xc4\\nf\\\\\\xff\\x90\\xc7\\\\\\x8e\""), fileFormat: "text", ctx: ctx, }, { name: "wrong signer text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), fileFormat: "text", ctx: ctx, wantErr: true, From 7d0db42314e5cd675ea5e1e16d1d8335766835a2 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 8 Jan 2024 13:05:11 +0100 Subject: [PATCH 23/33] add: test --- client/v2/offchain/verify_test.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 2f0c9fe97c5c..210a4069d2b7 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -6,9 +6,12 @@ import ( "github.com/stretchr/testify/require" _ "cosmossdk.io/api/cosmos/crypto/secp256k1" + apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" ) const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" @@ -90,6 +93,25 @@ func Test_Verify(t *testing.T) { } } +func Test_SignVerify(t *testing.T) { + k := keyring.NewInMemory(getCodec()) + _, err := k.NewAccount("signVerify", mnemonic, "", "m/44'/118'/0'/0/0", hd.Secp256k1) + require.NoError(t, err) + + ctx := client.Context{ + TxConfig: MakeTestTxConfig(t), + Codec: getCodec(), + AddressCodec: address.NewBech32Codec("cosmos"), + Keyring: k, + } + + tx, err := sign(ctx, "signVerify", "digest", apitxsigning.SignMode_SIGN_MODE_DIRECT) + require.NoError(t, err) + + err = verify(ctx, tx) + require.NoError(t, err) +} + func Test_unmarshal(t *testing.T) { tests := []struct { name string @@ -98,12 +120,12 @@ func Test_unmarshal(t *testing.T) { }{ { name: "json test", - digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData","appDomain":"simd","signer":"cosmos1rt2xyymh5pvycl8dc00et4mxgr4cpzcdlk8ped","data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}],"memo":"","timeoutHeight":"0","extensionOptions":[],"nonCriticalExtensionOptions":[]},"authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A06XPD6ML7BHSWHsc2u5EtkCXzsCNmhgPaDdNCp5nPF2"},"modeInfo":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"0"}],"fee":{"amount":[],"gasLimit":"0","payer":"","granter":""},"tip":null},"signatures":["hx8Qo6xZ/Ie0d1TFtiVxSK1rUsRKDEiv1IdcgbkSGYgePYZl6aHJxpSxQDXdIeoZiPeIdrsTkkgjmH4wv2BBdw=="]}`), + digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData","appDomain":"simd","signer":"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq","data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}],"memo":"","timeoutHeight":"0","extensionOptions":[],"nonCriticalExtensionOptions":[]},"authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U"},"modeInfo":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"0"}],"fee":{"amount":[],"gasLimit":"0","payer":"","granter":""},"tip":null},"signatures":["RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw=="]}`), fileFormat: "json", }, { name: "text test", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{appDomain:\"simd\" signer:\"cosmos15r8vphexk8tnu6gvq0a5dhfs3j06ht9kux78rp\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xa85\\xb37ړO\\xd6x\\xb5\\x9f\\xb1\\x83\\x92`\\x1b\\xf7Q\\xd0ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), fileFormat: "text", }, } From ef60b055a8092746cbae8a51841f144d933a24c8 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 8 Jan 2024 17:45:41 +0100 Subject: [PATCH 24/33] del: x/auth --- client/v2/offchain/sign.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index fa7d475aebbc..56918e6857a5 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -8,13 +8,13 @@ import ( apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" apitx "cosmossdk.io/api/cosmos/tx/v1beta1" "cosmossdk.io/client/v2/internal/offchain" - authsigning "cosmossdk.io/x/auth/signing" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/version" ) @@ -27,6 +27,14 @@ const ( ExpectedSequence = 0 ) +type signerData struct { + Address string + ChainID string + AccountNumber uint64 + Sequence uint64 + PubKey cryptotypes.PubKey +} + // getSignMode returns the expected SignMode. func getSignMode(signModeStr string) apisigning.SignMode { signMode := apisigning.SignMode_SIGN_MODE_UNSPECIFIED @@ -97,7 +105,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM return nil, err } - signerData := authsigning.SignerData{ + signerData := signerData{ Address: addr, ChainID: ExpectedChainID, AccountNumber: ExpectedAccountNumber, @@ -148,7 +156,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM func getSignBytes(ctx context.Context, handlerMap *txsigning.HandlerMap, mode apisigning.SignMode, - signerData authsigning.SignerData, + signerData signerData, tx *builder, ) ([]byte, error) { txData, err := tx.GetSigningTxData() From 520f7209fadb5661a6688e821abab5bfdfeed4bc Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 9 Jan 2024 17:26:21 +0100 Subject: [PATCH 25/33] del: x/auth from tests --- client/v2/offchain/common_test.go | 146 ++++++++++++++++++++++++++++++ client/v2/offchain/sign_test.go | 48 +--------- client/v2/offchain/verify_test.go | 6 +- 3 files changed, 149 insertions(+), 51 deletions(-) create mode 100644 client/v2/offchain/common_test.go diff --git a/client/v2/offchain/common_test.go b/client/v2/offchain/common_test.go new file mode 100644 index 000000000000..5b862fcb20bb --- /dev/null +++ b/client/v2/offchain/common_test.go @@ -0,0 +1,146 @@ +package offchain + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + + bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" + "cosmossdk.io/x/tx/signing" + "cosmossdk.io/x/tx/signing/aminojson" + "cosmossdk.io/x/tx/signing/direct" + "cosmossdk.io/x/tx/signing/directaux" + "cosmossdk.io/x/tx/signing/textual" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/codec/testutil" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +const ( + addressCodecPrefix = "cosmos" + validatorAddressCodecPrefix = "cosmosvaloper" + mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" +) + +func getCodec() codec.Codec { + registry := testutil.CodecOptions{}.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + return codec.NewProtoCodec(registry) +} + +func newGRPCCoinMetadataQueryFn(grpcConn grpc.ClientConnInterface) textual.CoinMetadataQueryFn { + return func(ctx context.Context, denom string) (*bankv1beta1.Metadata, error) { + bankQueryClient := bankv1beta1.NewQueryClient(grpcConn) + res, err := bankQueryClient.DenomMetadata(ctx, &bankv1beta1.QueryDenomMetadataRequest{ + Denom: denom, + }) + if err != nil { + return nil, err + } + + return res.Metadata, nil + } +} + +// testConfig fulfills client.TxConfig although SignModeHandler is the only method implemented. +type testConfig struct { + handler *signing.HandlerMap +} + +func (t testConfig) SignModeHandler() *signing.HandlerMap { + return t.handler +} + +func (t testConfig) TxEncoder() sdk.TxEncoder { + return nil +} + +func (t testConfig) TxDecoder() sdk.TxDecoder { + return nil +} + +func (t testConfig) TxJSONEncoder() sdk.TxEncoder { + return nil +} + +func (t testConfig) TxJSONDecoder() sdk.TxDecoder { + return nil +} + +func (t testConfig) MarshalSignatureJSON(v2s []signingtypes.SignatureV2) ([]byte, error) { + return nil, nil +} + +func (t testConfig) UnmarshalSignatureJSON(bytes []byte) ([]signingtypes.SignatureV2, error) { + return nil, nil +} + +func (t testConfig) NewTxBuilder() client.TxBuilder { + return nil +} + +func (t testConfig) WrapTxBuilder(s sdk.Tx) (client.TxBuilder, error) { + return nil, nil +} + +func (t testConfig) SigningContext() *signing.Context { + return nil +} + +func newTestConfig(t *testing.T) *testConfig { + t.Helper() + + enabledSignModes := []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT, + signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signingtypes.SignMode_SIGN_MODE_TEXTUAL, + } + + var err error + signingOptions := signing.Options{ + AddressCodec: address.NewBech32Codec(addressCodecPrefix), + ValidatorAddressCodec: address.NewBech32Codec(validatorAddressCodecPrefix), + } + signingContext, err := signing.NewContext(signingOptions) + require.NoError(t, err) + + lenSignModes := len(enabledSignModes) + handlers := make([]signing.SignModeHandler, lenSignModes) + for i, m := range enabledSignModes { + var err error + switch m { + case signingtypes.SignMode_SIGN_MODE_DIRECT: + handlers[i] = &direct.SignModeHandler{} + case signingtypes.SignMode_SIGN_MODE_DIRECT_AUX: + handlers[i], err = directaux.NewSignModeHandler(directaux.SignModeHandlerOptions{ + TypeResolver: signingOptions.TypeResolver, + SignersContext: signingContext, + }) + require.NoError(t, err) + case signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: + handlers[i] = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{ + FileResolver: signingOptions.FileResolver, + TypeResolver: signingOptions.TypeResolver, + }) + case signingtypes.SignMode_SIGN_MODE_TEXTUAL: + handlers[i], err = textual.NewSignModeHandler(textual.SignModeOptions{ + CoinMetadataQuerier: newGRPCCoinMetadataQueryFn(client.Context{}), + FileResolver: signingOptions.FileResolver, + TypeResolver: signingOptions.TypeResolver, + }) + require.NoError(t, err) + } + } + + handler := signing.NewHandlerMap(handlers...) + return &testConfig{handler: handler} +} diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index 58f14965eadf..bf535b45aea7 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -3,63 +3,17 @@ package offchain import ( "testing" - "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" - "cosmossdk.io/x/auth/tx" - txmodule "cosmossdk.io/x/auth/tx/config" - "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/cosmos/cosmos-sdk/codec/testutil" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" // TODO: needed as textual is not enabled by default ) -func getCodec() codec.Codec { - registry := testutil.CodecOptions{}.NewInterfaceRegistry() - cryptocodec.RegisterInterfaces(registry) - - return codec.NewProtoCodec(registry) -} - -func MakeTestTxConfig(t *testing.T) client.TxConfig { - t.Helper() - enabledSignModes := []signingtypes.SignMode{ - signingtypes.SignMode_SIGN_MODE_DIRECT, - signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, - signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - signingtypes.SignMode_SIGN_MODE_TEXTUAL, - } - initClientCtx := client.Context{} - txConfigOpts := tx.ConfigOptions{ - EnabledSignModes: enabledSignModes, - TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), - } - ir, err := codectypes.NewInterfaceRegistryWithOptions(codectypes.InterfaceRegistryOptions{ - ProtoFiles: proto.HybridResolver, - SigningOptions: signing.Options{ - AddressCodec: address.NewBech32Codec("cosmos"), - ValidatorAddressCodec: address.NewBech32Codec("cosmosvaloper"), - }, - }) - require.NoError(t, err) - - cryptocodec.RegisterInterfaces(ir) - cdc := codec.NewProtoCodec(ir) - txConfig, err := tx.NewTxConfigWithOptions(cdc, txConfigOpts) - require.NoError(t, err) - - return txConfig -} - func Test_getSignMode(t *testing.T) { tests := []struct { name string @@ -105,7 +59,7 @@ func Test_sign(t *testing.T) { ctx := client.Context{ Keyring: k, - TxConfig: MakeTestTxConfig(t), + TxConfig: newTestConfig(t), AddressCodec: address.NewBech32Codec("cosmos"), } diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 210a4069d2b7..04300fd4c227 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -14,11 +14,9 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" ) -const mnemonic = "have embark stumble card pistol fun gauge obtain forget oil awesome lottery unfold corn sure original exist siren pudding spread uphold dwarf goddess card" - func Test_Verify(t *testing.T) { ctx := client.Context{ - TxConfig: MakeTestTxConfig(t), + TxConfig: newTestConfig(t), Codec: getCodec(), AddressCodec: address.NewBech32Codec("cosmos"), } @@ -99,7 +97,7 @@ func Test_SignVerify(t *testing.T) { require.NoError(t, err) ctx := client.Context{ - TxConfig: MakeTestTxConfig(t), + TxConfig: newTestConfig(t), Codec: getCodec(), AddressCodec: address.NewBech32Codec("cosmos"), Keyring: k, From 19ba61fb6ead0633a5d767c6966ef332b8167991 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 22 Jan 2024 10:54:30 +0100 Subject: [PATCH 26/33] update: use only textual --- client/v2/offchain/cli.go | 6 ++---- client/v2/offchain/sign.go | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index ff6009a84cbe..663016dada2d 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -21,7 +21,7 @@ const ( // OffChain off-chain utilities. func OffChain() *cobra.Command { cmd := &cobra.Command{ - Use: "offchain", + Use: "off-chain", Short: "Off-chain utilities.", Long: `Utilities for off-chain data.`, } @@ -50,13 +50,12 @@ func SignFile() *cobra.Command { return err } - signmode, _ := cmd.Flags().GetString(flags.FlagSignMode) notEmitUnpopulated, _ := cmd.Flags().GetBool(flagNotEmitUnpopulated) indent, _ := cmd.Flags().GetString(flagIndent) encoding, _ := cmd.Flags().GetString(flagEncoding) outputFormat, _ := cmd.Flags().GetString(v2flags.FlagOutput) - signedTx, err := Sign(clientCtx, bz, args[0], signmode, indent, encoding, outputFormat, !notEmitUnpopulated) + signedTx, err := Sign(clientCtx, bz, args[0], indent, encoding, outputFormat, !notEmitUnpopulated) if err != nil { return err } @@ -69,7 +68,6 @@ func SignFile() *cobra.Command { cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx") cmd.PersistentFlags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") cmd.PersistentFlags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") - cmd.PersistentFlags().String(flags.FlagSignMode, "direct", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature") cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") return cmd } diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 56918e6857a5..afcb6abd9204 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -52,7 +52,7 @@ func getSignMode(signModeStr string) apisigning.SignMode { } // Sign signs given bytes using the specified encoder and SignMode. -func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encoding, output string, emitUnpopulated bool) (string, error) { +func Sign(ctx client.Context, rawBytes []byte, fromName, indent, encoding, output string, emitUnpopulated bool) (string, error) { encoder, err := getEncoder(encoding) if err != nil { return "", err @@ -63,7 +63,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, signMode, indent, encod return "", err } - tx, err := sign(ctx, fromName, digest, getSignMode(signMode)) + tx, err := sign(ctx, fromName, digest, getSignMode(flags.SignModeTextual)) if err != nil { return "", err } From 3e1daa3828def52f79d3c71f3096d226e84fe4d7 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Mon, 22 Jan 2024 18:16:20 +0100 Subject: [PATCH 27/33] update: remove signmodes --- client/v2/offchain/sign.go | 31 ++--------- client/v2/offchain/sign_test.go | 93 ++----------------------------- client/v2/offchain/verify_test.go | 43 +++----------- 3 files changed, 21 insertions(+), 146 deletions(-) diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index afcb6abd9204..6f00cc74ee3d 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -11,7 +11,6 @@ import ( txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -35,22 +34,6 @@ type signerData struct { PubKey cryptotypes.PubKey } -// getSignMode returns the expected SignMode. -func getSignMode(signModeStr string) apisigning.SignMode { - signMode := apisigning.SignMode_SIGN_MODE_UNSPECIFIED - switch signModeStr { - case flags.SignModeDirect: - signMode = apisigning.SignMode_SIGN_MODE_DIRECT - case flags.SignModeLegacyAminoJSON: - signMode = apisigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON - case flags.SignModeDirectAux: - signMode = apisigning.SignMode_SIGN_MODE_DIRECT_AUX - case flags.SignModeTextual: - signMode = apisigning.SignMode_SIGN_MODE_TEXTUAL - } - return signMode -} - // Sign signs given bytes using the specified encoder and SignMode. func Sign(ctx client.Context, rawBytes []byte, fromName, indent, encoding, output string, emitUnpopulated bool) (string, error) { encoder, err := getEncoder(encoding) @@ -63,7 +46,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, indent, encoding, outpu return "", err } - tx, err := sign(ctx, fromName, digest, getSignMode(flags.SignModeTextual)) + tx, err := sign(ctx, fromName, digest) if err != nil { return "", err } @@ -77,7 +60,7 @@ func Sign(ctx client.Context, rawBytes []byte, fromName, indent, encoding, outpu } // sign signs a digest with provided key and SignMode. -func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignMode) (*apitx.Tx, error) { +func sign(ctx client.Context, fromName, digest string) (*apitx.Tx, error) { keybase, err := keyring.NewAutoCLIKeyring(ctx.Keyring) if err != nil { return nil, err @@ -114,7 +97,7 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM } sigData := &SingleSignatureData{ - SignMode: signMode, + SignMode: apisigning.SignMode_SIGN_MODE_TEXTUAL, Signature: nil, } @@ -131,13 +114,12 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM } bytesToSign, err := getSignBytes( - context.Background(), ctx.TxConfig.SignModeHandler(), - signMode, signerData, txBuilder) + context.Background(), ctx.TxConfig.SignModeHandler(), signerData, txBuilder) if err != nil { return nil, err } - signedBytes, err := keybase.Sign(fromName, bytesToSign, signMode) + signedBytes, err := keybase.Sign(fromName, bytesToSign, apisigning.SignMode_SIGN_MODE_TEXTUAL) if err != nil { return nil, err } @@ -155,7 +137,6 @@ func sign(ctx client.Context, fromName, digest string, signMode apisigning.SignM // getSignBytes gets the bytes to be signed for the given Tx and SignMode. func getSignBytes(ctx context.Context, handlerMap *txsigning.HandlerMap, - mode apisigning.SignMode, signerData signerData, tx *builder, ) ([]byte, error) { @@ -180,5 +161,5 @@ func getSignBytes(ctx context.Context, }, } - return handlerMap.GetSignBytes(ctx, mode, txSignerData, txData) + return handlerMap.GetSignBytes(ctx, apisigning.SignMode_SIGN_MODE_TEXTUAL, txSignerData, txData) } diff --git a/client/v2/offchain/sign_test.go b/client/v2/offchain/sign_test.go index bf535b45aea7..b47a84584d36 100644 --- a/client/v2/offchain/sign_test.go +++ b/client/v2/offchain/sign_test.go @@ -5,55 +5,12 @@ import ( "github.com/stretchr/testify/require" - apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" ) -func Test_getSignMode(t *testing.T) { - tests := []struct { - name string - signModeStr string - want apitxsigning.SignMode - }{ - { - name: "direct", - signModeStr: flags.SignModeDirect, - want: apitxsigning.SignMode_SIGN_MODE_DIRECT, - }, - { - name: "legacy Amino JSON", - signModeStr: flags.SignModeLegacyAminoJSON, - want: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - }, - { - name: "direct Aux", - signModeStr: flags.SignModeDirectAux, - want: apitxsigning.SignMode_SIGN_MODE_DIRECT_AUX, - }, - { - name: "textual", - signModeStr: flags.SignModeTextual, - want: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, - }, - { - name: "unspecified", - signModeStr: "", - want: apitxsigning.SignMode_SIGN_MODE_UNSPECIFIED, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := getSignMode(tt.signModeStr) - require.Equal(t, got, tt.want) - }) - } -} - func Test_sign(t *testing.T) { k := keyring.NewInMemory(getCodec()) @@ -67,52 +24,18 @@ func Test_sign(t *testing.T) { ctx client.Context fromName string digest string - signMode apitxsigning.SignMode } tests := []struct { - name string - args args - wantErr bool + name string + args args }{ { - name: "signMode direct", + name: "Sign", args: args{ ctx: ctx, fromName: "direct", digest: "Hello world!", - signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT, }, - wantErr: false, - }, - { - name: "signMode textual", - args: args{ - ctx: ctx, - fromName: "textual", - digest: "Hello world!", - signMode: apitxsigning.SignMode_SIGN_MODE_TEXTUAL, - }, - wantErr: false, - }, - { - name: "signMode legacyAmino", - args: args{ - ctx: ctx, - fromName: "legacy", - digest: "Hello world!", - signMode: apitxsigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - }, - wantErr: false, - }, - { - name: "signMode direct aux", - args: args{ - ctx: ctx, - fromName: "direct-aux", - digest: "Hello world!", - signMode: apitxsigning.SignMode_SIGN_MODE_DIRECT_AUX, - }, - wantErr: true, }, } for _, tt := range tests { @@ -120,13 +43,9 @@ func Test_sign(t *testing.T) { _, err := k.NewAccount(tt.args.fromName, mnemonic, tt.name, "m/44'/118'/0'/0/0", hd.Secp256k1) require.NoError(t, err) - got, err := sign(tt.args.ctx, tt.args.fromName, tt.args.digest, tt.args.signMode) - if !tt.wantErr { - require.NoError(t, err) - require.NotNil(t, got) - } else { - require.Error(t, err) - } + got, err := sign(tt.args.ctx, tt.args.fromName, tt.args.digest) + require.NoError(t, err) + require.NotNil(t, got) }) } } diff --git a/client/v2/offchain/verify_test.go b/client/v2/offchain/verify_test.go index 04300fd4c227..ecdd57e75b5b 100644 --- a/client/v2/offchain/verify_test.go +++ b/client/v2/offchain/verify_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" _ "cosmossdk.io/api/cosmos/crypto/secp256k1" - apitxsigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec/address" @@ -29,51 +28,27 @@ func Test_Verify(t *testing.T) { wantErr bool }{ { - name: "signMode direct json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw==\"]}"), - fileFormat: "json", - ctx: ctx, - }, - { - name: "signMode textual json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"vAGdBHxFWQUtkHg0zZFgXnYZmUCIIBfdAKVGy7NGaaA8Wyz5E6qpmrDW6929U+L/pKU3u1kZ1JKSYsWgcgPzpw==\"]}"), - fileFormat: "json", - ctx: ctx, - }, - { - name: "signMode legacyAmino json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_LEGACY_AMINO_JSON\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"83vXwdnwHK4EhA6d8ynRwpee/tE5CBAr3t5tsW/DMLpjxSD1pSmqHpBeB+t19WOK/plBv2ODLMQKZlz/kMdcjg==\"]}"), + name: "verify json", + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\", \"appDomain\":\"simd\", \"signer\":\"cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu\", \"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}]}, \"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\", \"key\":\"A/Bfsb7grZtysreo48oB1XAXbcgHnEJyhAqzDMgbLlXw\"}, \"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}}}], \"fee\":{}}, \"signatures\":[\"gRufjcmATaJ3hZSiXII3lcsLDJlHM4OhQs3O/QgAK4weQ73kmj30/gw3HwTKxGb4pnVe0iyLXrKRNeSl1O3zSQ==\"]}"), fileFormat: "json", ctx: ctx, }, { name: "wrong signer json", - digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\",\"appDomain\":\"simd\",\"signer\":\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74gmaq\",\"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}],\"memo\":\"\",\"timeoutHeight\":\"0\",\"extensionOptions\":[],\"nonCriticalExtensionOptions\":[]},\"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U\"},\"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_DIRECT\"}},\"sequence\":\"0\"}],\"fee\":{\"amount\":[],\"gasLimit\":\"0\",\"payer\":\"\",\"granter\":\"\"},\"tip\":null},\"signatures\":[\"RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw==\"]}"), + digest: []byte("{\"body\":{\"messages\":[{\"@type\":\"/offchain.MsgSignArbitraryData\", \"appDomain\":\"simd\", \"signer\":\"cosmos1450l4uau674z55c36df0v7904rnvdk9aq8w96j\", \"data\":\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}]}, \"authInfo\":{\"signerInfos\":[{\"publicKey\":{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\", \"key\":\"A/Bfsb7grZtysreo48oB1XAXbcgHnEJyhAqzDMgbLlXw\"}, \"modeInfo\":{\"single\":{\"mode\":\"SIGN_MODE_TEXTUAL\"}}}], \"fee\":{}}, \"signatures\":[\"gRufjcmATaJ3hZSiXII3lcsLDJlHM4OhQs3O/QgAK4weQ73kmj30/gw3HwTKxGb4pnVe0iyLXrKRNeSl1O3zSQ==\"]}"), fileFormat: "json", ctx: ctx, wantErr: true, }, { - name: "signMode direct text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03,<\\xf2O/2\\xcaYr\\n:\\xaaB\\xfc \\xd4s?a\\xd5@\\x96V\\xbb\\xa6\\xa6>ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), - fileFormat: "text", - ctx: ctx, - }, - { - name: "signMode textual text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03,<\\xf2O/2\\xcaYr\\n:\\xaaB\\xfc \\xd4s?a\\xd5@\\x96V\\xbb\\xa6\\xa6>ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_TEXTUAL}}} fee:{}} signatures:\"\\xbc\\x01\\x9d\\x04|EY\\x05-\\x90x4͑`^v\\x19\\x99@\\x88 \\x17\\xdd\\x00\\xa5F˳Fi\\xa0<[,\\xf9\\x13\\xaa\\xa9\\x9a\\xb0\\xd6\\xebݽS\\xe2\\xff\\xa4\\xa57\\xbbY\\x19Ԓ\\x92bŠr\\x03\\xf3\\xa7\""), - fileFormat: "text", - ctx: ctx, - }, - { - name: "signMode legacyAmino text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03,<\\xf2O/2\\xcaYr\\n:\\xaaB\\xfc \\xd4s?a\\xd5@\\x96V\\xbb\\xa6\\xa6>ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_LEGACY_AMINO_JSON}}} fee:{}} signatures:\"\\xf3{\\xd7\\xc1\\xd9\\xf0\\x1c\\xae\\x04\\x84\\x0e\\x9d\\xf3)\\xd1\\u0097\\x9e\\xfe\\xd19\\x08\\x10+\\xde\\xdem\\xb1o\\xc30\\xbac\\xc5 \\xf5\\xa5)\\xaa\\x1e\\x90^\\x07\\xebu\\xf5c\\x8a\\xfe\\x99A\\xbfc\\x83,\\xc4\\nf\\\\\\xff\\x90\\xc7\\\\\\x8e\""), + name: "verify text", + digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xf0_\\xb1\\xbe\u0B5Br\\xb2\\xb7\\xa8\\xe3\\xca\\x01\\xd5p\\x17m\\xc8\\x07\\x9cBr\\x84\\n\\xb3\\x0c\\xc8\\x1b.U\\xf0\"}} mode_info:{single:{mode:SIGN_MODE_TEXTUAL}}} fee:{}} signatures:\"\\x81\\x1b\\x9f\\x8dɀM\\xa2w\\x85\\x94\\xa2\\\\\\x827\\x95\\xcb\\x0b\\x0c\\x99G3\\x83\\xa1B\\xcd\\xce\\xfd\\x08\\x00+\\x8c\\x1eC\\xbd\\xe4\\x9a=\\xf4\\xfe\\x0c7\\x1f\\x04\\xca\\xc4f\\xf8\\xa6u^\\xd2,\\x8b^\\xb2\\x915\\xe4\\xa5\\xd4\\xed\\xf3I\"\n"), fileFormat: "text", ctx: ctx, }, { name: "wrong signer text", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74gmaq\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03,<\\xf2O/2\\xcaYr\\n:\\xaaB\\xfc \\xd4s?a\\xd5@\\x96V\\xbb\\xa6\\xa6>ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), + digest: []byte("\"body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\\\"simd\\\" signer:\\\"cosmos1450l4uau674z55c36df0v7904rnvdk9aq8w96j\\\" data:\\\"{\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"John\\\\\\\",\\\\n\\\\t\\\\\\\"surname\\\\\\\": \\\\\\\"Connor\\\\\\\",\\\\n\\\\t\\\\\\\"age\\\\\\\": 15\\\\n}\\\\n\\\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\\\"\\\\x03\\\\xf0_\\\\xb1\\\\xbe\\u0B5Br\\\\xb2\\\\xb7\\\\xa8\\\\xe3\\\\xca\\\\x01\\\\xd5p\\\\x17m\\\\xc8\\\\x07\\\\x9cBr\\\\x84\\\\n\\\\xb3\\\\x0c\\\\xc8\\\\x1b.U\\\\xf0\\\"}} mode_info:{single:{mode:SIGN_MODE_TEXTUAL}}} fee:{}} signatures:\\\"\\\\x81\\\\x1b\\\\x9f\\\\x8dɀM\\\\xa2w\\\\x85\\\\x94\\\\xa2\\\\\\\\\\\\x827\\\\x95\\\\xcb\\\\x0b\\\\x0c\\\\x99G3\\\\x83\\\\xa1B\\\\xcd\\\\xce\\\\xfd\\\\x08\\\\x00+\\\\x8c\\\\x1eC\\\\xbd\\\\xe4\\\\x9a=\\\\xf4\\\\xfe\\\\x0c7\\\\x1f\\\\x04\\\\xca\\\\xc4f\\\\xf8\\\\xa6u^\\\\xd2,\\\\x8b^\\\\xb2\\\\x915\\\\xe4\\\\xa5\\\\xd4\\\\xed\\\\xf3I\\\"\\n"), fileFormat: "text", ctx: ctx, wantErr: true, @@ -103,7 +78,7 @@ func Test_SignVerify(t *testing.T) { Keyring: k, } - tx, err := sign(ctx, "signVerify", "digest", apitxsigning.SignMode_SIGN_MODE_DIRECT) + tx, err := sign(ctx, "signVerify", "digest") require.NoError(t, err) err = verify(ctx, tx) @@ -118,12 +93,12 @@ func Test_unmarshal(t *testing.T) { }{ { name: "json test", - digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData","appDomain":"simd","signer":"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq","data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}],"memo":"","timeoutHeight":"0","extensionOptions":[],"nonCriticalExtensionOptions":[]},"authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ayw88k8vMspZcgo6qkL8INRzP2HVQJZWu6amPsq+Fg4U"},"modeInfo":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"0"}],"fee":{"amount":[],"gasLimit":"0","payer":"","granter":""},"tip":null},"signatures":["RUf2CTYcyeFIijviTAtRN9oqlY7BcaWsQtGvmTVQff0sinh6C1IeL4M2UxakDa1PSVveZyy8gdTsQs3zG43/Kw=="]}`), + digest: []byte(`{"body":{"messages":[{"@type":"/offchain.MsgSignArbitraryData", "appDomain":"simd", "signer":"cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu", "data":"{\n\t\"name\": \"John\",\n\t\"surname\": \"Connor\",\n\t\"age\": 15\n}\n"}]}, "authInfo":{"signerInfos":[{"publicKey":{"@type":"/cosmos.crypto.secp256k1.PubKey", "key":"A/Bfsb7grZtysreo48oB1XAXbcgHnEJyhAqzDMgbLlXw"}, "modeInfo":{"single":{"mode":"SIGN_MODE_TEXTUAL"}}}], "fee":{}}, "signatures":["gRufjcmATaJ3hZSiXII3lcsLDJlHM4OhQs3O/QgAK4weQ73kmj30/gw3HwTKxGb4pnVe0iyLXrKRNeSl1O3zSQ=="]}`), fileFormat: "json", }, { name: "text test", - digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1jcc2frcc4mww897ey4ejphj4x7jaza5w74dmaq\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03,<\\xf2O/2\\xcaYr\\n:\\xaaB\\xfc \\xd4s?a\\xd5@\\x96V\\xbb\\xa6\\xa6>ʾ\\x16\\x0e\\x14\"}} mode_info:{single:{mode:SIGN_MODE_DIRECT}}} fee:{}} signatures:\"EG\\xf6\\t6\\x1c\\xc9\\xe1H\\x8a;\\xe2L\\x0bQ7\\xda*\\x95\\x8e\\xc1q\\xa5\\xacBѯ\\x995P}\\xfd,\\x8axz\\x0bR\\x1e/\\x836S\\x16\\xa4\\r\\xadOI[\\xdeg,\\xbc\\x81\\xd4\\xecB\\xcd\\xf3\\x1b\\x8d\\xff+\""), + digest: []byte("body:{messages:{[/offchain.MsgSignArbitraryData]:{app_domain:\"simd\" signer:\"cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu\" data:\"{\\n\\t\\\"name\\\": \\\"John\\\",\\n\\t\\\"surname\\\": \\\"Connor\\\",\\n\\t\\\"age\\\": 15\\n}\\n\"}}} auth_info:{signer_infos:{public_key:{[/cosmos.crypto.secp256k1.PubKey]:{key:\"\\x03\\xf0_\\xb1\\xbe\u0B5Br\\xb2\\xb7\\xa8\\xe3\\xca\\x01\\xd5p\\x17m\\xc8\\x07\\x9cBr\\x84\\n\\xb3\\x0c\\xc8\\x1b.U\\xf0\"}} mode_info:{single:{mode:SIGN_MODE_TEXTUAL}}} fee:{}} signatures:\"\\x81\\x1b\\x9f\\x8dɀM\\xa2w\\x85\\x94\\xa2\\\\\\x827\\x95\\xcb\\x0b\\x0c\\x99G3\\x83\\xa1B\\xcd\\xce\\xfd\\x08\\x00+\\x8c\\x1eC\\xbd\\xe4\\x9a=\\xf4\\xfe\\x0c7\\x1f\\x04\\xca\\xc4f\\xf8\\xa6u^\\xd2,\\x8b^\\xb2\\x915\\xe4\\xa5\\xd4\\xed\\xf3I\"\n"), fileFormat: "text", }, } From c92ebc50c4e29b90810fc7dea9b01aa6a87f569f Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 23 Jan 2024 11:22:18 +0100 Subject: [PATCH 28/33] add: output document flag --- client/v2/offchain/cli.go | 20 +++++++++++++++----- client/v2/offchain/sign.go | 8 +++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 663016dada2d..0c0228654100 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -54,21 +54,31 @@ func SignFile() *cobra.Command { indent, _ := cmd.Flags().GetString(flagIndent) encoding, _ := cmd.Flags().GetString(flagEncoding) outputFormat, _ := cmd.Flags().GetString(v2flags.FlagOutput) + outputFile, _ := cmd.Flags().GetString(flags.FlagOutputDocument) signedTx, err := Sign(clientCtx, bz, args[0], indent, encoding, outputFormat, !notEmitUnpopulated) if err != nil { return err } + if outputFile != "" { + fp, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + if err != nil { + return err + } + cmd.SetOut(fp) + } + cmd.Println(signedTx) return nil }, } - cmd.PersistentFlags().String(flagIndent, " ", "Choose an indent for the tx") - cmd.PersistentFlags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") - cmd.PersistentFlags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") - cmd.PersistentFlags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") + cmd.Flags().String(flagIndent, " ", "Choose an indent for the tx") + cmd.Flags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") + cmd.Flags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") + cmd.Flags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") + cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") return cmd } @@ -100,6 +110,6 @@ func VerifyFile() *cobra.Command { }, } - cmd.PersistentFlags().String(flagFileFormat, "json", "Choose whats the file format to be verified (json|text)") + cmd.Flags().String(flagFileFormat, "json", "Choose whats the file format to be verified (json|text)") return cmd } diff --git a/client/v2/offchain/sign.go b/client/v2/offchain/sign.go index 6f00cc74ee3d..fd01227ed13e 100644 --- a/client/v2/offchain/sign.go +++ b/client/v2/offchain/sign.go @@ -24,6 +24,8 @@ const ( ExpectedAccountNumber = 0 // ExpectedSequence defines the sequence number an off-chain message must have ExpectedSequence = 0 + + signMode = apisigning.SignMode_SIGN_MODE_TEXTUAL ) type signerData struct { @@ -97,7 +99,7 @@ func sign(ctx client.Context, fromName, digest string) (*apitx.Tx, error) { } sigData := &SingleSignatureData{ - SignMode: apisigning.SignMode_SIGN_MODE_TEXTUAL, + SignMode: signMode, Signature: nil, } @@ -119,7 +121,7 @@ func sign(ctx client.Context, fromName, digest string) (*apitx.Tx, error) { return nil, err } - signedBytes, err := keybase.Sign(fromName, bytesToSign, apisigning.SignMode_SIGN_MODE_TEXTUAL) + signedBytes, err := keybase.Sign(fromName, bytesToSign, signMode) if err != nil { return nil, err } @@ -161,5 +163,5 @@ func getSignBytes(ctx context.Context, }, } - return handlerMap.GetSignBytes(ctx, apisigning.SignMode_SIGN_MODE_TEXTUAL, txSignerData, txData) + return handlerMap.GetSignBytes(ctx, signMode, txSignerData, txData) } From 3c8c664a9bf406c1be8ef593207d7906ae572808 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 23 Jan 2024 11:44:05 +0100 Subject: [PATCH 29/33] add: hex encoding --- client/v2/offchain/cli.go | 2 +- client/v2/offchain/encode.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 0c0228654100..2e1caf16e7bd 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -77,7 +77,7 @@ func SignFile() *cobra.Command { cmd.Flags().String(flagIndent, " ", "Choose an indent for the tx") cmd.Flags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") cmd.Flags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") - cmd.Flags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64)") + cmd.Flags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64|hex)") cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") return cmd } diff --git a/client/v2/offchain/encode.go b/client/v2/offchain/encode.go index 0de885368a46..6721ffc28cf2 100644 --- a/client/v2/offchain/encode.go +++ b/client/v2/offchain/encode.go @@ -2,12 +2,14 @@ package offchain import ( "encoding/base64" + "encoding/hex" "fmt" ) const ( noEncoder = "no-encoding" b64Encoder = "base64" + hexEncoder = "hex" ) type encodingFunc = func([]byte) (string, error) @@ -22,6 +24,11 @@ func base64Encoding(digest []byte) (string, error) { return base64.StdEncoding.EncodeToString(digest), nil } +// hexEncoding returns a byte slice as a hex encoded string. +func hexEncoding(digest []byte) (string, error) { + return hex.EncodeToString(digest), nil +} + // getEncoder returns a encodingFunc bases on the encoder id provided. func getEncoder(encoder string) (encodingFunc, error) { switch encoder { @@ -29,6 +36,8 @@ func getEncoder(encoder string) (encodingFunc, error) { return noEncoding, nil case b64Encoder: return base64Encoding, nil + case hexEncoder: + return hexEncoding, nil default: return nil, fmt.Errorf("unknown encoder: %s", encoder) } From 35c2a3058b775da9ebdfd1c5685ac8e878be93d9 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 23 Jan 2024 12:10:56 +0100 Subject: [PATCH 30/33] add: Readme --- client/v2/README.md | 57 +++++++++++++++++++++++++++++++++++++++ client/v2/offchain/cli.go | 4 +-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/client/v2/README.md b/client/v2/README.md index 5830667f644b..16b40ed001b4 100644 --- a/client/v2/README.md +++ b/client/v2/README.md @@ -209,6 +209,63 @@ For example, here is how the SDK does it for `cometbft` gRPC commands: https://github.com/cosmos/cosmos-sdk/blob/main/client/grpc/cmtservice/autocli.go#L52-L71 ``` +## Off-Chain + +Off-chain functionalities allow you to sign and verify files with two commands: + + `sign-file` for signing a file. + + `verify-file` for verifying a previously signed file. + +Signing a file will result in a Tx with a `MsgSignArbitraryData` as described in the [Off-chain CIP](https://github.com/cosmos/cips/blob/main/cips/cip-X.md). + +### Sign a file + +To sign a file `sign-file` command offers some helpful flags: +```text + --encoding string Choose an encoding method for the file content to be added as msg data (no-encoding|base64|hex) (default "no-encoding") + --indent string Choose an indent for the tx (default " ") + --notEmitUnpopulated Don't show unpopulated fields in the tx + --output string Choose an output format for the tx (json|text (default "json") + --output-document string The document will be written to the given file instead of STDOUT +``` + +The `encoding` flag lets you choose how the contents of the file should be encoded. For example: + + `simd off-chain sign-file alice myFile.json` + + ```json + { + "@type": "/offchain.MsgSignArbitraryData", + "appDomain": "simd", + "signer": "cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu", + "data": "Hello World!\n" + } + ``` + + `simd off-chain sign-file alice myFile.json --encoding base64` + + ```json + { + "@type": "/offchain.MsgSignArbitraryData", + "appDomain": "simd", + "signer": "cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu", + "data": "SGVsbG8gV29ybGQhCg==" + } + ``` ++ `simd off-chain sign-file alice myFile.json --encoding hex` + + ```json + { + "@type": "/offchain.MsgSignArbitraryData", + "appDomain": "simd", + "signer": "cosmos1x33fy6rusfprkntvjsfregss7rvsvyy4lkwrqu", + "data": "48656c6c6f20576f726c64210a" + } + ``` + +### Verify a file + +To verify a file only the key name used and the previously signed file are needed. +simd off-chain verify-file alice signedFile.json +```text +➜ simd off-chain verify-file alice signedFile.json +Verification OK! +``` + ## Summary `autocli` lets you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application. diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 2e1caf16e7bd..9533c6bb4f5a 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -75,9 +75,9 @@ func SignFile() *cobra.Command { } cmd.Flags().String(flagIndent, " ", "Choose an indent for the tx") - cmd.Flags().String(v2flags.FlagOutput, "json", "Choose output format (json|text") + cmd.Flags().String(v2flags.FlagOutput, "json", "Choose an output format for the tx (json|text") cmd.Flags().Bool(flagNotEmitUnpopulated, false, "Don't show unpopulated fields in the tx") - cmd.Flags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as the tx data (no-encoding|base64|hex)") + cmd.Flags().String(flagEncoding, "no-encoding", "Choose an encoding method for the file content to be added as msg data (no-encoding|base64|hex)") cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") return cmd } From e7a8fa834d9602eadeaafd8ef5086ff36edf3ea8 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 23 Jan 2024 12:26:30 +0100 Subject: [PATCH 31/33] gosec --- client/v2/offchain/cli.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/v2/offchain/cli.go b/client/v2/offchain/cli.go index 9533c6bb4f5a..1ff6011429dc 100644 --- a/client/v2/offchain/cli.go +++ b/client/v2/offchain/cli.go @@ -2,6 +2,7 @@ package offchain import ( "os" + "path/filepath" "github.com/spf13/cobra" @@ -62,7 +63,7 @@ func SignFile() *cobra.Command { } if outputFile != "" { - fp, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + fp, err := os.OpenFile(filepath.Clean(outputFile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { return err } From 6e78084d5145a0a3cb1b7493206af6ef268dd6f9 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 23 Jan 2024 13:18:17 +0100 Subject: [PATCH 32/33] fix --- client/v2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/v2/README.md b/client/v2/README.md index 16b40ed001b4..649972f3e134 100644 --- a/client/v2/README.md +++ b/client/v2/README.md @@ -260,7 +260,7 @@ The `encoding` flag lets you choose how the contents of the file should be encod ### Verify a file To verify a file only the key name used and the previously signed file are needed. -simd off-chain verify-file alice signedFile.json + ```text ➜ simd off-chain verify-file alice signedFile.json Verification OK! From 5494a37954cbfb703d13db58ca3644ed9301971a Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Wed, 24 Jan 2024 10:53:42 +0100 Subject: [PATCH 33/33] fix: readme --- client/v2/README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/client/v2/README.md b/client/v2/README.md index 649972f3e134..3e94e2c462ea 100644 --- a/client/v2/README.md +++ b/client/v2/README.md @@ -209,15 +209,23 @@ For example, here is how the SDK does it for `cometbft` gRPC commands: https://github.com/cosmos/cosmos-sdk/blob/main/client/grpc/cmtservice/autocli.go#L52-L71 ``` -## Off-Chain +## Summary + +`autocli` lets you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application. + +To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands. + +For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl). + +# Off-Chain Off-chain functionalities allow you to sign and verify files with two commands: - + `sign-file` for signing a file. - + `verify-file` for verifying a previously signed file. ++ `sign-file` for signing a file. ++ `verify-file` for verifying a previously signed file. Signing a file will result in a Tx with a `MsgSignArbitraryData` as described in the [Off-chain CIP](https://github.com/cosmos/cips/blob/main/cips/cip-X.md). -### Sign a file +## Sign a file To sign a file `sign-file` command offers some helpful flags: ```text @@ -229,8 +237,8 @@ To sign a file `sign-file` command offers some helpful flags: ``` The `encoding` flag lets you choose how the contents of the file should be encoded. For example: - + `simd off-chain sign-file alice myFile.json` - + ```json ++ `simd off-chain sign-file alice myFile.json` + + ```json { "@type": "/offchain.MsgSignArbitraryData", "appDomain": "simd", @@ -238,8 +246,8 @@ The `encoding` flag lets you choose how the contents of the file should be encod "data": "Hello World!\n" } ``` - + `simd off-chain sign-file alice myFile.json --encoding base64` - + ```json ++ `simd off-chain sign-file alice myFile.json --encoding base64` + + ```json { "@type": "/offchain.MsgSignArbitraryData", "appDomain": "simd", @@ -248,7 +256,7 @@ The `encoding` flag lets you choose how the contents of the file should be encod } ``` + `simd off-chain sign-file alice myFile.json --encoding hex` - + ```json + + ```json { "@type": "/offchain.MsgSignArbitraryData", "appDomain": "simd", @@ -256,8 +264,8 @@ The `encoding` flag lets you choose how the contents of the file should be encod "data": "48656c6c6f20576f726c64210a" } ``` - -### Verify a file + +## Verify a file To verify a file only the key name used and the previously signed file are needed. @@ -265,11 +273,3 @@ To verify a file only the key name used and the previously signed file are neede ➜ simd off-chain verify-file alice signedFile.json Verification OK! ``` - -## Summary - -`autocli` lets you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application. - -To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands. - -For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl).