diff --git a/CHANGELOG.md b/CHANGELOG.md index f493c1a28a9b..ffe6605b7c3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -209,6 +209,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/staking) [#14590](https://github.com/cosmos/cosmos-sdk/pull/14590) Return undelegate amount in MsgUndelegateResponse. * (baseapp) [#15023](https://github.com/cosmos/cosmos-sdk/pull/15023) & [#15213](https://github.com/cosmos/cosmos-sdk/pull/15213) Add `MessageRouter` interface to baseapp and pass it to authz, gov and groups instead of concrete type. * (x/consensus) [#15553](https://github.com/cosmos/cosmos-sdk/pull/15553) Migrate consensus module to use collections +* (baseapp) [#15038](https://github.com/cosmos/cosmos-sdk/pull/15038) Add non-atomic option for multi-message transactions * (store/cachekv) [#15767](https://github.com/cosmos/cosmos-sdk/pull/15767) Reduce peak RAM usage during and after InitGenesis * (x/bank) [#15764](https://github.com/cosmos/cosmos-sdk/pull/15764) Speedup x/bank InitGenesis * (x/auth) [#15867](https://github.com/cosmos/cosmos-sdk/pull/15867) Support better logging for signature verification failure. diff --git a/api/cosmos/gov/v1/gov.pulsar.go b/api/cosmos/gov/v1/gov.pulsar.go index c5df20627418..175c72422ad4 100644 --- a/api/cosmos/gov/v1/gov.pulsar.go +++ b/api/cosmos/gov/v1/gov.pulsar.go @@ -7182,7 +7182,8 @@ type Proposal struct { // voting_end_time is the end time of voting on a proposal. VotingEndTime *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=voting_end_time,json=votingEndTime,proto3" json:"voting_end_time,omitempty"` // metadata is any arbitrary metadata attached to the proposal. - // the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#proposal-3 + // the recommended format of the metadata is to be found here: + // https://docs.cosmos.network/v0.47/modules/gov#proposal-3 Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` // title is the title of the proposal // diff --git a/api/cosmos/msg/v1/msg.pulsar.go b/api/cosmos/msg/v1/msg.pulsar.go index 7bf986dd12c5..6cd27dc27ff1 100644 --- a/api/cosmos/msg/v1/msg.pulsar.go +++ b/api/cosmos/msg/v1/msg.pulsar.go @@ -2,12 +2,437 @@ package msgv1 import ( + fmt "fmt" + 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" descriptorpb "google.golang.org/protobuf/types/descriptorpb" + io "io" reflect "reflect" + sync "sync" ) +var ( + md_MsgFailureResponse protoreflect.MessageDescriptor + fd_MsgFailureResponse_error protoreflect.FieldDescriptor +) + +func init() { + file_cosmos_msg_v1_msg_proto_init() + md_MsgFailureResponse = File_cosmos_msg_v1_msg_proto.Messages().ByName("MsgFailureResponse") + fd_MsgFailureResponse_error = md_MsgFailureResponse.Fields().ByName("error") +} + +var _ protoreflect.Message = (*fastReflection_MsgFailureResponse)(nil) + +type fastReflection_MsgFailureResponse MsgFailureResponse + +func (x *MsgFailureResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgFailureResponse)(x) +} + +func (x *MsgFailureResponse) slowProtoReflect() protoreflect.Message { + mi := &file_cosmos_msg_v1_msg_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_MsgFailureResponse_messageType fastReflection_MsgFailureResponse_messageType +var _ protoreflect.MessageType = fastReflection_MsgFailureResponse_messageType{} + +type fastReflection_MsgFailureResponse_messageType struct{} + +func (x fastReflection_MsgFailureResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgFailureResponse)(nil) +} +func (x fastReflection_MsgFailureResponse_messageType) New() protoreflect.Message { + return new(fastReflection_MsgFailureResponse) +} +func (x fastReflection_MsgFailureResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgFailureResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgFailureResponse) Descriptor() protoreflect.MessageDescriptor { + return md_MsgFailureResponse +} + +// 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_MsgFailureResponse) Type() protoreflect.MessageType { + return _fastReflection_MsgFailureResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgFailureResponse) New() protoreflect.Message { + return new(fastReflection_MsgFailureResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgFailureResponse) Interface() protoreflect.ProtoMessage { + return (*MsgFailureResponse)(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_MsgFailureResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Error != "" { + value := protoreflect.ValueOfString(x.Error) + if !f(fd_MsgFailureResponse_error, 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_MsgFailureResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + return x.Error != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + x.Error = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + value := x.Error + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + x.Error = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + panic(fmt.Errorf("field error of message cosmos.msg.v1.MsgFailureResponse is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "cosmos.msg.v1.MsgFailureResponse.error": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.msg.v1.MsgFailureResponse")) + } + panic(fmt.Errorf("message cosmos.msg.v1.MsgFailureResponse 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_MsgFailureResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in cosmos.msg.v1.MsgFailureResponse", 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_MsgFailureResponse) 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_MsgFailureResponse) 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_MsgFailureResponse) 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_MsgFailureResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgFailureResponse) + 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.Error) + 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().(*MsgFailureResponse) + 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.Error) > 0 { + i -= len(x.Error) + copy(dAtA[i:], x.Error) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Error))) + 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().(*MsgFailureResponse) + 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: MsgFailureResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgFailureResponse: 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 Error", 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.Error = 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 @@ -21,6 +446,43 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// This is a message to be added as a response for failed messages +// when dealing with non-atomic multi-msg txs. +type MsgFailureResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *MsgFailureResponse) Reset() { + *x = MsgFailureResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cosmos_msg_v1_msg_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgFailureResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgFailureResponse) ProtoMessage() {} + +// Deprecated: Use MsgFailureResponse.ProtoReflect.Descriptor instead. +func (*MsgFailureResponse) Descriptor() ([]byte, []int) { + return file_cosmos_msg_v1_msg_proto_rawDescGZIP(), []int{0} +} + +func (x *MsgFailureResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + var file_cosmos_msg_v1_msg_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.ServiceOptions)(nil), @@ -74,34 +536,51 @@ var file_cosmos_msg_v1_msg_proto_rawDesc = []byte{ 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x76, 0x31, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x3c, 0x0a, 0x07, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xf0, 0x8c, 0xa6, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0xf0, 0x8c, 0xa6, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x72, 0x42, 0x99, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4d, 0x73, 0x67, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x24, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, - 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, - 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x73, 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, - 0x4d, 0x58, 0xaa, 0x02, 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x2e, - 0x56, 0x31, 0xca, 0x02, 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x73, 0x67, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x19, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x73, 0x67, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x0f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x4d, 0x73, 0x67, 0x3a, 0x3a, 0x56, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2a, 0x0a, 0x12, 0x4d, 0x73, + 0x67, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3a, 0x3c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0xf0, 0x8c, 0xa6, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x1f, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xf0, 0x8c, 0xa6, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, + 0x42, 0x99, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, + 0x6d, 0x73, 0x67, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4d, 0x73, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x24, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, + 0x76, 0x31, 0x3b, 0x6d, 0x73, 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x4d, 0x58, 0xaa, 0x02, + 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x73, 0x67, 0x5c, 0x56, 0x31, 0xe2, 0x02, + 0x19, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x73, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0f, 0x43, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x4d, 0x73, 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_cosmos_msg_v1_msg_proto_rawDescOnce sync.Once + file_cosmos_msg_v1_msg_proto_rawDescData = file_cosmos_msg_v1_msg_proto_rawDesc +) + +func file_cosmos_msg_v1_msg_proto_rawDescGZIP() []byte { + file_cosmos_msg_v1_msg_proto_rawDescOnce.Do(func() { + file_cosmos_msg_v1_msg_proto_rawDescData = protoimpl.X.CompressGZIP(file_cosmos_msg_v1_msg_proto_rawDescData) + }) + return file_cosmos_msg_v1_msg_proto_rawDescData } +var file_cosmos_msg_v1_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_cosmos_msg_v1_msg_proto_goTypes = []interface{}{ - (*descriptorpb.ServiceOptions)(nil), // 0: google.protobuf.ServiceOptions - (*descriptorpb.MessageOptions)(nil), // 1: google.protobuf.MessageOptions + (*MsgFailureResponse)(nil), // 0: cosmos.msg.v1.MsgFailureResponse + (*descriptorpb.ServiceOptions)(nil), // 1: google.protobuf.ServiceOptions + (*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions } var file_cosmos_msg_v1_msg_proto_depIdxs = []int32{ - 0, // 0: cosmos.msg.v1.service:extendee -> google.protobuf.ServiceOptions - 1, // 1: cosmos.msg.v1.signer:extendee -> google.protobuf.MessageOptions + 1, // 0: cosmos.msg.v1.service:extendee -> google.protobuf.ServiceOptions + 2, // 1: cosmos.msg.v1.signer:extendee -> google.protobuf.MessageOptions 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name @@ -114,18 +593,33 @@ func file_cosmos_msg_v1_msg_proto_init() { if File_cosmos_msg_v1_msg_proto != nil { return } + if !protoimpl.UnsafeEnabled { + file_cosmos_msg_v1_msg_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgFailureResponse); 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_cosmos_msg_v1_msg_proto_rawDesc, NumEnums: 0, - NumMessages: 0, + NumMessages: 1, NumExtensions: 2, NumServices: 0, }, GoTypes: file_cosmos_msg_v1_msg_proto_goTypes, DependencyIndexes: file_cosmos_msg_v1_msg_proto_depIdxs, + MessageInfos: file_cosmos_msg_v1_msg_proto_msgTypes, ExtensionInfos: file_cosmos_msg_v1_msg_proto_extTypes, }.Build() File_cosmos_msg_v1_msg_proto = out.File diff --git a/api/cosmos/tx/v1beta1/tx.pulsar.go b/api/cosmos/tx/v1beta1/tx.pulsar.go index e96c797a907f..4ef7f2de5f87 100644 --- a/api/cosmos/tx/v1beta1/tx.pulsar.go +++ b/api/cosmos/tx/v1beta1/tx.pulsar.go @@ -2767,6 +2767,7 @@ var ( fd_TxBody_messages protoreflect.FieldDescriptor fd_TxBody_memo protoreflect.FieldDescriptor fd_TxBody_timeout_height protoreflect.FieldDescriptor + fd_TxBody_non_atomic protoreflect.FieldDescriptor fd_TxBody_extension_options protoreflect.FieldDescriptor fd_TxBody_non_critical_extension_options protoreflect.FieldDescriptor ) @@ -2777,6 +2778,7 @@ func init() { fd_TxBody_messages = md_TxBody.Fields().ByName("messages") fd_TxBody_memo = md_TxBody.Fields().ByName("memo") fd_TxBody_timeout_height = md_TxBody.Fields().ByName("timeout_height") + fd_TxBody_non_atomic = md_TxBody.Fields().ByName("non_atomic") fd_TxBody_extension_options = md_TxBody.Fields().ByName("extension_options") fd_TxBody_non_critical_extension_options = md_TxBody.Fields().ByName("non_critical_extension_options") } @@ -2864,6 +2866,12 @@ func (x *fastReflection_TxBody) Range(f func(protoreflect.FieldDescriptor, proto return } } + if x.NonAtomic != false { + value := protoreflect.ValueOfBool(x.NonAtomic) + if !f(fd_TxBody_non_atomic, value) { + return + } + } if len(x.ExtensionOptions) != 0 { value := protoreflect.ValueOfList(&_TxBody_1023_list{list: &x.ExtensionOptions}) if !f(fd_TxBody_extension_options, value) { @@ -2897,6 +2905,8 @@ func (x *fastReflection_TxBody) Has(fd protoreflect.FieldDescriptor) bool { return x.Memo != "" case "cosmos.tx.v1beta1.TxBody.timeout_height": return x.TimeoutHeight != uint64(0) + case "cosmos.tx.v1beta1.TxBody.non_atomic": + return x.NonAtomic != false case "cosmos.tx.v1beta1.TxBody.extension_options": return len(x.ExtensionOptions) != 0 case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options": @@ -2923,6 +2933,8 @@ func (x *fastReflection_TxBody) Clear(fd protoreflect.FieldDescriptor) { x.Memo = "" case "cosmos.tx.v1beta1.TxBody.timeout_height": x.TimeoutHeight = uint64(0) + case "cosmos.tx.v1beta1.TxBody.non_atomic": + x.NonAtomic = false case "cosmos.tx.v1beta1.TxBody.extension_options": x.ExtensionOptions = nil case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options": @@ -2955,6 +2967,9 @@ func (x *fastReflection_TxBody) Get(descriptor protoreflect.FieldDescriptor) pro case "cosmos.tx.v1beta1.TxBody.timeout_height": value := x.TimeoutHeight return protoreflect.ValueOfUint64(value) + case "cosmos.tx.v1beta1.TxBody.non_atomic": + value := x.NonAtomic + return protoreflect.ValueOfBool(value) case "cosmos.tx.v1beta1.TxBody.extension_options": if len(x.ExtensionOptions) == 0 { return protoreflect.ValueOfList(&_TxBody_1023_list{}) @@ -2995,6 +3010,8 @@ func (x *fastReflection_TxBody) Set(fd protoreflect.FieldDescriptor, value proto x.Memo = value.Interface().(string) case "cosmos.tx.v1beta1.TxBody.timeout_height": x.TimeoutHeight = value.Uint() + case "cosmos.tx.v1beta1.TxBody.non_atomic": + x.NonAtomic = value.Bool() case "cosmos.tx.v1beta1.TxBody.extension_options": lv := value.List() clv := lv.(*_TxBody_1023_list) @@ -3045,6 +3062,8 @@ func (x *fastReflection_TxBody) Mutable(fd protoreflect.FieldDescriptor) protore panic(fmt.Errorf("field memo of message cosmos.tx.v1beta1.TxBody is not mutable")) case "cosmos.tx.v1beta1.TxBody.timeout_height": panic(fmt.Errorf("field timeout_height of message cosmos.tx.v1beta1.TxBody is not mutable")) + case "cosmos.tx.v1beta1.TxBody.non_atomic": + panic(fmt.Errorf("field non_atomic of message cosmos.tx.v1beta1.TxBody is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.tx.v1beta1.TxBody")) @@ -3065,6 +3084,8 @@ func (x *fastReflection_TxBody) NewField(fd protoreflect.FieldDescriptor) protor return protoreflect.ValueOfString("") case "cosmos.tx.v1beta1.TxBody.timeout_height": return protoreflect.ValueOfUint64(uint64(0)) + case "cosmos.tx.v1beta1.TxBody.non_atomic": + return protoreflect.ValueOfBool(false) case "cosmos.tx.v1beta1.TxBody.extension_options": list := []*anypb.Any{} return protoreflect.ValueOfList(&_TxBody_1023_list{list: &list}) @@ -3153,6 +3174,9 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods { if x.TimeoutHeight != 0 { n += 1 + runtime.Sov(uint64(x.TimeoutHeight)) } + if x.NonAtomic { + n += 2 + } if len(x.ExtensionOptions) > 0 { for _, e := range x.ExtensionOptions { l = options.Size(e) @@ -3230,6 +3254,16 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods { dAtA[i] = 0xfa } } + if x.NonAtomic { + i-- + if x.NonAtomic { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if x.TimeoutHeight != 0 { i = runtime.EncodeVarint(dAtA, i, uint64(x.TimeoutHeight)) i-- @@ -3392,6 +3426,26 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods { break } } + case 4: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field NonAtomic", wireType) + } + var v int + 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++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + x.NonAtomic = bool(v != 0) case 1023: if wireType != 2 { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType) @@ -8421,6 +8475,13 @@ type TxBody struct { // timeout is the block height after which this transaction will not // be processed by the chain TimeoutHeight uint64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"` + // non_atomic is an atomicity flag. If false, the transaction will be atomic + // and be rejected if any of the messages fail. This is the default and + // compatible with previous versions of the SDK and TxBody. + // + // If set to true, the transaction will be executed as long as at least one + // of the messages succeeds. + NonAtomic bool `protobuf:"varint,4,opt,name=non_atomic,json=nonAtomic,proto3" json:"non_atomic,omitempty"` // extension_options are arbitrary options that can be added by chains // when the default options are not sufficient. If any of these are present // and can't be handled, the transaction will be rejected @@ -8472,6 +8533,13 @@ func (x *TxBody) GetTimeoutHeight() uint64 { return 0 } +func (x *TxBody) GetNonAtomic() bool { + if x != nil { + return x.NonAtomic + } + return false +} + func (x *TxBody) GetExtensionOptions() []*anypb.Any { if x != nil { return x.ExtensionOptions diff --git a/api/tendermint/abci/types.pulsar.go b/api/tendermint/abci/types.pulsar.go index 8f6206311cb0..5d4db06158f5 100644 --- a/api/tendermint/abci/types.pulsar.go +++ b/api/tendermint/abci/types.pulsar.go @@ -32681,7 +32681,8 @@ type ResponseFinalizeBlock struct { ValidatorUpdates []*ValidatorUpdate `protobuf:"bytes,3,rep,name=validator_updates,json=validatorUpdates,proto3" json:"validator_updates,omitempty"` // updates to the consensus params, if any. ConsensusParamUpdates *types.ConsensusParams `protobuf:"bytes,4,opt,name=consensus_param_updates,json=consensusParamUpdates,proto3" json:"consensus_param_updates,omitempty"` - // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. + // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was + // deterministic. It is up to the application to decide which algorithm to use. AppHash []byte `protobuf:"bytes,5,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` } diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index 3e571274c86d..8b87bd168ffb 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -575,7 +575,7 @@ func TestABCI_CheckTx(t *testing.T) { require.NoError(t, err) for i := int64(0); i < nTxs; i++ { - tx := newTxCounter(t, suite.txConfig, i, 0) // no messages + tx := newTxCounter(t, suite.txConfig, Atomic, i, 0) // no messages txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -630,7 +630,7 @@ func TestABCI_FinalizeBlock_DeliverTx(t *testing.T) { txs := [][]byte{} for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) - tx := newTxCounter(t, suite.txConfig, counter, counter) + tx := newTxCounter(t, suite.txConfig, Atomic, counter, counter) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -678,7 +678,7 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) { // run a multi-msg tx // with all msgs the same route - tx := newTxCounter(t, suite.txConfig, 0, 0, 1, 2) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0, 1, 2) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -699,7 +699,7 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) { require.Equal(t, int64(3), msgCounter) // replace the second message with a Counter2 - tx = newTxCounter(t, suite.txConfig, 1, 3) + tx = newTxCounter(t, suite.txConfig, Atomic, 1, 3) builder := suite.txConfig.NewTxBuilder() msgs := tx.GetMsgs() @@ -737,6 +737,121 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) { require.Equal(t, int64(2), msgCounter2) } +func TestABCI_DeliverTx_NonAtomicMultiMsg(t *testing.T) { + anteKey := []byte("ante-key") + anteOpt := func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } + suite := NewBaseAppSuite(t, anteOpt) + + _, err := suite.baseApp.InitChain(&abci.RequestInitChain{ + ConsensusParams: &cmtproto.ConsensusParams{}, + }) + require.NoError(t, err) + + deliverKey := []byte("deliver-key") + baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, deliverKey}) + + // run a multi-msg tx + // with all msgs the same route + tx := newTxCounter(t, suite.txConfig, NonAtomic, 0, 0, 1, 2) + txBytes, err := suite.txConfig.TxEncoder()(tx) + require.NoError(t, err) + + res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: 1, + Txs: [][]byte{txBytes}, + }) + require.NoError(t, err) + + require.True(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res)) + + store := getFinalizeBlockStateCtx(suite.baseApp).KVStore(capKey1) + + // tx counter only incremented once + txCounter := getIntFromStore(t, store, anteKey) + require.Equal(t, int64(1), txCounter) + + // msg counter incremented three times + msgCounter := getIntFromStore(t, store, deliverKey) + require.Equal(t, int64(3), msgCounter) + + _, _, addr := testdata.KeyTestPubAddr() + + testCases := []struct { + name string + succeed []bool + finalCounter int64 + expErr bool + }{ + {"all messages succeed", []bool{true, true}, 5, false}, + {"all messages succeed2", []bool{true, true}, 7, false}, + {"first succeed", []bool{true, false}, 8, false}, + {"second succeed", []bool{false, true}, 9, false}, + {"all messages fail", []bool{false, false}, 9, true}, + } + + for testNum, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // replace the second message with a Counter2 + tx = newTxCounter(t, suite.txConfig, NonAtomic, int64(testNum+1), 3) + + builder := suite.txConfig.NewTxBuilder() + msgs := make([]sdk.Msg, 0, len(tc.succeed)) + + extra := 0 + for _, success := range tc.succeed { + msgs = append(msgs, &baseapptestutil.MsgCounter{Counter: msgCounter + int64(extra), FailOnHandler: !success, Signer: addr.String()}) + if success { + extra++ + } + } + err := builder.SetMsgs(msgs...) + require.NoError(t, err) + builder.SetMemo(tx.GetMemo()) + builder.SetNonAtomic(true) + setTxSignature(t, builder, 0) + + txBytes, err = suite.txConfig.TxEncoder()(builder.GetTx()) + require.NoError(t, err) + + res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: 1, + Txs: [][]byte{txBytes}, + }) + require.NoError(t, err) + + if tc.expErr { + require.True(t, res.TxResults[0].IsErr(), fmt.Sprintf("%v", res)) + } else { + require.True(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res)) + } + + resData := sdk.TxMsgData{} + err = suite.cdc.Unmarshal(res.TxResults[0].Data, &resData) + require.NoError(t, err) + if res.TxResults[0].IsOK() { + // If the whole tx fails, there are no msg responses in the result. + // We could include them, but it would be a breaking change. + for i, success := range tc.succeed { + if success { + require.Equal(t, "/MsgCreateCounterResponse", resData.MsgResponses[i].TypeUrl) + } else { + require.Equal(t, "/cosmos.msg.v1.MsgFailureResponse", resData.MsgResponses[i].TypeUrl) + } + } + } + + store = getFinalizeBlockStateCtx(suite.baseApp).KVStore(capKey1) + + // original counter increments by one + // new counter increments by two + internalCounter := getIntFromStore(t, store, deliverKey) + require.Equal(t, tc.finalCounter, internalCounter) + // Update msg counter + msgCounter = internalCounter + }) + } +} + func TestABCI_Query_SimulateTx(t *testing.T) { gasConsumed := uint64(5) anteOpt := func(bapp *baseapp.BaseApp) { @@ -758,7 +873,7 @@ func TestABCI_Query_SimulateTx(t *testing.T) { for blockN := 0; blockN < nBlocks; blockN++ { count := int64(blockN + 1) - tx := newTxCounter(t, suite.txConfig, count, count) + tx := newTxCounter(t, suite.txConfig, Atomic, count, count) txBytes, err := suite.txConfig.TxEncoder()(tx) require.Nil(t, err) @@ -849,14 +964,14 @@ func TestABCI_InvalidTransaction(t *testing.T) { tx signing.Tx fail bool }{ - {newTxCounter(t, suite.txConfig, 0, 0), false}, - {newTxCounter(t, suite.txConfig, -1, 0), false}, - {newTxCounter(t, suite.txConfig, 100, 100), false}, - {newTxCounter(t, suite.txConfig, 100, 5, 4, 3, 2, 1), false}, - - {newTxCounter(t, suite.txConfig, 0, -1), true}, - {newTxCounter(t, suite.txConfig, 0, 1, -2), true}, - {newTxCounter(t, suite.txConfig, 0, 1, 2, -10, 5), true}, + {newTxCounter(t, suite.txConfig, Atomic, 0, 0), false}, + {newTxCounter(t, suite.txConfig, Atomic, -1, 0), false}, + {newTxCounter(t, suite.txConfig, Atomic, 100, 100), false}, + {newTxCounter(t, suite.txConfig, Atomic, 100, 5, 4, 3, 2, 1), false}, + + {newTxCounter(t, suite.txConfig, Atomic, 0, -1), true}, + {newTxCounter(t, suite.txConfig, Atomic, 1, 0, -2), true}, + {newTxCounter(t, suite.txConfig, Atomic, 0, 1, 2, -10, 5), true}, } for _, testCase := range testCases { @@ -973,14 +1088,14 @@ func TestABCI_TxGasLimits(t *testing.T) { gasUsed int64 fail bool }{ - {newTxCounter(t, suite.txConfig, 0, 0), 0, false}, - {newTxCounter(t, suite.txConfig, 1, 1), 2, false}, - {newTxCounter(t, suite.txConfig, 9, 1), 10, false}, - {newTxCounter(t, suite.txConfig, 1, 9), 10, false}, - {newTxCounter(t, suite.txConfig, 10, 0), 10, false}, - - {newTxCounter(t, suite.txConfig, 9, 2), 11, true}, - {newTxCounter(t, suite.txConfig, 2, 9), 11, true}, + {newTxCounter(t, suite.txConfig, Atomic, 0, 0), 0, false}, + {newTxCounter(t, suite.txConfig, Atomic, 1, 1), 2, false}, + {newTxCounter(t, suite.txConfig, Atomic, 9, 1), 10, false}, + {newTxCounter(t, suite.txConfig, Atomic, 1, 9), 10, false}, + {newTxCounter(t, suite.txConfig, Atomic, 10, 0), 10, false}, + + {newTxCounter(t, suite.txConfig, Atomic, 9, 2), 11, true}, + {newTxCounter(t, suite.txConfig, Atomic, 2, 9), 11, true}, // {newTxCounter(t, suite.txConfig, 9, 1, 1), 11, true}, // {newTxCounter(t, suite.txConfig, 1, 8, 1, 1), 11, true}, // {newTxCounter(t, suite.txConfig, 11, 0), 11, true}, @@ -1066,11 +1181,11 @@ func TestABCI_MaxBlockGasLimits(t *testing.T) { fail bool failAfterDeliver int }{ - {newTxCounter(t, suite.txConfig, 0, 0), 0, 0, false, 0}, - {newTxCounter(t, suite.txConfig, 9, 1), 2, 10, false, 0}, - {newTxCounter(t, suite.txConfig, 10, 0), 3, 10, false, 0}, - {newTxCounter(t, suite.txConfig, 10, 0), 10, 10, false, 0}, - {newTxCounter(t, suite.txConfig, 2, 7), 11, 9, false, 0}, + {newTxCounter(t, suite.txConfig, Atomic, 0, 0), 0, 0, false, 0}, + {newTxCounter(t, suite.txConfig, Atomic, 9, 1), 2, 10, false, 0}, + {newTxCounter(t, suite.txConfig, Atomic, 0, 0), 3, 10, false, 0}, + {newTxCounter(t, suite.txConfig, Atomic, 10, 0), 10, 10, false, 0}, + {newTxCounter(t, suite.txConfig, Atomic, 2, 7), 11, 9, false, 0}, // {newTxCounter(t, suite.txConfig, 10, 0), 10, 10, false, 0}, // hit the limit but pass // {newTxCounter(t, suite.txConfig, 10, 0), 11, 10, true, 10}, @@ -1157,13 +1272,13 @@ func TestABCI_GasConsumptionBadTx(t *testing.T) { }) require.NoError(t, err) - tx := newTxCounter(t, suite.txConfig, 5, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 5, 0) tx = setFailOnAnte(t, suite.txConfig, tx, true) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) // require next tx to fail due to black gas limit - tx = newTxCounter(t, suite.txConfig, 5, 0) + tx = newTxCounter(t, suite.txConfig, Atomic, 5, 0) txBytes2, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -1199,7 +1314,7 @@ func TestABCI_Query(t *testing.T) { Path: "/store/key1/key", Data: key, } - tx := newTxCounter(t, suite.txConfig, 0, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0) // query is empty before we do anything res, err := suite.baseApp.Query(context.TODO(), &query) @@ -1410,7 +1525,7 @@ func TestABCI_Proposal_HappyPath(t *testing.T) { }) require.NoError(t, err) - tx := newTxCounter(t, suite.txConfig, 0, 1) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 1) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -1421,7 +1536,7 @@ func TestABCI_Proposal_HappyPath(t *testing.T) { _, err = suite.baseApp.CheckTx(&reqCheckTx) require.NoError(t, err) - tx2 := newTxCounter(t, suite.txConfig, 1, 1) + tx2 := newTxCounter(t, suite.txConfig, Atomic, 1, 1) tx2Bytes, err := suite.txConfig.TxEncoder()(tx2) require.NoError(t, err) @@ -1588,7 +1703,7 @@ func TestABCI_PrepareProposal_ReachedMaxBytes(t *testing.T) { require.NoError(t, err) for i := 0; i < 100; i++ { - tx2 := newTxCounter(t, suite.txConfig, int64(i), int64(i)) + tx2 := newTxCounter(t, suite.txConfig, Atomic, int64(i), int64(i)) err := pool.Insert(sdk.Context{}, tx2) require.NoError(t, err) } @@ -1617,7 +1732,7 @@ func TestABCI_PrepareProposal_BadEncoding(t *testing.T) { }) require.NoError(t, err) - tx := newTxCounter(t, suite.txConfig, 0, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0) err = pool.Insert(sdk.Context{}, tx) require.NoError(t, err) @@ -1724,7 +1839,7 @@ func TestABCI_PrepareProposal_Failures(t *testing.T) { }) require.NoError(t, err) - tx := newTxCounter(t, suite.txConfig, 0, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -1736,7 +1851,7 @@ func TestABCI_PrepareProposal_Failures(t *testing.T) { require.NoError(t, err) require.True(t, checkTxRes.IsOK()) - failTx := newTxCounter(t, suite.txConfig, 1, 1) + failTx := newTxCounter(t, suite.txConfig, Atomic, 1, 1) failTx = setFailOnAnte(t, suite.txConfig, failTx, true) err = pool.Insert(sdk.Context{}, failTx) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 84fb00a066f7..94bf55897d91 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -30,6 +30,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/mempool" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) type ( @@ -592,6 +595,36 @@ func (app *BaseApp) validateFinalizeBlockHeight(req *abci.RequestFinalizeBlock) return nil } +// validate that the tx is not signed using SIGN_MODE_AMINO_JSON if the tx is nonAtomic +func validateNonAtomicSignMode(tx sdk.Tx) error { + if !tx.IsNonAtomic() { + return nil + } + + sigTx, ok := tx.(authsigning.SigVerifiableTx) + if !ok { + return errorsmod.Wrap(ErrBadNonAtomicSignature, "tx must be a SigVerifiableTx") + } + + signatures, err := sigTx.GetSignaturesV2() + if err != nil { + return err + } + + for _, signer := range signatures { + signMode, ok := signer.Data.(*signing.SingleSignatureData) + if !ok { + return errorsmod.Wrap(ErrBadNonAtomicSignature, "tx signature must be a SingleSignatureData") + } + + if signMode.SignMode == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON { + return errorsmod.Wrap(ErrBadNonAtomicSignature, "tx must not be signed using SIGN_MODE_LEGACY_AMINO_JSON") + } + } + + return nil +} + // validateBasicTxMsgs executes basic validator calls for messages. func validateBasicTxMsgs(msgs []sdk.Msg) error { if len(msgs) == 0 { @@ -904,6 +937,11 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res return gInfo, nil, anteEvents, fmt.Errorf("failed to remove tx from mempool: %w", err) } + err = validateNonAtomicSignMode(tx) + if err != nil { + return gInfo, nil, anteEvents, + fmt.Errorf("invalid signature: %w", err) + } } // Create a new Context based off of the existing Context with a MultiStore branch @@ -916,7 +954,7 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res // Result if any single message fails or does not have a registered Handler. msgsV2, err := tx.GetMsgsV2() if err == nil { - result, err = app.runMsgs(runMsgCtx, msgs, msgsV2, mode) + result, err = app.runMsgs(runMsgCtx, msgs, msgsV2, mode, tx.IsNonAtomic()) } if err == nil { // Run optional postHandlers. @@ -957,25 +995,52 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res // and DeliverTx. An error is returned if any single message fails or if a // Handler does not exist for a given message route. Otherwise, a reference to a // Result is returned. The caller must not commit state if an error is returned. -func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, msgsV2 []protov2.Message, mode execMode) (*sdk.Result, error) { +func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, msgsV2 []protov2.Message, mode execMode, isNonAtomic bool) (*sdk.Result, error) { events := sdk.EmptyEvents() var msgResponses []*codectypes.Any + var txSuccess bool + msgCtx := ctx // This context may be replaced on each message for non-atomic execution. + // NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter. for i, msg := range msgs { if mode != execModeFinalize && mode != execModeSimulate { break } + // When dealing with non-atomic execution, we want each message to have its own context + var writeFn func() + if isNonAtomic { + msgCtx, writeFn = ctx.CacheContext() + } + handler := app.msgServiceRouter.Handler(msg) if handler == nil { return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "no message handler found for %T", msg) } // ADR 031 request type routing - msgResult, err := handler(ctx, msg) + msgResult, err := handler(msgCtx, msg) if err != nil { - return nil, errorsmod.Wrapf(err, "failed to execute message; message index: %d", i) + space, code, logInfo := errorsmod.ABCIInfo(err, true) + safeErr := errorsmod.ABCIError(space, code, logInfo) + wrappedErr := errorsmod.Wrapf(safeErr, "failed to execute message; message index: %d", i) + if isNonAtomic { + value, err := codectypes.NewAnyWithValue(&msgservice.MsgFailureResponse{Error: wrappedErr.Error()}) + if err != nil { + return nil, err + } + msgResponses = append(msgResponses, value) + continue + } + return nil, wrappedErr + } + // at least one message was executed successfully + txSuccess = true + + // Write the msg execution result to the context + if isNonAtomic { + writeFn() } // create message events @@ -1010,6 +1075,10 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, msgsV2 []protov2.Me } + if (mode == execModeFinalize || mode == execModeSimulate) && !txSuccess { + return nil, ErrMultiMsgFailure.Wrapf("failed to execute all messages in a non-atomic multi-message") + } + data, err := makeABCIData(msgResponses) if err != nil { return nil, errorsmod.Wrap(err, "failed to marshal tx data") diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 0fef6903b776..d854db1feb0a 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -460,7 +460,7 @@ func TestTxDecoder(t *testing.T) { // patch in TxConfig instead of using an output from x/auth/tx txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes) - tx := newTxCounter(t, txConfig, 1, 0) + tx := newTxCounter(t, txConfig, Atomic, 1, 0) txBytes, err := txConfig.TxEncoder()(tx) require.NoError(t, err) @@ -504,7 +504,7 @@ func TestCustomRunTxPanicHandler(t *testing.T) { // transaction should panic with custom handler above { - tx := newTxCounter(t, suite.txConfig, 0, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0) require.PanicsWithValue(t, customPanicMsg, func() { bz, err := suite.txConfig.TxEncoder()(tx) @@ -534,7 +534,7 @@ func TestBaseAppAnteHandler(t *testing.T) { // // NOTE: State should not be mutated here. This will be implicitly checked by // the next txs ante handler execution (anteHandlerTxTest). - tx := newTxCounter(t, suite.txConfig, 0, 0) + tx := newTxCounter(t, suite.txConfig, Atomic, 0, 0) tx = setFailOnAnte(t, suite.txConfig, tx, true) txBytes, err := suite.txConfig.TxEncoder()(tx) @@ -551,7 +551,7 @@ func TestBaseAppAnteHandler(t *testing.T) { // execute at tx that will pass the ante handler (the checkTx state should // mutate) but will fail the message handler - tx = newTxCounter(t, suite.txConfig, 0, 0) + tx = newTxCounter(t, suite.txConfig, Atomic, 0, 0) tx = setFailOnHandler(t, suite.txConfig, tx, true) txBytes, err = suite.txConfig.TxEncoder()(tx) @@ -569,7 +569,7 @@ func TestBaseAppAnteHandler(t *testing.T) { // Execute a successful ante handler and message execution where state is // implicitly checked by previous tx executions. - tx = newTxCounter(t, suite.txConfig, 1, 0) + tx = newTxCounter(t, suite.txConfig, Atomic, 1, 0) txBytes, err = suite.txConfig.TxEncoder()(tx) require.NoError(t, err) diff --git a/baseapp/errors.go b/baseapp/errors.go new file mode 100644 index 000000000000..6e6679fbd7a2 --- /dev/null +++ b/baseapp/errors.go @@ -0,0 +1,14 @@ +package baseapp + +import errorsmod "cosmossdk.io/errors" + +// RootCodespace is the codespace for all errors defined in this package +const RootCodespace = "baseapp" + +var ( + // ErrMultiMsgFailure defines an error for when a multi-message transaction fails. + ErrMultiMsgFailure = errorsmod.Register(RootCodespace, 2, "all messages failed") + + // ErrBadNonAtomicSignature defines an error for when a non-atomic transaction has a legacy or invalid signature + ErrBadNonAtomicSignature = errorsmod.Register(RootCodespace, 3, "bad signature for non-atomic txs") +) diff --git a/baseapp/streaming_test.go b/baseapp/streaming_test.go index 41f797a77b8b..c2087b205172 100644 --- a/baseapp/streaming_test.go +++ b/baseapp/streaming_test.go @@ -74,7 +74,7 @@ func TestABCI_MultiListener_StateChanges(t *testing.T) { for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) - tx := newTxCounter(t, suite.txConfig, counter, counter) + tx := newTxCounter(t, suite.txConfig, Atomic, counter, counter) txBytes, err := suite.txConfig.TxEncoder()(tx) require.NoError(t, err) diff --git a/baseapp/testutil/messages.proto b/baseapp/testutil/messages.proto index 1cce9dba5ded..d2b25d24c117 100644 --- a/baseapp/testutil/messages.proto +++ b/baseapp/testutil/messages.proto @@ -9,17 +9,17 @@ option go_package = "github.com/cosmos/cosmos-sdk/baseapp/testutil"; message MsgCounter { option (cosmos.msg.v1.signer) = "signer"; - int64 counter = 1; - bool fail_on_handler = 2; - string signer = 3; + int64 counter = 1; + bool fail_on_handler = 2; + string signer = 3; } message MsgCounter2 { option (cosmos.msg.v1.signer) = "signer"; - int64 counter = 1; - bool fail_on_handler = 2; - string signer = 3; + int64 counter = 1; + bool fail_on_handler = 2; + string signer = 3; } message MsgCreateCounterResponse {} diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go index 1cad8c905808..e7ccc619efd1 100644 --- a/baseapp/utils_test.go +++ b/baseapp/utils_test.go @@ -337,7 +337,14 @@ func parseTxMemo(t *testing.T, tx sdk.Tx) (counter int64, failOnAnte bool) { return counter, failOnAnte } -func newTxCounter(t *testing.T, cfg client.TxConfig, counter int64, msgCounters ...int64) signing.Tx { +type Atomicity uint8 + +const ( + Atomic Atomicity = iota + NonAtomic +) + +func newTxCounter(t *testing.T, cfg client.TxConfig, atomicity Atomicity, counter int64, msgCounters ...int64) signing.Tx { t.Helper() _, _, addr := testdata.KeyTestPubAddr() msgs := make([]sdk.Msg, 0, len(msgCounters)) @@ -350,6 +357,7 @@ func newTxCounter(t *testing.T, cfg client.TxConfig, counter int64, msgCounters err := builder.SetMsgs(msgs...) require.NoError(t, err) builder.SetMemo("counter=" + strconv.FormatInt(counter, 10) + "&failOnAnte=false") + builder.SetNonAtomic(atomicity == NonAtomic) setTxSignature(t, builder, uint64(counter)) return builder.GetTx() diff --git a/client/flags/flags.go b/client/flags/flags.go index c397b89be82e..918b2ff5ba72 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -58,6 +58,7 @@ const ( FlagAccountNumber = "account-number" FlagSequence = "sequence" FlagNote = "note" + FlagNonAtomic = "non-atomic" FlagFees = "fees" FlagGas = "gas" FlagGasPrices = "gas-prices" @@ -123,6 +124,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) { f.Uint64P(FlagAccountNumber, "a", 0, "The account number of the signing account (offline mode only)") f.Uint64P(FlagSequence, "s", 0, "The sequence number of the signing account (offline mode only)") f.String(FlagNote, "", "Note to add a description to the transaction (previously --memo)") + f.Bool(FlagNonAtomic, false, "Execute the transaction non-atomically. The transaction will succeed if at least one of the supplied messages succeeds. The success information of each message will be included in the events") f.String(FlagFees, "", "Fees to pay along with transaction; eg: 10uatom") f.String(FlagGasPrices, "", "Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)") f.String(FlagNode, "tcp://localhost:26657", ": to CometBFT rpc interface for this chain") diff --git a/client/tx/factory.go b/client/tx/factory.go index f30c8216c217..53a0d1d07d71 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -37,6 +37,7 @@ type Factory struct { offline bool generateOnly bool memo string + nonAtomic bool fees sdk.Coins tip *tx.Tip feeGranter sdk.AccAddress @@ -78,6 +79,7 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e gasAdj, _ := flagSet.GetFloat64(flags.FlagGasAdjustment) memo, _ := flagSet.GetString(flags.FlagNote) + nonAtomic, _ := flagSet.GetBool(flags.FlagNonAtomic) timeoutHeight, _ := flagSet.GetUint64(flags.FlagTimeoutHeight) gasStr, _ := flagSet.GetString(flags.FlagGas) @@ -97,6 +99,7 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e timeoutHeight: timeoutHeight, gasAdjustment: gasAdj, memo: memo, + nonAtomic: nonAtomic, signMode: signMode, feeGranter: clientCtx.FeeGranter, feePayer: clientCtx.FeePayer, diff --git a/client/tx_config.go b/client/tx_config.go index 4f6af35fa2a4..b15bd74add7e 100644 --- a/client/tx_config.go +++ b/client/tx_config.go @@ -44,6 +44,7 @@ type ( SetMsgs(msgs ...sdk.Msg) error SetSignatures(signatures ...signingtypes.SignatureV2) error SetMemo(memo string) + SetNonAtomic(nonAtomic bool) SetFeeAmount(amount sdk.Coins) SetFeePayer(feePayer sdk.AccAddress) SetGasLimit(limit uint64) diff --git a/orm/internal/testpb/bank_query.proto b/orm/internal/testpb/bank_query.proto index a204777bd4ba..6335d52eaf58 100644 --- a/orm/internal/testpb/bank_query.proto +++ b/orm/internal/testpb/bank_query.proto @@ -42,7 +42,7 @@ message ListBalanceRequest { // denom specifies the value of the Denom index key to use in the query. Denom denom = 2; } - + message AddressDenom { // address is the value of the address field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. @@ -51,14 +51,14 @@ message ListBalanceRequest { // It can be omitted to query for all valid values of that field in this segment of the index. optional string denom = 2; } - + message Denom { // denom is the value of the denom field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional string denom = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -68,7 +68,7 @@ message ListBalanceRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -110,14 +110,14 @@ message ListSupplyRequest { // denom specifies the value of the Denom index key to use in the query. Denom denom = 1; } - + message Denom { // denom is the value of the denom field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional string denom = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -127,7 +127,7 @@ message ListSupplyRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -147,4 +147,3 @@ message ListSupplyResponse { // pagination is the pagination response. cosmos.base.query.v1beta1.PageResponse pagination = 2; } - diff --git a/orm/internal/testpb/test_schema_query.proto b/orm/internal/testpb/test_schema_query.proto index 8e07ccfcfe80..c3dbcfd5d885 100644 --- a/orm/internal/testpb/test_schema_query.proto +++ b/orm/internal/testpb/test_schema_query.proto @@ -16,11 +16,15 @@ service TestSchemaQueryService { // ListExampleTable queries the ExampleTable table using prefix and range queries against defined indexes. rpc ListExampleTable(ListExampleTableRequest) returns (ListExampleTableResponse) {} // Get queries the ExampleAutoIncrementTable table by its primary key. - rpc GetExampleAutoIncrementTable(GetExampleAutoIncrementTableRequest) returns (GetExampleAutoIncrementTableResponse) {} + rpc GetExampleAutoIncrementTable(GetExampleAutoIncrementTableRequest) returns (GetExampleAutoIncrementTableResponse) { + } // GetExampleAutoIncrementTableByX queries the ExampleAutoIncrementTable table by its X index - rpc GetExampleAutoIncrementTableByX(GetExampleAutoIncrementTableByXRequest) returns (GetExampleAutoIncrementTableByXResponse) {} - // ListExampleAutoIncrementTable queries the ExampleAutoIncrementTable table using prefix and range queries against defined indexes. - rpc ListExampleAutoIncrementTable(ListExampleAutoIncrementTableRequest) returns (ListExampleAutoIncrementTableResponse) {} + rpc GetExampleAutoIncrementTableByX(GetExampleAutoIncrementTableByXRequest) + returns (GetExampleAutoIncrementTableByXResponse) {} + // ListExampleAutoIncrementTable queries the ExampleAutoIncrementTable table using prefix and range queries against + // defined indexes. + rpc ListExampleAutoIncrementTable(ListExampleAutoIncrementTableRequest) + returns (ListExampleAutoIncrementTableResponse) {} // GetExampleSingleton queries the ExampleSingleton singleton. rpc GetExampleSingleton(GetExampleSingletonRequest) returns (GetExampleSingletonResponse) {} // Get queries the ExampleTimestamp table by its primary key. @@ -39,7 +43,8 @@ service TestSchemaQueryService { rpc ListSimpleExample(ListSimpleExampleRequest) returns (ListSimpleExampleResponse) {} // Get queries the ExampleAutoIncFieldName table by its primary key. rpc GetExampleAutoIncFieldName(GetExampleAutoIncFieldNameRequest) returns (GetExampleAutoIncFieldNameResponse) {} - // ListExampleAutoIncFieldName queries the ExampleAutoIncFieldName table using prefix and range queries against defined indexes. + // ListExampleAutoIncFieldName queries the ExampleAutoIncFieldName table using prefix and range queries against + // defined indexes. rpc ListExampleAutoIncFieldName(ListExampleAutoIncFieldNameRequest) returns (ListExampleAutoIncFieldNameResponse) {} } @@ -85,7 +90,7 @@ message ListExampleTableRequest { // bz_str specifies the value of the BzStr index key to use in the query. BzStr bz_str = 4; } - + message U32I64Str { // u32 is the value of the u32 field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. @@ -97,7 +102,7 @@ message ListExampleTableRequest { // It can be omitted to query for all valid values of that field in this segment of the index. optional string str = 3; } - + message U64Str { // u64 is the value of the u64 field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. @@ -106,7 +111,7 @@ message ListExampleTableRequest { // It can be omitted to query for all valid values of that field in this segment of the index. optional string str = 2; } - + message StrU32 { // str is the value of the str field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. @@ -115,7 +120,7 @@ message ListExampleTableRequest { // It can be omitted to query for all valid values of that field in this segment of the index. optional uint32 u32 = 2; } - + message BzStr { // bz is the value of the bz field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. @@ -125,7 +130,7 @@ message ListExampleTableRequest { optional string str = 2; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -135,7 +140,7 @@ message ListExampleTableRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -189,20 +194,20 @@ message ListExampleAutoIncrementTableRequest { // x specifies the value of the X index key to use in the query. X x = 2; } - + message Id { // id is the value of the id field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional uint64 id = 1; } - + message X { // x is the value of the x field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional string x = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -212,7 +217,7 @@ message ListExampleAutoIncrementTableRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -234,8 +239,7 @@ message ListExampleAutoIncrementTableResponse { } // GetExampleSingletonRequest is the TestSchemaQuery/GetExampleSingletonRequest request type. -message GetExampleSingletonRequest { -} +message GetExampleSingletonRequest {} // GetExampleSingletonResponse is the TestSchemaQuery/GetExampleSingletonResponse request type. message GetExampleSingletonResponse { @@ -265,20 +269,20 @@ message ListExampleTimestampRequest { // ts specifies the value of the Ts index key to use in the query. Ts ts = 2; } - + message Id { // id is the value of the id field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional uint64 id = 1; } - + message Ts { // ts is the value of the ts field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional google.protobuf.Timestamp ts = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -288,7 +292,7 @@ message ListExampleTimestampRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -332,20 +336,20 @@ message ListExampleDurationRequest { // dur specifies the value of the Dur index key to use in the query. Dur dur = 2; } - + message Id { // id is the value of the id field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional uint64 id = 1; } - + message Dur { // dur is the value of the dur field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional google.protobuf.Duration dur = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -355,7 +359,7 @@ message ListExampleDurationRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -409,20 +413,20 @@ message ListSimpleExampleRequest { // unique specifies the value of the Unique index key to use in the query. Unique unique = 2; } - + message Name { // name is the value of the name field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional string name = 1; } - + message Unique { // unique is the value of the unique field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional string unique = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -432,7 +436,7 @@ message ListSimpleExampleRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -474,14 +478,14 @@ message ListExampleAutoIncFieldNameRequest { // foo specifies the value of the Foo index key to use in the query. Foo foo = 1; } - + message Foo { // foo is the value of the foo field in the index. // It can be omitted to query for all valid values of that field in this segment of the index. optional uint64 foo = 1; } } - + // query specifies the type of query - either a prefix or range query. oneof query { // prefix_query specifies the index key value to use for the prefix query. @@ -491,7 +495,7 @@ message ListExampleAutoIncFieldNameRequest { } // pagination specifies optional pagination parameters. cosmos.base.query.v1beta1.PageRequest pagination = 3; - + // RangeQuery specifies the from/to index keys for a range query. message RangeQuery { // from is the index key to use for the start of the range query. @@ -511,4 +515,3 @@ message ListExampleAutoIncFieldNameResponse { // pagination is the pagination response. cosmos.base.query.v1beta1.PageResponse pagination = 2; } - diff --git a/proto/cosmos/auth/v1beta1/auth.proto b/proto/cosmos/auth/v1beta1/auth.proto index bcac98314597..ebc18b69f052 100644 --- a/proto/cosmos/auth/v1beta1/auth.proto +++ b/proto/cosmos/auth/v1beta1/auth.proto @@ -18,15 +18,12 @@ message BaseAccount { option (cosmos_proto.implements_interface) = "cosmos.auth.v1beta1.AccountI"; - string address = 1 [ - (cosmos_proto.scalar) = "cosmos.AddressString"]; + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - google.protobuf.Any pub_key = 2 [ - (gogoproto.jsontag) = "public_key,omitempty", - (amino.field_name) = "public_key"]; + google.protobuf.Any pub_key = 2 [(gogoproto.jsontag) = "public_key,omitempty", (amino.field_name) = "public_key"]; - uint64 account_number = 3; - uint64 sequence = 4; + uint64 account_number = 3; + uint64 sequence = 4; } // ModuleAccount defines an account for modules that holds coins on a pool. diff --git a/proto/cosmos/base/abci/v1beta1/abci.proto b/proto/cosmos/base/abci/v1beta1/abci.proto index fc3c0c6da503..9e3b4e55dbc7 100644 --- a/proto/cosmos/base/abci/v1beta1/abci.proto +++ b/proto/cosmos/base/abci/v1beta1/abci.proto @@ -27,10 +27,7 @@ message TxResponse { // non-deterministic. string raw_log = 6; // The output of the application's logger (typed). May be non-deterministic. - repeated ABCIMessageLog logs = 7 [ - (gogoproto.castrepeated) = "ABCIMessageLogs", - (gogoproto.nullable) = false - ]; + repeated ABCIMessageLog logs = 7 [(gogoproto.castrepeated) = "ABCIMessageLogs", (gogoproto.nullable) = false]; // Additional information. May be non-deterministic. string info = 8; // Amount of gas requested for transaction. @@ -61,8 +58,7 @@ message ABCIMessageLog { // Events contains a slice of Event objects that were emitted during some // execution. - repeated StringEvent events = 3 - [(gogoproto.castrepeated) = "StringEvents", (gogoproto.nullable) = false]; + repeated StringEvent events = 3 [(gogoproto.castrepeated) = "StringEvents", (gogoproto.nullable) = false]; } // StringEvent defines en Event object wrapper where all the attributes diff --git a/proto/cosmos/base/node/v1beta1/query.proto b/proto/cosmos/base/node/v1beta1/query.proto index 78fd9f6703a3..33dd9667ba32 100644 --- a/proto/cosmos/base/node/v1beta1/query.proto +++ b/proto/cosmos/base/node/v1beta1/query.proto @@ -35,9 +35,9 @@ message StatusRequest {} // StateResponse defines the response structure for the status of a node. message StatusResponse { - uint64 earliest_store_height = 1; // earliest block height available in the store - uint64 height = 2; // current block height - google.protobuf.Timestamp timestamp = 3 [(gogoproto.stdtime) = true]; // block height timestamp - bytes app_hash = 4; // app hash of the current block - bytes validator_hash = 5; // validator hash provided by the consensus header + uint64 earliest_store_height = 1; // earliest block height available in the store + uint64 height = 2; // current block height + google.protobuf.Timestamp timestamp = 3 [(gogoproto.stdtime) = true]; // block height timestamp + bytes app_hash = 4; // app hash of the current block + bytes validator_hash = 5; // validator hash provided by the consensus header } diff --git a/proto/cosmos/consensus/v1/tx.proto b/proto/cosmos/consensus/v1/tx.proto index 86ee7e245a6f..4323de703a5e 100644 --- a/proto/cosmos/consensus/v1/tx.proto +++ b/proto/cosmos/consensus/v1/tx.proto @@ -23,7 +23,7 @@ service Msg { // MsgUpdateParams is the Msg/UpdateParams request type. message MsgUpdateParams { option (cosmos.msg.v1.signer) = "authority"; - option (amino.name) = "cosmos-sdk/x/consensus/MsgUpdateParams"; + option (amino.name) = "cosmos-sdk/x/consensus/MsgUpdateParams"; // authority is the address that controls the module (defaults to x/gov unless overwritten). string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; diff --git a/proto/cosmos/distribution/v1beta1/tx.proto b/proto/cosmos/distribution/v1beta1/tx.proto index fc14c5e10c53..32318bd81219 100644 --- a/proto/cosmos/distribution/v1beta1/tx.proto +++ b/proto/cosmos/distribution/v1beta1/tx.proto @@ -192,14 +192,14 @@ message MsgCommunityPoolSpendResponse {} // // Since: cosmos-sdk 0.50 message MsgDepositValidatorRewardsPool { - option (amino.name) = "cosmos-sdk/distr/MsgDepositValRewards"; + option (amino.name) = "cosmos-sdk/distr/MsgDepositValRewards"; option (cosmos.msg.v1.signer) = "depositor"; option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; - string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; - string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"]; + string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"]; repeated cosmos.base.v1beta1.Coin amount = 3 [ (gogoproto.nullable) = false, (amino.encoding) = "legacy_coins", diff --git a/proto/cosmos/gov/v1/gov.proto b/proto/cosmos/gov/v1/gov.proto index 8b34e6f63032..c6ec98a41497 100644 --- a/proto/cosmos/gov/v1/gov.proto +++ b/proto/cosmos/gov/v1/gov.proto @@ -80,7 +80,8 @@ message Proposal { google.protobuf.Timestamp voting_end_time = 9 [(gogoproto.stdtime) = true]; // metadata is any arbitrary metadata attached to the proposal. - // the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#proposal-3 + // the recommended format of the metadata is to be found here: + // https://docs.cosmos.network/v0.47/modules/gov#proposal-3 string metadata = 10; // title is the title of the proposal @@ -249,14 +250,15 @@ message Params { string expedited_threshold = 11 [(cosmos_proto.scalar) = "cosmos.Dec"]; // Minimum expedited deposit for a proposal to enter voting period. - repeated cosmos.base.v1beta1.Coin expedited_min_deposit = 12 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + repeated cosmos.base.v1beta1.Coin expedited_min_deposit = 12 + [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; // burn deposits if a proposal does not meet quorum bool burn_vote_quorum = 13; - + // burn deposits if the proposal does not enter voting period bool burn_proposal_deposit_prevote = 14; - + // burn deposits if quorum with vote type no_veto is met bool burn_vote_veto = 15; } diff --git a/proto/cosmos/msg/v1/msg.proto b/proto/cosmos/msg/v1/msg.proto index 853efa1f7c0f..0bc3ce8e2823 100644 --- a/proto/cosmos/msg/v1/msg.proto +++ b/proto/cosmos/msg/v1/msg.proto @@ -28,3 +28,9 @@ extend google.protobuf.MessageOptions { // a message inside the cosmos message. repeated string signer = 11110000; } + +// This is a message to be added as a response for failed messages +// when dealing with non-atomic multi-msg txs. +message MsgFailureResponse { + string error = 1; +} diff --git a/proto/cosmos/slashing/v1beta1/slashing.proto b/proto/cosmos/slashing/v1beta1/slashing.proto index ae2f2135f936..2347f34973cc 100644 --- a/proto/cosmos/slashing/v1beta1/slashing.proto +++ b/proto/cosmos/slashing/v1beta1/slashing.proto @@ -23,11 +23,8 @@ message ValidatorSigningInfo { // signed_blocks_window param determines the index in the missed block bitmap. int64 index_offset = 3; // Timestamp until which the validator is jailed due to liveness downtime. - google.protobuf.Timestamp jailed_until = 4 [ - (gogoproto.stdtime) = true, - (gogoproto.nullable) = false, - (amino.dont_omitempty) = true - ]; + google.protobuf.Timestamp jailed_until = 4 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true]; // Whether or not a validator has been tombstoned (killed out of validator // set). It is set once the validator commits an equivocation or for any other // configured misbehavior. @@ -48,11 +45,8 @@ message Params { (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; - google.protobuf.Duration downtime_jail_duration = 3 [ - (gogoproto.nullable) = false, - (amino.dont_omitempty) = true, - (gogoproto.stdduration) = true - ]; + google.protobuf.Duration downtime_jail_duration = 3 + [(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true]; bytes slash_fraction_double_sign = 4 [ (cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", diff --git a/proto/cosmos/staking/v1beta1/tx.proto b/proto/cosmos/staking/v1beta1/tx.proto index d3d0d8baa463..238645ba9232 100644 --- a/proto/cosmos/staking/v1beta1/tx.proto +++ b/proto/cosmos/staking/v1beta1/tx.proto @@ -159,7 +159,7 @@ message MsgUndelegateResponse { // amount returns the amount of undelegated coins // // Since: cosmos-sdk 0.50 - cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; } // MsgCancelUnbondingDelegation defines the SDK message for performing a cancel unbonding delegation for delegator diff --git a/proto/cosmos/store/v1beta1/commit_info.proto b/proto/cosmos/store/v1beta1/commit_info.proto index a931e3e7d762..37459a0d05a1 100644 --- a/proto/cosmos/store/v1beta1/commit_info.proto +++ b/proto/cosmos/store/v1beta1/commit_info.proto @@ -11,8 +11,7 @@ option go_package = "cosmossdk.io/store/types"; message CommitInfo { int64 version = 1; repeated StoreInfo store_infos = 2 [(gogoproto.nullable) = false]; - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp timestamp = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } // StoreInfo defines store-specific commit information. It contains a reference diff --git a/proto/cosmos/store/v1beta1/listening.proto b/proto/cosmos/store/v1beta1/listening.proto index 24131a47df53..14bc2e7a78fd 100644 --- a/proto/cosmos/store/v1beta1/listening.proto +++ b/proto/cosmos/store/v1beta1/listening.proto @@ -11,8 +11,8 @@ option go_package = "cosmossdk.io/store/types"; // // Since: cosmos-sdk 0.43 message StoreKVPair { - string store_key = 1; // the store key for the KVStore this pair originates from - bool delete = 2; // true indicates a delete operation, false indicates a set operation + string store_key = 1; // the store key for the KVStore this pair originates from + bool delete = 2; // true indicates a delete operation, false indicates a set operation bytes key = 3; bytes value = 4; } @@ -22,7 +22,7 @@ message StoreKVPair { message BlockMetadata { tendermint.abci.ResponseCommit response_commit = 6; tendermint.abci.RequestFinalizeBlock request_finalize_block = 7; - tendermint.abci.ResponseFinalizeBlock response_finalize_block = 8; // TODO: should we renumber this? + tendermint.abci.ResponseFinalizeBlock response_finalize_block = 8; // TODO: should we renumber this? - reserved 1, 2, 3, 4, 5; // reserved for from previous use in comet <= 0.37 + reserved 1, 2, 3, 4, 5; // reserved for from previous use in comet <= 0.37 } diff --git a/proto/cosmos/tx/v1beta1/service.proto b/proto/cosmos/tx/v1beta1/service.proto index 5cd848e09afc..7e19a1865259 100644 --- a/proto/cosmos/tx/v1beta1/service.proto +++ b/proto/cosmos/tx/v1beta1/service.proto @@ -37,8 +37,7 @@ service Service { // GetBlockWithTxs fetches a block with decoded txs. // // Since: cosmos-sdk 0.45.2 - rpc GetBlockWithTxs(GetBlockWithTxsRequest) - returns (GetBlockWithTxsResponse) { + rpc GetBlockWithTxs(GetBlockWithTxsRequest) returns (GetBlockWithTxsResponse) { option (google.api.http).get = "/cosmos/tx/v1beta1/txs/block/{height}"; } // TxDecode decodes the transaction. diff --git a/proto/cosmos/tx/v1beta1/tx.proto b/proto/cosmos/tx/v1beta1/tx.proto index 04f98cf57f63..a90480a9e5e8 100644 --- a/proto/cosmos/tx/v1beta1/tx.proto +++ b/proto/cosmos/tx/v1beta1/tx.proto @@ -117,6 +117,14 @@ message TxBody { // be processed by the chain uint64 timeout_height = 3; + // non_atomic is an atomicity flag. If false, the transaction will be atomic + // and be rejected if any of the messages fail. This is the default and + // compatible with previous versions of the SDK and TxBody. + // + // If set to true, the transaction will be executed as long as at least one + // of the messages succeeds. + bool non_atomic = 4; + // extension_options are arbitrary options that can be added by chains // when the default options are not sufficient. If any of these are present // and can't be handled, the transaction will be rejected diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 73151984ff95..961ba321621c 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -25,10 +25,8 @@ service ABCI { rpc InitChain(RequestInitChain) returns (ResponseInitChain); rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); - rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) - returns (ResponseLoadSnapshotChunk); - rpc ApplySnapshotChunk(RequestApplySnapshotChunk) - returns (ResponseApplySnapshotChunk); + rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); + rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); rpc PrepareProposal(RequestPrepareProposal) returns (ResponsePrepareProposal); rpc ProcessProposal(RequestProcessProposal) returns (ResponseProcessProposal); rpc ExtendVote(RequestExtendVote) returns (ResponseExtendVote); @@ -58,7 +56,7 @@ message Request { RequestVerifyVoteExtension verify_vote_extension = 19; RequestFinalizeBlock finalize_block = 20; } - reserved 4, 7, 9, 10; // SetOption, BeginBlock, DeliverTx, EndBlock + reserved 4, 7, 9, 10; // SetOption, BeginBlock, DeliverTx, EndBlock } message RequestEcho { @@ -75,8 +73,7 @@ message RequestInfo { } message RequestInitChain { - google.protobuf.Timestamp time = 1 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; string chain_id = 2; tendermint.types.ConsensusParams consensus_params = 3; repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; @@ -108,8 +105,8 @@ message RequestListSnapshots {} // offers a snapshot to the application message RequestOfferSnapshot { - Snapshot snapshot = 1; // snapshot offered by peers - bytes app_hash = 2; // light client-verified app hash for snapshot height + Snapshot snapshot = 1; // snapshot offered by peers + bytes app_hash = 2; // light client-verified app hash for snapshot height } // loads a snapshot chunk @@ -157,7 +154,7 @@ message RequestProcessProposal { // Extends a vote with application-injected data message RequestExtendVote { // the hash of the block that this vote may be referring to - bytes hash = 1; + bytes hash = 1; // the height of the extended vote int64 height = 2; } @@ -165,7 +162,7 @@ message RequestExtendVote { // Verify the vote extension message RequestVerifyVoteExtension { // the hash of the block that this received vote corresponds to - bytes hash = 1; + bytes hash = 1; // the validator that signed the vote extension bytes validator_address = 2; int64 height = 3; @@ -208,7 +205,7 @@ message Response { ResponseVerifyVoteExtension verify_vote_extension = 20; ResponseFinalizeBlock finalize_block = 21; } - reserved 5, 8, 10, 11; // SetOption, BeginBlock, DeliverTx, EndBlock + reserved 5, 8, 10, 11; // SetOption, BeginBlock, DeliverTx, EndBlock } // nondeterministic @@ -241,8 +238,8 @@ message ResponseInitChain { message ResponseQuery { uint32 code = 1; // bytes data = 2; // use "value" instead. - string log = 3; // nondeterministic - string info = 4; // nondeterministic + string log = 3; // nondeterministic + string info = 4; // nondeterministic int64 index = 5; bytes key = 6; bytes value = 7; @@ -254,13 +251,12 @@ message ResponseQuery { message ResponseCheckTx { uint32 code = 1; bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic + string log = 3; // nondeterministic + string info = 4; // nondeterministic int64 gas_wanted = 5 [json_name = "gas_wanted"]; int64 gas_used = 6 [json_name = "gas_used"]; - repeated Event events = 7 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; - string codespace = 8; + repeated Event events = 7 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; + string codespace = 8; // These reserved fields were used until v0.37 by the priority mempool (now // removed). @@ -269,7 +265,7 @@ message ResponseCheckTx { } message ResponseCommit { - reserved 1, 2; // data was previously returned here + reserved 1, 2; // data was previously returned here int64 retain_height = 3; } @@ -281,12 +277,12 @@ message ResponseOfferSnapshot { Result result = 1; enum Result { - UNKNOWN = 0; // Unknown result, abort all snapshot restoration - ACCEPT = 1; // Snapshot accepted, apply chunks - ABORT = 2; // Abort all snapshot restoration - REJECT = 3; // Reject this specific snapshot, try others - REJECT_FORMAT = 4; // Reject all snapshots of this format, try others - REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Snapshot accepted, apply chunks + ABORT = 2; // Abort all snapshot restoration + REJECT = 3; // Reject this specific snapshot, try others + REJECT_FORMAT = 4; // Reject all snapshots of this format, try others + REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others } } @@ -296,16 +292,16 @@ message ResponseLoadSnapshotChunk { message ResponseApplySnapshotChunk { Result result = 1; - repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply - repeated string reject_senders = 3; // Chunk senders to reject and ban + repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply + repeated string reject_senders = 3; // Chunk senders to reject and ban enum Result { - UNKNOWN = 0; // Unknown result, abort all snapshot restoration - ACCEPT = 1; // Chunk successfully accepted - ABORT = 2; // Abort all snapshot restoration - RETRY = 3; // Retry chunk (combine with refetch and reject) - RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) - REJECT_SNAPSHOT = 5; // Reject this snapshot, try others + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Chunk successfully accepted + ABORT = 2; // Abort all snapshot restoration + RETRY = 3; // Retry chunk (combine with refetch and reject) + RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) + REJECT_SNAPSHOT = 5; // Reject this snapshot, try others } } @@ -337,14 +333,13 @@ message ResponseVerifyVoteExtension { // Incorrectly implementing this thus has liveness implications as it may affect // CometBFT's ability to receive 2/3+ valid votes to finalize the block. // Honest nodes should never be rejected. - REJECT = 2; + REJECT = 2; } } message ResponseFinalizeBlock { // set of block events emmitted as part of executing the block - repeated Event events = 1 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; + repeated Event events = 1 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // the result of executing each transaction including the events // the particular transction emitted. This should match the order // of the transactions delivered in the block itself @@ -353,7 +348,8 @@ message ResponseFinalizeBlock { repeated ValidatorUpdate validator_updates = 3 [(gogoproto.nullable) = false]; // updates to the consensus params, if any. tendermint.types.ConsensusParams consensus_param_updates = 4; - // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. + // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was + // deterministic. It is up to the application to decide which algorithm to use. bytes app_hash = 5; } @@ -381,17 +377,14 @@ message ExtendedCommitInfo { // Later, transactions may be queried using these events. message Event { string type = 1; - repeated EventAttribute attributes = 2 [ - (gogoproto.nullable) = false, - (gogoproto.jsontag) = "attributes,omitempty" - ]; + repeated EventAttribute attributes = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "attributes,omitempty"]; } // EventAttribute is a single key-value pair, associated with an event. message EventAttribute { string key = 1; string value = 2; - bool index = 3; // nondeterministic + bool index = 3; // nondeterministic } // ExecTxResult contains results of executing one individual transaction. @@ -400,12 +393,12 @@ message EventAttribute { message ExecTxResult { uint32 code = 1; bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic + string log = 3; // nondeterministic + string info = 4; // nondeterministic int64 gas_wanted = 5; int64 gas_used = 6; repeated Event events = 7 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic string codespace = 8; } @@ -423,9 +416,9 @@ message TxResult { // Blockchain Types message Validator { - bytes address = 1; // The first 20 bytes of SHA256(public key) + bytes address = 1; // The first 20 bytes of SHA256(public key) // PubKey pub_key = 2 [(gogoproto.nullable)=false]; - int64 power = 3; // The voting power + int64 power = 3; // The voting power } message ValidatorUpdate { @@ -434,7 +427,7 @@ message ValidatorUpdate { } message VoteInfo { - Validator validator = 1 [(gogoproto.nullable) = false]; + Validator validator = 1 [(gogoproto.nullable) = false]; tendermint.types.BlockIDFlag block_id_flag = 3; reserved 2; // signed_last_block @@ -466,8 +459,7 @@ message Misbehavior { // The height when the offense occurred int64 height = 3; // The corresponding time where the offense occurred - google.protobuf.Timestamp time = 4 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; // Total voting power of the validator set in case the ABCI application does // not store historical validators. // https://github.com/tendermint/tendermint/issues/4581 @@ -478,9 +470,9 @@ message Misbehavior { // State Sync Types message Snapshot { - uint64 height = 1; // The height at which the snapshot was taken - uint32 format = 2; // The application-specific snapshot format - uint32 chunks = 3; // Number of chunks in the snapshot - bytes hash = 4; // Arbitrary snapshot hash, equal only if identical - bytes metadata = 5; // Arbitrary application metadata + uint64 height = 1; // The height at which the snapshot was taken + uint32 format = 2; // The application-specific snapshot format + uint32 chunks = 3; // Number of chunks in the snapshot + bytes hash = 4; // Arbitrary snapshot hash, equal only if identical + bytes metadata = 5; // Arbitrary application metadata } diff --git a/proto/tendermint/types/evidence.proto b/proto/tendermint/types/evidence.proto index 1f35049bdc4f..06f30ec2f5f3 100644 --- a/proto/tendermint/types/evidence.proto +++ b/proto/tendermint/types/evidence.proto @@ -30,7 +30,7 @@ message LightClientAttackEvidence { int64 common_height = 2; repeated tendermint.types.Validator byzantine_validators = 3; int64 total_voting_power = 4; - google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } message EvidenceList { diff --git a/proto/tendermint/types/params.proto b/proto/tendermint/types/params.proto index f96a2e2f5726..6a42d5ef0da7 100644 --- a/proto/tendermint/types/params.proto +++ b/proto/tendermint/types/params.proto @@ -27,7 +27,7 @@ message BlockParams { // Note: must be greater or equal to -1 int64 max_gas = 2; - reserved 3; // was TimeIotaMs see https://github.com/tendermint/tendermint/pull/5792 + reserved 3; // was TimeIotaMs see https://github.com/tendermint/tendermint/pull/5792 } // EvidenceParams determine how we handle evidence of malfeasance. @@ -43,8 +43,7 @@ message EvidenceParams { // It should correspond with an app's "unbonding period" or other similar // mechanism for handling [Nothing-At-Stake // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). - google.protobuf.Duration max_age_duration = 2 - [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; + google.protobuf.Duration max_age_duration = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; // This sets the maximum size of total evidence in bytes that can be committed in a single block. // and should fall comfortably under the max block bytes. diff --git a/proto/tendermint/types/types.proto b/proto/tendermint/types/types.proto index a527e2ffb217..a0d545ad9ee7 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -55,19 +55,19 @@ message Header { BlockID last_block_id = 5 [(gogoproto.nullable) = false]; // hashes of block data - bytes last_commit_hash = 6; // commit from validators from the last block - bytes data_hash = 7; // transactions + bytes last_commit_hash = 6; // commit from validators from the last block + bytes data_hash = 7; // transactions // hashes from the app output from the prev block - bytes validators_hash = 8; // validators for the current block - bytes next_validators_hash = 9; // validators for the next block - bytes consensus_hash = 10; // consensus params for current block - bytes app_hash = 11; // state after txs from the previous block - bytes last_results_hash = 12; // root hash of all results from the txs from the previous block + bytes validators_hash = 8; // validators for the current block + bytes next_validators_hash = 9; // validators for the next block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block // consensus info - bytes evidence_hash = 13; // evidence included in the block - bytes proposer_address = 14; // original proposer of the block + bytes evidence_hash = 13; // evidence included in the block + bytes proposer_address = 14; // original proposer of the block } // Data contains the set of transactions included in the block @@ -84,12 +84,10 @@ message Vote { SignedMsgType type = 1; int64 height = 2; int32 round = 3; - BlockID block_id = 4 - [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. - google.protobuf.Timestamp timestamp = 5 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes validator_address = 6; - int32 validator_index = 7; + BlockID block_id = 4 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes validator_address = 6; + int32 validator_index = 7; // Vote signature by the validator if they participated in consensus for the // associated block. bytes signature = 8; @@ -114,16 +112,14 @@ message Commit { message CommitSig { tendermint.types.BlockIDFlag block_id_flag = 1; bytes validator_address = 2; - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 4; + google.protobuf.Timestamp timestamp = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 4; } message ExtendedCommit { - int64 height = 1; - int32 round = 2; - BlockID block_id = 3 - [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; + int64 height = 1; + int32 round = 2; + BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; repeated ExtendedCommitSig extended_signatures = 4 [(gogoproto.nullable) = false]; } @@ -133,9 +129,8 @@ message ExtendedCommit { message ExtendedCommitSig { tendermint.types.BlockIDFlag block_id_flag = 1; bytes validator_address = 2; - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 4; + google.protobuf.Timestamp timestamp = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 4; // Vote extension data bytes extension = 5; // Vote extension signature @@ -148,9 +143,8 @@ message Proposal { int32 round = 3; int32 pol_round = 4; BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - google.protobuf.Timestamp timestamp = 6 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 7; + google.protobuf.Timestamp timestamp = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 7; } message SignedHeader { diff --git a/proto/tendermint/types/validator.proto b/proto/tendermint/types/validator.proto index 7b55956fcdd1..cd5105fac765 100644 --- a/proto/tendermint/types/validator.proto +++ b/proto/tendermint/types/validator.proto @@ -11,13 +11,13 @@ enum BlockIDFlag { option (gogoproto.goproto_enum_stringer) = true; option (gogoproto.goproto_enum_prefix) = false; - BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; // indicates an error condition - BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received - BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority - BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil + BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; // indicates an error condition + BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received + BLOCK_ID_FLAG_COMMIT = 2 + [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority + BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil } - message ValidatorSet { repeated Validator validators = 1; Validator proposer = 2; diff --git a/server/mock/tx.go b/server/mock/tx.go index f8bdef446a9a..168fb1843343 100644 --- a/server/mock/tx.go +++ b/server/mock/tx.go @@ -121,6 +121,10 @@ func (msg *KVStoreTx) GetSigners() ([][]byte, error) { func (msg *KVStoreTx) GetPubKeys() ([]cryptotypes.PubKey, error) { panic("GetPubKeys not implemented") } +func (msg *KVStoreTx) IsNonAtomic() bool { + return false +} + // takes raw transaction bytes and decodes them into an sdk.Tx. An sdk.Tx has // all the signatures and can be used to authenticate. func decodeTx(txBytes []byte) (sdk.Tx, error) { diff --git a/tests/e2e/tx/service_test.go b/tests/e2e/tx/service_test.go index fa878a901f84..f695ace86d13 100644 --- a/tests/e2e/tx/service_test.go +++ b/tests/e2e/tx/service_test.go @@ -198,6 +198,61 @@ func (s *E2ETestSuite) TestSimulateTx_GRPC() { } } +func (s *E2ETestSuite) TestSimulateTx_GRPC_NonAtomic() { + val := s.network.Validators[0] + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + expEvtCount int + }{ + {"valid non-atomic request", &tx.SimulateRequest{ + TxBytes: func() []byte { + txBuilder := s.mkNonAtomicTxBuilder(10, 10) + txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + return txBytes + }(), + }, false, "", 17}, + // {"valid non-atomic request: one failure", &tx.SimulateRequest{ + // TxBytes: func() []byte { + // txBuilder := s.mkNonAtomicTxBuilder(10, 5000000000000000000) + // txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + // s.Require().NoError(err) + // return txBytes + // }(), + // }, false, "", 12}, + // {"invalid non-atomic request: two failures", &tx.SimulateRequest{ + // TxBytes: func() []byte { + // txBuilder := s.mkNonAtomicTxBuilder(5000000000000000000, 5000000000000000000) + // txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + // s.Require().NoError(err) + // return txBytes + // }(), + // }, true, "all messages failed", 0}, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + res, err := s.queryClient.Simulate(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + // Check the result and gas used are correct. + s.Require().Equal(tc.expEvtCount, len(res.GetResult().GetEvents())) + s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } +} + func (s *E2ETestSuite) TestSimulateTx_GRPCGateway() { val := s.network.Validators[0] txBuilder := s.mkTxBuilder() @@ -1084,6 +1139,51 @@ func TestE2ETestSuite(t *testing.T) { suite.Run(t, new(E2ETestSuite)) } +func (s *E2ETestSuite) mkNonAtomicTxBuilder(amount0, amount1 int64) client.TxBuilder { + val := s.network.Validators[0] + s.Require().NoError(s.network.WaitForNextBlock()) + + // prepare txBuilder with msg + txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + s.Require().NoError( + txBuilder.SetMsgs( + &banktypes.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, amount0)}, + }, + &banktypes.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, amount1)}, + }, + ), + ) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetNonAtomic(true) + txBuilder.SetMemo("foobar") + signers, err := txBuilder.GetTx().GetSigners() + fmt.Println(signers, 123, [][]byte{val.Address.Bytes()}) + s.Require().NoError(err) + s.Require().Equal([][]byte{val.Address.Bytes()}, signers) + + // setup txFactory + txFactory := clienttx.Factory{}. + WithChainID(val.ClientCtx.ChainID). + WithKeybase(val.ClientCtx.Keyring). + WithTxConfig(val.ClientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err = authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true) + s.Require().NoError(err) + + return txBuilder +} + func (s *E2ETestSuite) mkTxBuilder() client.TxBuilder { val := s.network.Validators[0] s.Require().NoError(s.network.WaitForNextBlock()) diff --git a/testutil/testdata/testpb/tx.proto b/testutil/testdata/testpb/tx.proto index 13e48c7702a7..d3b97193acf7 100644 --- a/testutil/testdata/testpb/tx.proto +++ b/testutil/testdata/testpb/tx.proto @@ -19,8 +19,8 @@ service Msg { message MsgCreateDog { option (cosmos.msg.v1.signer) = "owner"; - testpb.Dog dog = 1; - string owner = 2; + testpb.Dog dog = 1; + string owner = 2; } message MsgCreateDogResponse { @@ -30,9 +30,9 @@ message MsgCreateDogResponse { // TestMsg is msg type for testing protobuf message using any, as defined in // https://github.com/cosmos/cosmos-sdk/issues/6213. message TestMsg { - option (cosmos.msg.v1.signer) = "signers"; + option (cosmos.msg.v1.signer) = "signers"; option (gogoproto.goproto_getters) = false; - option (amino.name) = "testpb/TestMsg"; + option (amino.name) = "testpb/TestMsg"; - repeated string signers = 1; + repeated string signers = 1; } diff --git a/types/mempool/mempool_test.go b/types/mempool/mempool_test.go index 9ed3b4d596a9..b38de283b6e4 100644 --- a/types/mempool/mempool_test.go +++ b/types/mempool/mempool_test.go @@ -74,6 +74,8 @@ var ( _ cryptotypes.PubKey = (*testPubKey)(nil) ) +func (tx testTx) IsNonAtomic() bool { return false } + func (tx testTx) GetMsgs() []sdk.Msg { return nil } func (tx testTx) GetMsgsV2() ([]protov2.Message, error) { return nil, nil } @@ -88,6 +90,10 @@ type sigErrTx struct { getSigs func() ([]txsigning.SignatureV2, error) } +func (sigErrTx) IsNonAtomic() bool { + return false +} + func (sigErrTx) Size() int64 { return 0 } func (sigErrTx) GetMsgs() []sdk.Msg { return nil } diff --git a/types/msgservice/msg.pb.go b/types/msgservice/msg.pb.go index 8b75fd6ea57a..1b1164168b34 100644 --- a/types/msgservice/msg.pb.go +++ b/types/msgservice/msg.pb.go @@ -7,7 +7,9 @@ import ( fmt "fmt" proto "github.com/cosmos/gogoproto/proto" descriptorpb "google.golang.org/protobuf/types/descriptorpb" + io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -21,6 +23,52 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// This is a message to be added as a response for failed messages +// when dealing with non-atomic multi-msg txs. +type MsgFailureResponse struct { + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *MsgFailureResponse) Reset() { *m = MsgFailureResponse{} } +func (m *MsgFailureResponse) String() string { return proto.CompactTextString(m) } +func (*MsgFailureResponse) ProtoMessage() {} +func (*MsgFailureResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5c08b83ea858d203, []int{0} +} +func (m *MsgFailureResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFailureResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFailureResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgFailureResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFailureResponse.Merge(m, src) +} +func (m *MsgFailureResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgFailureResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFailureResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFailureResponse proto.InternalMessageInfo + +func (m *MsgFailureResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + var E_Service = &proto.ExtensionDesc{ ExtendedType: (*descriptorpb.ServiceOptions)(nil), ExtensionType: (*bool)(nil), @@ -40,6 +88,7 @@ var E_Signer = &proto.ExtensionDesc{ } func init() { + proto.RegisterType((*MsgFailureResponse)(nil), "cosmos.msg.v1.MsgFailureResponse") proto.RegisterExtension(E_Service) proto.RegisterExtension(E_Signer) } @@ -47,19 +96,248 @@ func init() { func init() { proto.RegisterFile("cosmos/msg/v1/msg.proto", fileDescriptor_5c08b83ea858d203) } var fileDescriptor_5c08b83ea858d203 = []byte{ - // 214 bytes of a gzipped FileDescriptorProto + // 254 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0xd6, 0xcf, 0x2d, 0x4e, 0xd7, 0x2f, 0x33, 0x04, 0x51, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xbc, 0x10, 0x09, 0x3d, 0x90, 0x48, 0x99, 0xa1, 0x94, 0x42, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x3e, 0x58, 0x32, 0xa9, 0x34, 0x4d, 0x3f, 0x25, 0xb5, 0x38, 0xb9, 0x28, 0xb3, 0xa0, - 0x24, 0xbf, 0x08, 0xa2, 0xc1, 0xca, 0x86, 0x8b, 0xbd, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 0x55, - 0x48, 0x5e, 0x0f, 0xa2, 0x5a, 0x0f, 0xa6, 0x5a, 0x2f, 0x18, 0x22, 0xe3, 0x5f, 0x50, 0x92, 0x99, - 0x9f, 0x57, 0x2c, 0xf1, 0xa1, 0x67, 0x19, 0xab, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x4c, 0x8b, 0x95, - 0x15, 0x17, 0x5b, 0x71, 0x66, 0x7a, 0x5e, 0x6a, 0x11, 0x16, 0xcd, 0xbe, 0xa9, 0xc5, 0xc5, 0x89, - 0xe9, 0xa8, 0x9a, 0x99, 0x35, 0x38, 0x83, 0xa0, 0x3a, 0x9c, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, - 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, - 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x37, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, - 0x57, 0x1f, 0xea, 0x51, 0x08, 0xa5, 0x5b, 0x9c, 0x92, 0xad, 0x5f, 0x52, 0x59, 0x90, 0x0a, 0xf6, - 0x39, 0xd4, 0x11, 0x49, 0x6c, 0x60, 0x2b, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xa2, - 0x03, 0xdb, 0x15, 0x01, 0x00, 0x00, + 0x24, 0xbf, 0x08, 0xa2, 0x41, 0x49, 0x8b, 0x4b, 0xc8, 0xb7, 0x38, 0xdd, 0x2d, 0x31, 0x33, 0xa7, + 0xb4, 0x28, 0x35, 0x28, 0xb5, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x48, 0x84, 0x8b, 0x35, 0xb5, + 0xa8, 0x28, 0xbf, 0x48, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc2, 0xb1, 0xb2, 0xe1, 0x62, + 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x15, 0x92, 0xd7, 0x83, 0x98, 0xac, 0x07, 0x33, 0x59, + 0x2f, 0x18, 0x22, 0xe3, 0x5f, 0x50, 0x92, 0x99, 0x9f, 0x57, 0x2c, 0xf1, 0xa1, 0x67, 0x19, 0xab, + 0x02, 0xa3, 0x06, 0x47, 0x10, 0x4c, 0x8b, 0x95, 0x15, 0x17, 0x5b, 0x71, 0x66, 0x7a, 0x5e, 0x6a, + 0x11, 0x16, 0xcd, 0xbe, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0xa8, 0x9a, 0x99, 0x35, 0x38, 0x83, 0xa0, + 0x3a, 0x9c, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, + 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x37, 0x3d, + 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x1a, 0x28, 0x10, 0x4a, 0xb7, 0x38, + 0x25, 0x5b, 0xbf, 0xa4, 0xb2, 0x20, 0x15, 0x1c, 0x4a, 0x50, 0x47, 0x24, 0xb1, 0x81, 0xad, 0x34, + 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xed, 0xee, 0x85, 0x1b, 0x41, 0x01, 0x00, 0x00, } + +func (m *MsgFailureResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgFailureResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFailureResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Error) > 0 { + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintMsg(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintMsg(dAtA []byte, offset int, v uint64) int { + offset -= sovMsg(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgFailureResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovMsg(uint64(l)) + } + return n +} + +func sovMsg(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMsg(x uint64) (n int) { + return sovMsg(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgFailureResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsg + } + if iNdEx >= l { + return 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 fmt.Errorf("proto: MsgFailureResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFailureResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsg + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsg + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsg + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsg(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMsg + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMsg(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsg + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsg + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMsg + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMsg + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMsg + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMsg + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMsg = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMsg = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMsg = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/tx/msgs.go b/types/tx/msgs.go index 25e79c4457f8..78f24ea025fc 100644 --- a/types/tx/msgs.go +++ b/types/tx/msgs.go @@ -1,7 +1,7 @@ package tx import ( - fmt "fmt" + "fmt" "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/types/tx/tx.pb.go b/types/tx/tx.pb.go index 6782890cc9e4..932efc74cb69 100644 --- a/types/tx/tx.pb.go +++ b/types/tx/tx.pb.go @@ -365,6 +365,13 @@ type TxBody struct { // timeout is the block height after which this transaction will not // be processed by the chain TimeoutHeight uint64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"` + // non_atomic is an atomicity flag. If false, the transaction will be atomic + // and be rejected if any of the messages fail. This is the default and + // compatible with previous versions of the SDK and TxBody. + // + // If set to true, the transaction will be executed as long as at least one + // of the messages succeeds. + NonAtomic bool `protobuf:"varint,4,opt,name=non_atomic,json=nonAtomic,proto3" json:"non_atomic,omitempty"` // extension_options are arbitrary options that can be added by chains // when the default options are not sufficient. If any of these are present // and can't be handled, the transaction will be rejected @@ -429,6 +436,13 @@ func (m *TxBody) GetTimeoutHeight() uint64 { return 0 } +func (m *TxBody) GetNonAtomic() bool { + if m != nil { + return m.NonAtomic + } + return false +} + func (m *TxBody) GetExtensionOptions() []*types.Any { if m != nil { return m.ExtensionOptions @@ -1364,6 +1378,16 @@ func (m *TxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xfa } } + if m.NonAtomic { + i-- + if m.NonAtomic { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if m.TimeoutHeight != 0 { i = encodeVarintTx(dAtA, i, uint64(m.TimeoutHeight)) i-- @@ -1942,6 +1966,9 @@ func (m *TxBody) Size() (n int) { if m.TimeoutHeight != 0 { n += 1 + sovTx(uint64(m.TimeoutHeight)) } + if m.NonAtomic { + n += 2 + } if len(m.ExtensionOptions) > 0 { for _, e := range m.ExtensionOptions { l = e.Size() @@ -2955,6 +2982,26 @@ func (m *TxBody) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NonAtomic", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NonAtomic = bool(v != 0) case 1023: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType) diff --git a/types/tx/types.go b/types/tx/types.go index 9e2c5c306688..b64126f24ab1 100644 --- a/types/tx/types.go +++ b/types/tx/types.go @@ -36,6 +36,10 @@ func (t *Tx) GetMsgs() []sdk.Msg { return res } +func (t *Tx) IsNonAtomic() bool { + return t.Body.NonAtomic +} + // ValidateBasic implements the ValidateBasic method on sdk.Tx. func (t *Tx) ValidateBasic() error { if t == nil { diff --git a/types/tx_msg.go b/types/tx_msg.go index 230092ad2181..584ff6a2ff1a 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -45,6 +45,9 @@ type ( HasMsgs interface { // GetMsgs gets the all the transaction's messages. GetMsgs() []Msg + + // IsNonAtomic returns true if the transaction is non-atomic. + IsNonAtomic() bool } // Tx defines an interface a transaction must fulfill. diff --git a/x/auth/migrations/legacytx/stdtx.go b/x/auth/migrations/legacytx/stdtx.go index d2a16cfb0b14..b68ea29006b8 100644 --- a/x/auth/migrations/legacytx/stdtx.go +++ b/x/auth/migrations/legacytx/stdtx.go @@ -87,6 +87,7 @@ type StdTx struct { Signatures []StdSignature `json:"signatures" yaml:"signatures"` Memo string `json:"memo" yaml:"memo"` TimeoutHeight uint64 `json:"timeout_height" yaml:"timeout_height"` + NonAtomic bool `json:"non_atomic" yaml:"non_atomic"` } // Deprecated @@ -102,6 +103,11 @@ func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdT // GetMsgs returns the all the transaction's messages. func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } +// GetMsgs returns the all the transaction's messages. +func (tx StdTx) IsNonAtomic() bool { + return tx.NonAtomic +} + // Deprecated: AsAny implements intoAny. It doesn't work for protobuf serialization, // so it can't be saved into protobuf configured storage. We are using it only for API // compatibility. diff --git a/x/auth/migrations/legacytx/stdtx_builder.go b/x/auth/migrations/legacytx/stdtx_builder.go index 80d3450a441b..25fb57a78ac6 100644 --- a/x/auth/migrations/legacytx/stdtx_builder.go +++ b/x/auth/migrations/legacytx/stdtx_builder.go @@ -54,6 +54,10 @@ func (s *StdTxBuilder) SetMemo(memo string) { s.Memo = memo } +func (s *StdTxBuilder) SetNonAtomic(nonAtomic bool) { + s.NonAtomic = nonAtomic +} + // SetTimeoutHeight sets the transaction's height timeout. func (s *StdTxBuilder) SetTimeoutHeight(height uint64) { s.TimeoutHeight = height diff --git a/x/auth/tx/builder.go b/x/auth/tx/builder.go index 7f3a398e0299..67f21b4f9c86 100644 --- a/x/auth/tx/builder.go +++ b/x/auth/tx/builder.go @@ -76,6 +76,10 @@ func (w *wrapper) GetMsgs() []sdk.Msg { return w.tx.GetMsgs() } +func (w *wrapper) IsNonAtomic() bool { + return w.tx.IsNonAtomic() +} + func (w *wrapper) GetMsgsV2() ([]protov2.Message, error) { if w.msgsV2 == nil { err := w.initSignersAndMsgsV2() @@ -296,6 +300,13 @@ func (w *wrapper) SetMemo(memo string) { w.bodyBz = nil } +func (w *wrapper) SetNonAtomic(nonAtomic bool) { + w.tx.Body.NonAtomic = nonAtomic + + // set bodyBz to nil because the cached bodyBz no longer matches tx.Body + w.bodyBz = nil +} + func (w *wrapper) SetGasLimit(limit uint64) { if w.tx.AuthInfo.Fee == nil { w.tx.AuthInfo.Fee = &tx.Fee{} diff --git a/x/auth/tx/direct_test.go b/x/auth/tx/direct_test.go index 296f82008f88..f38e0bb64631 100644 --- a/x/auth/tx/direct_test.go +++ b/x/auth/tx/direct_test.go @@ -160,6 +160,7 @@ type nonProtoTx int func (npt *nonProtoTx) GetMsgs() []sdk.Msg { return nil } func (npt *nonProtoTx) GetMsgsV2() ([]protov2.Message, error) { return nil, nil } func (npt *nonProtoTx) ValidateBasic() error { return nil } +func (npt *nonProtoTx) IsNonAtomic() bool { return false } var _ sdk.Tx = (*nonProtoTx)(nil) diff --git a/x/gov/types/v1/gov.pb.go b/x/gov/types/v1/gov.pb.go index c4dcc507c9de..3929e47c9d79 100644 --- a/x/gov/types/v1/gov.pb.go +++ b/x/gov/types/v1/gov.pb.go @@ -264,7 +264,8 @@ type Proposal struct { // voting_end_time is the end time of voting on a proposal. VotingEndTime *time.Time `protobuf:"bytes,9,opt,name=voting_end_time,json=votingEndTime,proto3,stdtime" json:"voting_end_time,omitempty"` // metadata is any arbitrary metadata attached to the proposal. - // the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#proposal-3 + // the recommended format of the metadata is to be found here: + // https://docs.cosmos.network/v0.47/modules/gov#proposal-3 Metadata string `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` // title is the title of the proposal // diff --git a/x/tx/go.mod b/x/tx/go.mod index a9080280c129..ad4afbc9981d 100644 --- a/x/tx/go.mod +++ b/x/tx/go.mod @@ -34,3 +34,5 @@ require ( google.golang.org/grpc v1.57.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace cosmossdk.io/api => ../../api diff --git a/x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto b/x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto index 42566486be21..d28008c87dcf 100644 --- a/x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto +++ b/x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto @@ -10,22 +10,22 @@ import "cosmos/tx/v1beta1/tx.proto"; // by the name of the Gas field (GasLimit in txv1beta.Fee). // Note: this is only used for signing, see x/tx/signing/aminojson.go for more details. message AminoSignFee { - repeated cosmos.base.v1beta1.Coin amount = 1 [(amino.dont_omitempty) = true, (amino.encoding) = "legacy_coins"]; - uint64 gas = 2 [(amino.dont_omitempty) = true]; - string payer = 3; - string granter = 4; + repeated cosmos.base.v1beta1.Coin amount = 1 [(amino.dont_omitempty) = true, (amino.encoding) = "legacy_coins"]; + uint64 gas = 2 [(amino.dont_omitempty) = true]; + string payer = 3; + string granter = 4; } // AminoSignDoc is a message container used to generate the SIGN_MODE_LEGACY_AMINO_JSON sign bytes with proto3 API. // Note: This is only used for generated JSON in signing, see x/tx/signing/aminojson.go for more details. // We allow omitempty based on what is already defined in the legacy StdSignDoc. message AminoSignDoc { - uint64 account_number = 1 [(amino.dont_omitempty) = true]; - uint64 sequence = 2 [(amino.dont_omitempty) = true]; - uint64 timeout_height = 3; - string chain_id = 4 [(amino.dont_omitempty) = true]; - string memo = 5 [(amino.dont_omitempty) = true]; - AminoSignFee fee = 6 [(amino.dont_omitempty) = true]; + uint64 account_number = 1 [(amino.dont_omitempty) = true]; + uint64 sequence = 2 [(amino.dont_omitempty) = true]; + uint64 timeout_height = 3; + string chain_id = 4 [(amino.dont_omitempty) = true]; + string memo = 5 [(amino.dont_omitempty) = true]; + AminoSignFee fee = 6 [(amino.dont_omitempty) = true]; repeated google.protobuf.Any msgs = 7 [(amino.dont_omitempty) = true]; - cosmos.tx.v1beta1.Tip tip = 8; + cosmos.tx.v1beta1.Tip tip = 8; } \ No newline at end of file diff --git a/x/tx/signing/textual/internal/textualpb/textual.proto b/x/tx/signing/textual/internal/textualpb/textual.proto index efd8d35275bd..7ad4fa67ec25 100644 --- a/x/tx/signing/textual/internal/textualpb/textual.proto +++ b/x/tx/signing/textual/internal/textualpb/textual.proto @@ -85,4 +85,5 @@ message Envelope { repeated google.protobuf.Any extension_options = 16; repeated google.protobuf.Any non_critical_extension_options = 17; string hash_of_raw_bytes = 18; + bool non_atomic = 19; } diff --git a/x/tx/signing/textual/internal/textualpb/textual.pulsar.go b/x/tx/signing/textual/internal/textualpb/textual.pulsar.go index 14b94260756f..dcfad7ef81ca 100644 --- a/x/tx/signing/textual/internal/textualpb/textual.pulsar.go +++ b/x/tx/signing/textual/internal/textualpb/textual.pulsar.go @@ -1568,6 +1568,7 @@ var ( fd_Envelope_extension_options protoreflect.FieldDescriptor fd_Envelope_non_critical_extension_options protoreflect.FieldDescriptor fd_Envelope_hash_of_raw_bytes protoreflect.FieldDescriptor + fd_Envelope_non_atomic protoreflect.FieldDescriptor ) func init() { @@ -1591,6 +1592,7 @@ func init() { fd_Envelope_extension_options = md_Envelope.Fields().ByName("extension_options") fd_Envelope_non_critical_extension_options = md_Envelope.Fields().ByName("non_critical_extension_options") fd_Envelope_hash_of_raw_bytes = md_Envelope.Fields().ByName("hash_of_raw_bytes") + fd_Envelope_non_atomic = md_Envelope.Fields().ByName("non_atomic") } var _ protoreflect.Message = (*fastReflection_Envelope)(nil) @@ -1766,6 +1768,12 @@ func (x *fastReflection_Envelope) Range(f func(protoreflect.FieldDescriptor, pro return } } + if x.NonAtomic != false { + value := protoreflect.ValueOfBool(x.NonAtomic) + if !f(fd_Envelope_non_atomic, value) { + return + } + } } // Has reports whether a field is populated. @@ -1817,6 +1825,8 @@ func (x *fastReflection_Envelope) Has(fd protoreflect.FieldDescriptor) bool { return len(x.NonCriticalExtensionOptions) != 0 case "Envelope.hash_of_raw_bytes": return x.HashOfRawBytes != "" + case "Envelope.non_atomic": + return x.NonAtomic != false default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -1869,6 +1879,8 @@ func (x *fastReflection_Envelope) Clear(fd protoreflect.FieldDescriptor) { x.NonCriticalExtensionOptions = nil case "Envelope.hash_of_raw_bytes": x.HashOfRawBytes = "" + case "Envelope.non_atomic": + x.NonAtomic = false default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -1957,6 +1969,9 @@ func (x *fastReflection_Envelope) Get(descriptor protoreflect.FieldDescriptor) p case "Envelope.hash_of_raw_bytes": value := x.HashOfRawBytes return protoreflect.ValueOfString(value) + case "Envelope.non_atomic": + value := x.NonAtomic + return protoreflect.ValueOfBool(value) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -2025,6 +2040,8 @@ func (x *fastReflection_Envelope) Set(fd protoreflect.FieldDescriptor, value pro x.NonCriticalExtensionOptions = *clv.list case "Envelope.hash_of_raw_bytes": x.HashOfRawBytes = value.Interface().(string) + case "Envelope.non_atomic": + x.NonAtomic = value.Bool() default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -2108,6 +2125,8 @@ func (x *fastReflection_Envelope) Mutable(fd protoreflect.FieldDescriptor) proto panic(fmt.Errorf("field timeout_height of message Envelope is not mutable")) case "Envelope.hash_of_raw_bytes": panic(fmt.Errorf("field hash_of_raw_bytes of message Envelope is not mutable")) + case "Envelope.non_atomic": + panic(fmt.Errorf("field non_atomic of message Envelope is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -2164,6 +2183,8 @@ func (x *fastReflection_Envelope) NewField(fd protoreflect.FieldDescriptor) prot return protoreflect.ValueOfList(&_Envelope_17_list{list: &list}) case "Envelope.hash_of_raw_bytes": return protoreflect.ValueOfString("") + case "Envelope.non_atomic": + return protoreflect.ValueOfBool(false) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: Envelope")) @@ -2313,6 +2334,9 @@ func (x *fastReflection_Envelope) ProtoMethods() *protoiface.Methods { if l > 0 { n += 2 + l + runtime.Sov(uint64(l)) } + if x.NonAtomic { + n += 3 + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -2342,6 +2366,18 @@ func (x *fastReflection_Envelope) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if x.NonAtomic { + i-- + if x.NonAtomic { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if len(x.HashOfRawBytes) > 0 { i -= len(x.HashOfRawBytes) copy(dAtA[i:], x.HashOfRawBytes) @@ -3116,6 +3152,26 @@ func (x *fastReflection_Envelope) ProtoMethods() *protoiface.Methods { } x.HashOfRawBytes = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 19: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field NonAtomic", wireType) + } + var v int + 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++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + x.NonAtomic = bool(v != 0) default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -3165,7 +3221,7 @@ const ( ) // TextualData represents all the information needed to generate -// the textual SignDoc (which is []Screen encoded to XBOR). It is meant to be +// the textual SignDoc (which is []Screen encoded to CBOR). It is meant to be // used as an internal type in Textual's implementations. type TextualData struct { state protoimpl.MessageState @@ -3228,7 +3284,8 @@ func (x *TextualData) GetSignerData() *SignerData { // isn't included in the transaction body itself. // // It is the same struct as signing.SignerData, but only used internally -// in Textual because we need it as a proto.Message. +// in Textual because we need it as a proto.Message. If that struct is updated, +// then this proto SignerData also needs to be modified. type SignerData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3345,6 +3402,7 @@ type Envelope struct { ExtensionOptions []*anypb.Any `protobuf:"bytes,16,rep,name=extension_options,json=extensionOptions,proto3" json:"extension_options,omitempty"` NonCriticalExtensionOptions []*anypb.Any `protobuf:"bytes,17,rep,name=non_critical_extension_options,json=nonCriticalExtensionOptions,proto3" json:"non_critical_extension_options,omitempty"` HashOfRawBytes string `protobuf:"bytes,18,opt,name=hash_of_raw_bytes,json=hashOfRawBytes,proto3" json:"hash_of_raw_bytes,omitempty"` + NonAtomic bool `protobuf:"varint,19,opt,name=non_atomic,json=nonAtomic,proto3" json:"non_atomic,omitempty"` } func (x *Envelope) Reset() { @@ -3493,6 +3551,13 @@ func (x *Envelope) GetHashOfRawBytes() string { return "" } +func (x *Envelope) GetNonAtomic() bool { + if x != nil { + return x.NonAtomic + } + return false +} + var File_textual_proto protoreflect.FileDescriptor var file_textual_proto_rawDesc = []byte{ @@ -3525,7 +3590,7 @@ var file_textual_proto_rawDesc = []byte{ 0x12, 0x2d, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, - 0xfc, 0x05, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x9b, 0x06, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, @@ -3572,12 +3637,14 @@ var file_textual_proto_rawDesc = []byte{ 0x63, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x11, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6f, 0x66, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x68, 0x61, 0x73, 0x68, 0x4f, 0x66, 0x52, 0x61, 0x77, 0x42, 0x79, 0x74, 0x65, 0x73, 0x42, 0x3c, - 0x42, 0x0c, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x2a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x74, - 0x78, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x61, 0x73, 0x68, 0x4f, 0x66, 0x52, 0x61, 0x77, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x6e, 0x6f, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x6e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x42, 0x3e, 0x42, + 0x0c, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x2c, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x78, 0x2f, + 0x74, 0x78, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/x/tx/signing/textual/tx.go b/x/tx/signing/textual/tx.go index 143726034ec6..87296a5575da 100644 --- a/x/tx/signing/textual/tx.go +++ b/x/tx/signing/textual/tx.go @@ -74,6 +74,7 @@ func (vr txValueRenderer) Format(ctx context.Context, v protoreflect.Value) ([]S PublicKey: textualData.SignerData.PubKey, Message: txBody.Messages, Memo: txBody.Memo, + NonAtomic: txBody.NonAtomic, Fees: txAuthInfo.Fee.Amount, FeePayer: txAuthInfo.Fee.Payer, FeeGranter: txAuthInfo.Fee.Granter, @@ -232,6 +233,7 @@ func (vr txValueRenderer) Parse(ctx context.Context, screens []Screen) (protoref Messages: envelope.Message, Memo: envelope.Memo, TimeoutHeight: envelope.TimeoutHeight, + NonAtomic: envelope.NonAtomic, ExtensionOptions: envelope.ExtensionOptions, NonCriticalExtensionOptions: envelope.NonCriticalExtensionOptions, }