feat(tx)!: make timeout_height time based (backport #20870) (#21104)

Co-authored-by: son trinh <trinhleson2000@gmail.com>
This commit is contained in:
mergify[bot] 2024-07-29 14:21:00 +02:00 committed by GitHub
parent 029d6e5106
commit 7d801cc3c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 736 additions and 477 deletions

View File

@ -61,6 +61,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
* (client) [#19870](https://github.com/cosmos/cosmos-sdk/pull/19870) Add new query command `wait-tx`. Alias `event-query-tx-for` to `wait-tx` for backward compatibility.
* (crypto/keyring) [#20212](https://github.com/cosmos/cosmos-sdk/pull/20212) Expose the db keyring used in the keystore.
* (genutil) [#19971](https://github.com/cosmos/cosmos-sdk/pull/19971) Allow manually setting the consensus key type in genesis
* (client/tx) [#20870](https://github.com/cosmos/cosmos-sdk/pull/20870) Add `timeout-timestamp` field for tx body defines time based timeout.Add `WithTimeoutTimestamp` to tx factory. Increased gas cost for processing newly added timeout timestamp field in tx body.
### Improvements
@ -199,12 +200,13 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
### Client Breaking Changes
* (runtime) [#19040](https://github.com/cosmos/cosmos-sdk/pull/19040) Simplify app config implementation and deprecate `/cosmos/app/v1alpha1/config` query.
* (runtime) [#19040](https://github.com/cosmos/cosmos-sdk/pull/19040) Simplify app config implementation and deprecate `/cosmos/app/v1alpha1/config` query.
### CLI Breaking Changes
* (perf)[#20490](https://github.com/cosmos/cosmos-sdk/pull/20490) Sims: Replace runsim command with Go stdlib testing. CLI: `Commit` default true, `Lean`, `SimulateEveryOperation`, `PrintAllInvariants`, `DBBackend` params removed
* (server) [#18303](https://github.com/cosmos/cosmos-sdk/pull/18303) `appd export` has moved with other genesis commands, use `appd genesis export` instead.
* (client/tx) [#20870](https://github.com/cosmos/cosmos-sdk/pull/20870) Removed `timeout-height` flag replace with `timeout-timestamp` flag for a time based timeout.
### Deprecated

View File

@ -103,7 +103,7 @@ transactions in your application:
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
// ...
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, app.UnorderedTxManager),
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, options.TxManager, options.Environment),
// ...
}

View File

@ -14,6 +14,7 @@ import (
protoiface "google.golang.org/protobuf/runtime/protoiface"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
io "io"
reflect "reflect"
sync "sync"
@ -2768,6 +2769,7 @@ var (
fd_TxBody_memo protoreflect.FieldDescriptor
fd_TxBody_timeout_height protoreflect.FieldDescriptor
fd_TxBody_unordered protoreflect.FieldDescriptor
fd_TxBody_timeout_timestamp protoreflect.FieldDescriptor
fd_TxBody_extension_options protoreflect.FieldDescriptor
fd_TxBody_non_critical_extension_options protoreflect.FieldDescriptor
)
@ -2779,6 +2781,7 @@ func init() {
fd_TxBody_memo = md_TxBody.Fields().ByName("memo")
fd_TxBody_timeout_height = md_TxBody.Fields().ByName("timeout_height")
fd_TxBody_unordered = md_TxBody.Fields().ByName("unordered")
fd_TxBody_timeout_timestamp = md_TxBody.Fields().ByName("timeout_timestamp")
fd_TxBody_extension_options = md_TxBody.Fields().ByName("extension_options")
fd_TxBody_non_critical_extension_options = md_TxBody.Fields().ByName("non_critical_extension_options")
}
@ -2872,6 +2875,12 @@ func (x *fastReflection_TxBody) Range(f func(protoreflect.FieldDescriptor, proto
return
}
}
if x.TimeoutTimestamp != nil {
value := protoreflect.ValueOfMessage(x.TimeoutTimestamp.ProtoReflect())
if !f(fd_TxBody_timeout_timestamp, value) {
return
}
}
if len(x.ExtensionOptions) != 0 {
value := protoreflect.ValueOfList(&_TxBody_1023_list{list: &x.ExtensionOptions})
if !f(fd_TxBody_extension_options, value) {
@ -2907,6 +2916,8 @@ func (x *fastReflection_TxBody) Has(fd protoreflect.FieldDescriptor) bool {
return x.TimeoutHeight != uint64(0)
case "cosmos.tx.v1beta1.TxBody.unordered":
return x.Unordered != false
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
return x.TimeoutTimestamp != nil
case "cosmos.tx.v1beta1.TxBody.extension_options":
return len(x.ExtensionOptions) != 0
case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options":
@ -2935,6 +2946,8 @@ func (x *fastReflection_TxBody) Clear(fd protoreflect.FieldDescriptor) {
x.TimeoutHeight = uint64(0)
case "cosmos.tx.v1beta1.TxBody.unordered":
x.Unordered = false
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
x.TimeoutTimestamp = nil
case "cosmos.tx.v1beta1.TxBody.extension_options":
x.ExtensionOptions = nil
case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options":
@ -2970,6 +2983,9 @@ func (x *fastReflection_TxBody) Get(descriptor protoreflect.FieldDescriptor) pro
case "cosmos.tx.v1beta1.TxBody.unordered":
value := x.Unordered
return protoreflect.ValueOfBool(value)
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
value := x.TimeoutTimestamp
return protoreflect.ValueOfMessage(value.ProtoReflect())
case "cosmos.tx.v1beta1.TxBody.extension_options":
if len(x.ExtensionOptions) == 0 {
return protoreflect.ValueOfList(&_TxBody_1023_list{})
@ -3012,6 +3028,8 @@ func (x *fastReflection_TxBody) Set(fd protoreflect.FieldDescriptor, value proto
x.TimeoutHeight = value.Uint()
case "cosmos.tx.v1beta1.TxBody.unordered":
x.Unordered = value.Bool()
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
x.TimeoutTimestamp = value.Message().Interface().(*timestamppb.Timestamp)
case "cosmos.tx.v1beta1.TxBody.extension_options":
lv := value.List()
clv := lv.(*_TxBody_1023_list)
@ -3046,6 +3064,11 @@ func (x *fastReflection_TxBody) Mutable(fd protoreflect.FieldDescriptor) protore
}
value := &_TxBody_1_list{list: &x.Messages}
return protoreflect.ValueOfList(value)
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
if x.TimeoutTimestamp == nil {
x.TimeoutTimestamp = new(timestamppb.Timestamp)
}
return protoreflect.ValueOfMessage(x.TimeoutTimestamp.ProtoReflect())
case "cosmos.tx.v1beta1.TxBody.extension_options":
if x.ExtensionOptions == nil {
x.ExtensionOptions = []*anypb.Any{}
@ -3086,6 +3109,9 @@ func (x *fastReflection_TxBody) NewField(fd protoreflect.FieldDescriptor) protor
return protoreflect.ValueOfUint64(uint64(0))
case "cosmos.tx.v1beta1.TxBody.unordered":
return protoreflect.ValueOfBool(false)
case "cosmos.tx.v1beta1.TxBody.timeout_timestamp":
m := new(timestamppb.Timestamp)
return protoreflect.ValueOfMessage(m.ProtoReflect())
case "cosmos.tx.v1beta1.TxBody.extension_options":
list := []*anypb.Any{}
return protoreflect.ValueOfList(&_TxBody_1023_list{list: &list})
@ -3177,6 +3203,10 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
if x.Unordered {
n += 2
}
if x.TimeoutTimestamp != nil {
l = options.Size(x.TimeoutTimestamp)
n += 1 + l + runtime.Sov(uint64(l))
}
if len(x.ExtensionOptions) > 0 {
for _, e := range x.ExtensionOptions {
l = options.Size(e)
@ -3254,6 +3284,20 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
dAtA[i] = 0xfa
}
}
if x.TimeoutTimestamp != nil {
encoded, err := options.Marshal(x.TimeoutTimestamp)
if err != nil {
return protoiface.MarshalOutput{
NoUnkeyedLiterals: input.NoUnkeyedLiterals,
Buf: input.Buf,
}, err
}
i -= len(encoded)
copy(dAtA[i:], encoded)
i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded)))
i--
dAtA[i] = 0x2a
}
if x.Unordered {
i--
if x.Unordered {
@ -3446,6 +3490,42 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
}
}
x.Unordered = bool(v != 0)
case 5:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field TimeoutTimestamp", wireType)
}
var msglen 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++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
postIndex := iNdEx + msglen
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
}
if x.TimeoutTimestamp == nil {
x.TimeoutTimestamp = &timestamppb.Timestamp{}
}
if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.TimeoutTimestamp); err != nil {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err
}
iNdEx = postIndex
case 1023:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType)
@ -8471,10 +8551,6 @@ type TxBody struct {
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
// timeout_height is the block height after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
TimeoutHeight uint64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"`
// unordered, when set to true, indicates that the transaction signer(s)
// intend for the transaction to be evaluated and executed in an un-ordered
@ -8482,10 +8558,17 @@ type TxBody struct {
// incremented, which allows for fire-and-forget as well as concurrent
// transaction execution.
//
// Note, when set to true, the existing 'timeout_height' value must be set and
// will be used to correspond to a height in which the transaction is deemed
// Note, when set to true, the existing 'timeout_height' value must
// be set and will be used to correspond to a time_stamp in which the transaction is deemed
// valid.
Unordered bool `protobuf:"varint,4,opt,name=unordered,proto3" json:"unordered,omitempty"`
// timeout_timestamp is the block time after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
TimeoutTimestamp *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,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
@ -8544,6 +8627,13 @@ func (x *TxBody) GetUnordered() bool {
return false
}
func (x *TxBody) GetTimeoutTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.TimeoutTimestamp
}
return nil
}
func (x *TxBody) GetExtensionOptions() []*anypb.Any {
if x != nil {
return x.ExtensionOptions
@ -9059,128 +9149,119 @@ var file_cosmos_tx_v1beta1_tx_proto_rawDesc = []byte{
0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x69,
0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x02, 0x54, 0x78, 0x12, 0x2d, 0x0a,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e,
0x54, 0x78, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x38, 0x0a, 0x09,
0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65,
0x74, 0x61, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x61, 0x75,
0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x6e, 0x0a, 0x05, 0x54, 0x78, 0x52, 0x61, 0x77, 0x12,
0x1d, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x26,
0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66,
0x6f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e, 0x44,
0x6f, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x79, 0x74, 0x65,
0x73, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x62,
0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68,
0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61,
0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x87, 0x02, 0x0a, 0x10,
0x53, 0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x41, 0x75, 0x78,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x02, 0x54, 0x78, 0x12, 0x2d,
0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x54, 0x78, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x38, 0x0a,
0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x61,
0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67,
0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x6e, 0x0a, 0x05, 0x54, 0x78, 0x52, 0x61, 0x77,
0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
0x33, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 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, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64,
0x18, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x63, 0x65, 0x12, 0x2c, 0x0a, 0x03, 0x74, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x16, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65,
0x74, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x70, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x74, 0x69, 0x70,
0x3a, 0x13, 0xd2, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b,
0x20, 0x30, 0x2e, 0x34, 0x36, 0x22, 0xb3, 0x02, 0x0a, 0x06, 0x54, 0x78, 0x42, 0x6f, 0x64, 0x79,
0x12, 0x30, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
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, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d,
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a,
0x09, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x52, 0x09, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x11, 0x65,
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x18, 0xff, 0x07, 0x20, 0x03, 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, 0x10, 0x65,
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
0x5a, 0x0a, 0x1e, 0x6e, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f,
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x18, 0xff, 0x0f, 0x20, 0x03, 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, 0x1b,
0x6e, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e,
0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x08,
0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x40, 0x0a, 0x0c, 0x73, 0x69, 0x67, 0x6e,
0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d,
0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x73,
0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x28, 0x0a, 0x03, 0x66, 0x65,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x52,
0x03, 0x66, 0x65, 0x65, 0x12, 0x3f, 0x0a, 0x03, 0x74, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x70, 0x42, 0x15, 0x18, 0x01, 0xda, 0xb4, 0x2d,
0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x20, 0x30, 0x2e, 0x34, 0x36,
0x52, 0x03, 0x74, 0x69, 0x70, 0x22, 0x97, 0x01, 0x0a, 0x0a, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72,
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
0x65, 0x79, 0x18, 0x01, 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, 0x09,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x6d, 0x6f, 0x64,
0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x49,
0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22,
0xe0, 0x02, 0x0a, 0x08, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x06,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x48, 0x00, 0x52, 0x06, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x6d, 0x75,
0x6c, 0x74, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x6f,
0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x48, 0x00, 0x52, 0x05,
0x6d, 0x75, 0x6c, 0x74, 0x69, 0x1a, 0x41, 0x0a, 0x06, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12,
0x37, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e,
0x67, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x6f,
0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x1a, 0x90, 0x01, 0x0a, 0x05, 0x4d, 0x75, 0x6c,
0x74, 0x69, 0x12, 0x4b, 0x0a, 0x08, 0x62, 0x69, 0x74, 0x61, 0x72, 0x72, 0x61, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x6f, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x2e, 0x76, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, 0x69, 0x74,
0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x08, 0x62, 0x69, 0x74, 0x61, 0x72, 0x72, 0x61, 0x79, 0x12,
0x3a, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e,
0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f,
0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x42, 0x05, 0x0a, 0x03, 0x73,
0x75, 0x6d, 0x22, 0x81, 0x02, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x12, 0x79, 0x0a, 0x06, 0x61, 0x6d,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x42, 0x46, 0xc8, 0xde, 0x1f, 0x00, 0xaa, 0xdf, 0x1f, 0x28, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x74, 0x79, 0x70, 0x65,
0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x9a, 0xe7, 0xb0, 0x2a, 0x0c, 0x6c, 0x65, 0x67, 0x61,
0x63, 0x79, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0xa8, 0xe7, 0xb0, 0x2a, 0x01, 0x52, 0x06, 0x61,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d,
0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x70, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x70, 0x61, 0x79,
0x65, 0x72, 0x12, 0x32, 0x0a, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x65, 0x72, 0x22, 0xc9, 0x01, 0x0a, 0x03, 0x54, 0x69, 0x70, 0x12, 0x79,
0x26, 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x62, 0x79, 0x74,
0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x49, 0x6e,
0x66, 0x6f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67,
0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x07, 0x53, 0x69, 0x67, 0x6e,
0x44, 0x6f, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x79, 0x74,
0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f,
0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x75, 0x74,
0x68, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68,
0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x87, 0x02, 0x0a,
0x10, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x41, 0x75,
0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73,
0x12, 0x33, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02,
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, 0x09, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69,
0x64, 0x18, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x03, 0x74, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x70, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x74, 0x69,
0x70, 0x3a, 0x13, 0xd2, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64,
0x6b, 0x20, 0x30, 0x2e, 0x34, 0x36, 0x22, 0x86, 0x03, 0x0a, 0x06, 0x54, 0x78, 0x42, 0x6f, 0x64,
0x79, 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
0x03, 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, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f,
0x75, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
0x0d, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c,
0x0a, 0x09, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
0x08, 0x52, 0x09, 0x75, 0x6e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x12, 0x51, 0x0a, 0x11,
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x01, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x10, 0x74,
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
0x42, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, 0x07, 0x20, 0x03, 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, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x5a, 0x0a, 0x1e, 0x6e, 0x6f, 0x6e, 0x5f, 0x63, 0x72, 0x69, 0x74, 0x69,
0x63, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, 0x0f, 0x20, 0x03, 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, 0x1b, 0x6e, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x45,
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
0xb7, 0x01, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x40, 0x0a, 0x0c,
0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66,
0x6f, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x28,
0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e,
0x46, 0x65, 0x65, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x3f, 0x0a, 0x03, 0x74, 0x69, 0x70, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74,
0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x70, 0x42, 0x15, 0x18,
0x01, 0xda, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x20,
0x30, 0x2e, 0x34, 0x36, 0x52, 0x03, 0x74, 0x69, 0x70, 0x22, 0x97, 0x01, 0x0a, 0x0a, 0x53, 0x69,
0x67, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 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, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x38, 0x0a,
0x09, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6d,
0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x63, 0x65, 0x22, 0xe0, 0x02, 0x0a, 0x08, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f,
0x12, 0x3c, 0x0a, 0x06, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x22, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x06, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x39,
0x0a, 0x05, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,
0x48, 0x00, 0x52, 0x05, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x1a, 0x41, 0x0a, 0x06, 0x53, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x73, 0x69,
0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69,
0x67, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x1a, 0x90, 0x01, 0x0a,
0x05, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x4b, 0x0a, 0x08, 0x62, 0x69, 0x74, 0x61, 0x72, 0x72,
0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69,
0x67, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63,
0x74, 0x42, 0x69, 0x74, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x08, 0x62, 0x69, 0x74, 0x61, 0x72,
0x72, 0x61, 0x79, 0x12, 0x3a, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x42,
0x05, 0x0a, 0x03, 0x73, 0x75, 0x6d, 0x22, 0x81, 0x02, 0x0a, 0x03, 0x46, 0x65, 0x65, 0x12, 0x79,
0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19,
0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x42, 0x46, 0xc8, 0xde, 0x1f, 0x00, 0xaa,
@ -9188,38 +9269,54 @@ var file_cosmos_tx_v1beta1_tx_proto_rawDesc = []byte{
0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x9a, 0xe7, 0xb0, 0x2a, 0x0c,
0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0xa8, 0xe7, 0xb0, 0x2a,
0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x74, 0x69, 0x70,
0x70, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x52, 0x06, 0x74, 0x69, 0x70, 0x70, 0x65, 0x72, 0x3a, 0x15, 0x18, 0x01, 0xd2,
0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x20, 0x30, 0x2e,
0x34, 0x36, 0x22, 0xe3, 0x01, 0x0a, 0x0d, 0x41, 0x75, 0x78, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72,
0x44, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73,
0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61,
0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x70, 0x61, 0x79, 0x65, 0x72, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e,
0x5f, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53,
0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x41, 0x75, 0x78, 0x52,
0x07, 0x73, 0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x12, 0x37, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x74, 0x78, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74,
0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64,
0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03,
0x73, 0x69, 0x67, 0x3a, 0x13, 0xd2, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d,
0x73, 0x64, 0x6b, 0x20, 0x30, 0x2e, 0x34, 0x36, 0x42, 0xb4, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x6d,
0x05, 0x70, 0x61, 0x79, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65,
0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x52, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x72, 0x22, 0xc9, 0x01, 0x0a, 0x03, 0x54,
0x69, 0x70, 0x12, 0x79, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65,
0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x42, 0x46, 0xc8,
0xde, 0x1f, 0x00, 0xaa, 0xdf, 0x1f, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d,
0x73, 0x64, 0x6b, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x9a,
0xe7, 0xb0, 0x2a, 0x0c, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x73,
0xa8, 0xe7, 0xb0, 0x2a, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a,
0x06, 0x74, 0x69, 0x70, 0x70, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2,
0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x74, 0x69, 0x70, 0x70, 0x65, 0x72, 0x3a,
0x15, 0x18, 0x01, 0xd2, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64,
0x6b, 0x20, 0x30, 0x2e, 0x34, 0x36, 0x22, 0xe3, 0x01, 0x0a, 0x0d, 0x41, 0x75, 0x78, 0x53, 0x69,
0x67, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3e, 0x0a, 0x08,
0x73, 0x69, 0x67, 0x6e, 0x5f, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74,
0x61, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x74, 0x78, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x3b, 0x74, 0x78, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x54,
0x58, 0xaa, 0x02, 0x11, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x54, 0x78, 0x2e, 0x56, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x11, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x54,
0x78, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x1d, 0x43, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x5c, 0x54, 0x78, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50,
0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x3a, 0x3a, 0x54, 0x78, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
0x41, 0x75, 0x78, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x44, 0x6f, 0x63, 0x12, 0x37, 0x0a, 0x04,
0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x76,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52,
0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x3a, 0x13, 0xd2, 0xb4, 0x2d, 0x0f, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x20, 0x30, 0x2e, 0x34, 0x36, 0x42, 0xb4, 0x01, 0x0a,
0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x74, 0x78, 0x2e, 0x76,
0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
0x01, 0x5a, 0x2c, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x74, 0x78, 0x2f, 0x76, 0x31,
0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x74, 0x78, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2,
0x02, 0x03, 0x43, 0x54, 0x58, 0xaa, 0x02, 0x11, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x54,
0x78, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x11, 0x43, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x5c, 0x54, 0x78, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x1d,
0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x54, 0x78, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13,
0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x54, 0x78, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65,
0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -9250,9 +9347,10 @@ var file_cosmos_tx_v1beta1_tx_proto_goTypes = []interface{}{
(*ModeInfo_Single)(nil), // 11: cosmos.tx.v1beta1.ModeInfo.Single
(*ModeInfo_Multi)(nil), // 12: cosmos.tx.v1beta1.ModeInfo.Multi
(*anypb.Any)(nil), // 13: google.protobuf.Any
(*v1beta12.Coin)(nil), // 14: cosmos.base.v1beta1.Coin
(v1beta1.SignMode)(0), // 15: cosmos.tx.signing.v1beta1.SignMode
(*v1beta11.CompactBitArray)(nil), // 16: cosmos.crypto.multisig.v1beta1.CompactBitArray
(*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
(*v1beta12.Coin)(nil), // 15: cosmos.base.v1beta1.Coin
(v1beta1.SignMode)(0), // 16: cosmos.tx.signing.v1beta1.SignMode
(*v1beta11.CompactBitArray)(nil), // 17: cosmos.crypto.multisig.v1beta1.CompactBitArray
}
var file_cosmos_tx_v1beta1_tx_proto_depIdxs = []int32{
4, // 0: cosmos.tx.v1beta1.Tx.body:type_name -> cosmos.tx.v1beta1.TxBody
@ -9260,27 +9358,28 @@ var file_cosmos_tx_v1beta1_tx_proto_depIdxs = []int32{
13, // 2: cosmos.tx.v1beta1.SignDocDirectAux.public_key:type_name -> google.protobuf.Any
9, // 3: cosmos.tx.v1beta1.SignDocDirectAux.tip:type_name -> cosmos.tx.v1beta1.Tip
13, // 4: cosmos.tx.v1beta1.TxBody.messages:type_name -> google.protobuf.Any
13, // 5: cosmos.tx.v1beta1.TxBody.extension_options:type_name -> google.protobuf.Any
13, // 6: cosmos.tx.v1beta1.TxBody.non_critical_extension_options:type_name -> google.protobuf.Any
6, // 7: cosmos.tx.v1beta1.AuthInfo.signer_infos:type_name -> cosmos.tx.v1beta1.SignerInfo
8, // 8: cosmos.tx.v1beta1.AuthInfo.fee:type_name -> cosmos.tx.v1beta1.Fee
9, // 9: cosmos.tx.v1beta1.AuthInfo.tip:type_name -> cosmos.tx.v1beta1.Tip
13, // 10: cosmos.tx.v1beta1.SignerInfo.public_key:type_name -> google.protobuf.Any
7, // 11: cosmos.tx.v1beta1.SignerInfo.mode_info:type_name -> cosmos.tx.v1beta1.ModeInfo
11, // 12: cosmos.tx.v1beta1.ModeInfo.single:type_name -> cosmos.tx.v1beta1.ModeInfo.Single
12, // 13: cosmos.tx.v1beta1.ModeInfo.multi:type_name -> cosmos.tx.v1beta1.ModeInfo.Multi
14, // 14: cosmos.tx.v1beta1.Fee.amount:type_name -> cosmos.base.v1beta1.Coin
14, // 15: cosmos.tx.v1beta1.Tip.amount:type_name -> cosmos.base.v1beta1.Coin
3, // 16: cosmos.tx.v1beta1.AuxSignerData.sign_doc:type_name -> cosmos.tx.v1beta1.SignDocDirectAux
15, // 17: cosmos.tx.v1beta1.AuxSignerData.mode:type_name -> cosmos.tx.signing.v1beta1.SignMode
15, // 18: cosmos.tx.v1beta1.ModeInfo.Single.mode:type_name -> cosmos.tx.signing.v1beta1.SignMode
16, // 19: cosmos.tx.v1beta1.ModeInfo.Multi.bitarray:type_name -> cosmos.crypto.multisig.v1beta1.CompactBitArray
7, // 20: cosmos.tx.v1beta1.ModeInfo.Multi.mode_infos:type_name -> cosmos.tx.v1beta1.ModeInfo
21, // [21:21] is the sub-list for method output_type
21, // [21:21] is the sub-list for method input_type
21, // [21:21] is the sub-list for extension type_name
21, // [21:21] is the sub-list for extension extendee
0, // [0:21] is the sub-list for field type_name
14, // 5: cosmos.tx.v1beta1.TxBody.timeout_timestamp:type_name -> google.protobuf.Timestamp
13, // 6: cosmos.tx.v1beta1.TxBody.extension_options:type_name -> google.protobuf.Any
13, // 7: cosmos.tx.v1beta1.TxBody.non_critical_extension_options:type_name -> google.protobuf.Any
6, // 8: cosmos.tx.v1beta1.AuthInfo.signer_infos:type_name -> cosmos.tx.v1beta1.SignerInfo
8, // 9: cosmos.tx.v1beta1.AuthInfo.fee:type_name -> cosmos.tx.v1beta1.Fee
9, // 10: cosmos.tx.v1beta1.AuthInfo.tip:type_name -> cosmos.tx.v1beta1.Tip
13, // 11: cosmos.tx.v1beta1.SignerInfo.public_key:type_name -> google.protobuf.Any
7, // 12: cosmos.tx.v1beta1.SignerInfo.mode_info:type_name -> cosmos.tx.v1beta1.ModeInfo
11, // 13: cosmos.tx.v1beta1.ModeInfo.single:type_name -> cosmos.tx.v1beta1.ModeInfo.Single
12, // 14: cosmos.tx.v1beta1.ModeInfo.multi:type_name -> cosmos.tx.v1beta1.ModeInfo.Multi
15, // 15: cosmos.tx.v1beta1.Fee.amount:type_name -> cosmos.base.v1beta1.Coin
15, // 16: cosmos.tx.v1beta1.Tip.amount:type_name -> cosmos.base.v1beta1.Coin
3, // 17: cosmos.tx.v1beta1.AuxSignerData.sign_doc:type_name -> cosmos.tx.v1beta1.SignDocDirectAux
16, // 18: cosmos.tx.v1beta1.AuxSignerData.mode:type_name -> cosmos.tx.signing.v1beta1.SignMode
16, // 19: cosmos.tx.v1beta1.ModeInfo.Single.mode:type_name -> cosmos.tx.signing.v1beta1.SignMode
17, // 20: cosmos.tx.v1beta1.ModeInfo.Multi.bitarray:type_name -> cosmos.crypto.multisig.v1beta1.CompactBitArray
7, // 21: cosmos.tx.v1beta1.ModeInfo.Multi.mode_infos:type_name -> cosmos.tx.v1beta1.ModeInfo
22, // [22:22] is the sub-list for method output_type
22, // [22:22] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
}
func init() { file_cosmos_tx_v1beta1_tx_proto_init() }

View File

@ -1597,19 +1597,29 @@ func TestABCI_PrepareProposal_ReachedMaxBytes(t *testing.T) {
})
require.NoError(t, err)
expectedTxs := 8
var expectedTxBytes int64
for i := 0; i < 100; i++ {
tx2 := newTxCounter(t, suite.txConfig, int64(i), int64(i))
err := pool.Insert(sdk.Context{}, tx2)
require.NoError(t, err)
txBz, err := suite.txConfig.TxEncoder()(tx2)
require.NoError(t, err)
txDataSize := int(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{txBz}))
if i < expectedTxs {
expectedTxBytes += int64(txDataSize)
}
}
reqPrepareProposal := abci.PrepareProposalRequest{
MaxTxBytes: 1500,
MaxTxBytes: expectedTxBytes,
Height: 1,
}
resPrepareProposal, err := suite.baseApp.PrepareProposal(&reqPrepareProposal)
require.NoError(t, err)
require.Equal(t, 8, len(resPrepareProposal.Txs))
require.Equal(t, expectedTxs, len(resPrepareProposal.Txs))
}
func TestABCI_PrepareProposal_BadEncoding(t *testing.T) {

View File

@ -496,10 +496,10 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_NoOpMempoolTxSelection()
tx := builder.GetTx()
txBz, err := txConfig.TxEncoder()(tx)
s.Require().NoError(err)
s.Require().Len(txBz, 152)
s.Require().Len(txBz, 165)
txDataSize := int(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{txBz}))
s.Require().Equal(txDataSize, 155)
s.Require().Equal(txDataSize, 168)
testCases := map[string]struct {
ctx sdk.Context
@ -532,15 +532,15 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_NoOpMempoolTxSelection()
Txs: [][]byte{txBz, txBz, txBz, txBz, txBz},
MaxTxBytes: 465,
},
expectedTxs: 3,
expectedTxs: 2,
},
"large max tx bytes len calculation": {
ctx: s.ctx,
req: &abci.PrepareProposalRequest{
Txs: [][]byte{txBz, txBz, txBz, txBz, txBz},
MaxTxBytes: 456,
MaxTxBytes: 504,
},
expectedTxs: 2,
expectedTxs: 3,
},
"max gas and tx bytes": {
ctx: s.ctx.WithConsensusParams(cmtproto.ConsensusParams{
@ -619,15 +619,15 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
testTxs[i].size = int(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{bz}))
}
s.Require().Equal(180, testTxs[0].size)
s.Require().Equal(190, testTxs[1].size)
s.Require().Equal(181, testTxs[2].size)
s.Require().Equal(181, testTxs[3].size)
s.Require().Equal(263, testTxs[4].size)
s.Require().Equal(273, testTxs[5].size)
s.Require().Equal(264, testTxs[6].size)
s.Require().Equal(264, testTxs[7].size)
s.Require().Equal(264, testTxs[8].size)
s.Require().Equal(193, testTxs[0].size)
s.Require().Equal(203, testTxs[1].size)
s.Require().Equal(194, testTxs[2].size)
s.Require().Equal(194, testTxs[3].size)
s.Require().Equal(276, testTxs[4].size)
s.Require().Equal(286, testTxs[5].size)
s.Require().Equal(277, testTxs[6].size)
s.Require().Equal(277, testTxs[7].size)
s.Require().Equal(277, testTxs[8].size)
testCases := map[string]struct {
ctx sdk.Context
@ -640,7 +640,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[0], testTxs[1], testTxs[2], testTxs[3]},
req: &abci.PrepareProposalRequest{
MaxTxBytes: 180 + 181,
MaxTxBytes: 193 + 194,
},
expectedTxs: []int{0, 3},
},
@ -648,7 +648,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[4], testTxs[5], testTxs[6], testTxs[7], testTxs[8]},
req: &abci.PrepareProposalRequest{
MaxTxBytes: 263 + 264,
MaxTxBytes: 276 + 277,
},
expectedTxs: []int{4, 8},
},
@ -657,7 +657,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[9], testTxs[10], testTxs[11]},
req: &abci.PrepareProposalRequest{
MaxTxBytes: 263 + 264,
MaxTxBytes: 276 + 277,
},
expectedTxs: []int{9},
},

View File

@ -73,7 +73,7 @@ const (
FlagPageKey = "page-key"
FlagOffset = "offset"
FlagCountTotal = "count-total"
FlagTimeoutHeight = "timeout-height"
FlagTimeoutTimestamp = "timeout-timestamp"
FlagUnordered = "unordered"
FlagKeyAlgorithm = "algo"
FlagKeyType = "key-type"
@ -136,8 +136,8 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
f.Bool(FlagOffline, false, "Offline mode (does not allow any online functionality)")
f.BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
f.String(FlagSignMode, "", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature")
f.Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height")
f.Bool(FlagUnordered, false, "Enable unordered transaction delivery; must be used in conjunction with --timeout-height")
f.Int64(FlagTimeoutTimestamp, 0, "Set a block timeout timestamp to prevent the tx from being committed past a certain time")
f.Bool(FlagUnordered, false, "Enable unordered transaction delivery; must be used in conjunction with --timeout-timestamp")
f.String(FlagFeePayer, "", "Fee payer pays fees for the transaction instead of deducting from the signer")
f.String(FlagFeeGranter, "", "Fee granter grants fees for the transaction")
f.String(FlagTip, "", "Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator")

View File

@ -2,9 +2,11 @@ package tx
import (
"context"
"time"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
txsigning "cosmossdk.io/x/tx/signing"
@ -58,6 +60,14 @@ func (b *AuxTxBuilder) SetTimeoutHeight(height uint64) {
b.auxSignerData.SignDoc.BodyBytes = nil
}
// SetTimeoutTimestamp sets a timeout timestamp in the tx.
func (b *AuxTxBuilder) SetTimeoutTimestamp(timestamp time.Time) {
b.checkEmptyFields()
b.body.TimeoutTimestamp = timestamppb.New(timestamp)
b.auxSignerData.SignDoc.BodyBytes = nil
}
// SetMsgs sets an array of Msgs in the tx.
func (b *AuxTxBuilder) SetMsgs(msgs ...sdk.Msg) error {
anys := make([]*anypb.Any, len(msgs))
@ -209,9 +219,10 @@ func (b *AuxTxBuilder) GetSignBytes() ([]byte, error) {
})
auxBody := &txv1beta1.TxBody{
Messages: body.Messages,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
Messages: body.Messages,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
TimeoutTimestamp: body.TimeoutTimestamp,
// AuxTxBuilder has no concern with extension options, so we set them to nil.
// This preserves pre-PR#16025 behavior where extension options were ignored, this code path:
// https://github.com/cosmos/cosmos-sdk/blob/ac3c209326a26b46f65a6cc6f5b5ebf6beb79b38/client/tx/aux_builder.go#L193

View File

@ -6,6 +6,7 @@ import (
"math/big"
"os"
"strings"
"time"
"github.com/cosmos/go-bip39"
"github.com/spf13/pflag"
@ -33,6 +34,7 @@ type Factory struct {
sequence uint64
gas uint64
timeoutHeight uint64
timeoutTimestamp time.Time
gasAdjustment float64
chainID string
fromName string
@ -86,7 +88,8 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
gasAdj := clientCtx.Viper.GetFloat64(flags.FlagGasAdjustment)
memo := clientCtx.Viper.GetString(flags.FlagNote)
timeoutHeight := clientCtx.Viper.GetUint64(flags.FlagTimeoutHeight)
timestampUnix := clientCtx.Viper.GetInt64(flags.FlagTimeoutTimestamp)
timeoutTimestamp := time.Unix(timestampUnix, 0)
unordered := clientCtx.Viper.GetBool(flags.FlagUnordered)
gasStr := clientCtx.Viper.GetString(flags.FlagGas)
@ -104,7 +107,7 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
simulateAndExecute: gasSetting.Simulate,
accountNumber: accNum,
sequence: accSeq,
timeoutHeight: timeoutHeight,
timeoutTimestamp: timeoutTimestamp,
unordered: unordered,
gasAdjustment: gasAdj,
memo: memo,
@ -135,6 +138,7 @@ func (f Factory) Fees() sdk.Coins { return f.fees }
func (f Factory) GasPrices() sdk.DecCoins { return f.gasPrices }
func (f Factory) AccountRetriever() client.AccountRetriever { return f.accountRetriever }
func (f Factory) TimeoutHeight() uint64 { return f.timeoutHeight }
func (f Factory) TimeoutTimestamp() time.Time { return f.timeoutTimestamp }
func (f Factory) Unordered() bool { return f.unordered }
func (f Factory) FromName() string { return f.fromName }
@ -249,6 +253,12 @@ func (f Factory) WithTimeoutHeight(height uint64) Factory {
return f
}
// WithTimeoutTimestamp returns a copy of the Factory with an updated timeout timestamp.
func (f Factory) WithTimeoutTimestamp(timestamp time.Time) Factory {
f.timeoutTimestamp = timestamp
return f
}
// WithUnordered returns a copy of the Factory with an updated unordered field.
func (f Factory) WithUnordered(v bool) Factory {
f.unordered = v
@ -361,6 +371,7 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) {
tx.SetFeeGranter(f.feeGranter)
tx.SetFeePayer(f.feePayer)
tx.SetTimeoutHeight(f.TimeoutHeight())
tx.SetTimeoutTimestamp(f.TimeoutTimestamp())
tx.SetUnordered(f.Unordered())
if etx, ok := tx.(client.ExtendedTxBuilder); ok {

View File

@ -1,6 +1,8 @@
package client
import (
"time"
"cosmossdk.io/x/auth/signing"
txsigning "cosmossdk.io/x/tx/signing"
@ -48,6 +50,7 @@ type (
SetFeePayer(feePayer sdk.AccAddress)
SetGasLimit(limit uint64)
SetTimeoutHeight(height uint64)
SetTimeoutTimestamp(timestamp time.Time)
SetUnordered(v bool)
SetFeeGranter(feeGranter sdk.AccAddress)
AddAuxSignerData(tx.AuxSignerData) error

View File

@ -27,7 +27,7 @@ Flags:
-o, --output string Output format (text|json) (default "json")
-s, --sequence uint The sequence number of the signing account (offline mode only)
--sign-mode string Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature
--timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height
--timeout-timestamp int Set a block timeout timestamp to prevent the tx from being committed past a certain time
--tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator
--unordered Enable unordered transaction delivery; must be used in conjunction with --timeout-height
--unordered Enable unordered transaction delivery; must be used in conjunction with --timeout-timestamp
-y, --yes Skip tx broadcasting prompt confirmation

View File

@ -1 +1 @@
{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgSend","from_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","to_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","amount":[{"denom":"foo","amount":"1"}]}]},"auth_info":{"fee":{"gas_limit":"200000"}}}
{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgSend","from_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","to_address":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","amount":[{"denom":"foo","amount":"1"}]}],"timeout_timestamp":"1970-01-01T00:00:00Z"},"auth_info":{"fee":{"gas_limit":"200000"}}}

View File

@ -113,6 +113,7 @@ func (b *builder) GetSigningTxData() (txsigning.TxData, error) {
Messages: msgs,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
TimeoutTimestamp: body.TimeoutTimestamp,
ExtensionOptions: extOptions,
NonCriticalExtensionOptions: nonCriticalExtOptions,
}

View File

@ -35,13 +35,13 @@ this proposal, they'll follow the nonce rules the same as before.
When an un-ordered transaction is included into a block, the transaction hash is
recorded in a dictionary. New transactions are checked against this dictionary for
duplicates, and to prevent the dictionary grow indefinitely, the transaction must
specify `timeout_height` for expiration, so it's safe to removed it from the
specify `timeout_timestamp` for expiration, so it's safe to removed it from the
dictionary after it's expired.
The dictionary can be simply implemented as an in-memory golang map, a preliminary
analysis shows that the memory consumption won't be too big, for example `32M = 32 * 1024 * 1024`
can support 1024 blocks where each block contains 1024 unordered transactions. For
safety, we should limit the range of `timeout_height` to prevent very long expiration,
safety, we should limit the range of `timeout_timestamp` to prevent very long expiration,
and limit the size of the dictionary.
### Transaction Format
@ -58,12 +58,12 @@ message TxBody {
In order to provide replay protection, a user should ensure that the transaction's
TTL value is relatively short-lived but long enough to provide enough time to be
included in a block, e.g. ~H+50.
included in a block, e.g. ~10 minutes.
We facilitate this by storing the transaction's hash in a durable map, `UnorderedTxManager`,
to prevent duplicates, i.e. replay attacks. Upon transaction ingress during `CheckTx`,
we check if the transaction's hash exists in this map or if the TTL value is stale,
i.e. before the current block. If so, we reject it. Upon inclusion in a block
i.e. before the current block time. If so, we reject it. Upon inclusion in a block
during `DeliverTx`, the transaction's hash is set in the map along with it's TTL
value.
@ -93,20 +93,20 @@ const PurgeLoopSleepMS = 500
// UnorderedTxManager contains the tx hash dictionary for duplicates checking,
// and expire them when block production progresses.
type UnorderedTxManager struct {
// blockCh defines a channel to receive newly committed block heights
blockCh chan uint64
// blockCh defines a channel to receive newly committed block time
blockCh chan time.Time
mu sync.RWMutex
// txHashes defines a map from tx hash -> TTL value, which is used for duplicate
// checking and replay protection, as well as purging the map when the TTL is
// expired.
txHashes map[TxHash]uint64
txHashes map[TxHash]time.Time
}
func NewUnorderedTxManager() *UnorderedTxManager {
m := &UnorderedTxManager{
blockCh: make(chan uint64, 16),
txHashes: make(map[TxHash]uint64),
blockCh: make(chan time.Time, 16),
txHashes: make(map[TxHash]time.Time),
}
return m
@ -137,27 +137,27 @@ func (m *UnorderedTxManager) Size() int {
return len(m.txHashes)
}
func (m *UnorderedTxManager) Add(hash TxHash, expire uint64) {
func (m *UnorderedTxManager) Add(hash TxHash, expire time.Time) {
m.mu.Lock()
defer m.mu.Unlock()
m.txHashes[hash] = expire
}
// OnNewBlock send the latest block number to the background purge loop, which
// OnNewBlock send the latest block time to the background purge loop, which
// should be called in ABCI Commit event.
func (m *UnorderedTxManager) OnNewBlock(blockHeight uint64) {
m.blockCh <- blockHeight
func (m *UnorderedTxManager) OnNewBlock(blockTime time.Time) {
m.blockCh <- blockTime
}
// expiredTxs returns expired tx hashes based on the provided block height.
func (m *UnorderedTxManager) expiredTxs(blockHeight uint64) []TxHash {
// expiredTxs returns expired tx hashes based on the provided block time.
func (m *UnorderedTxManager) expiredTxs(blockTime time.Time) []TxHash {
m.mu.RLock()
defer m.mu.RUnlock()
var result []TxHash
for txHash, expire := range m.txHashes {
if blockHeight > expire {
if blockTime.After(expire) {
result = append(result, txHash)
}
}
@ -178,21 +178,17 @@ func (m *UnorderedTxManager) purge(txHashes []TxHash) {
// purgeLoop removes expired tx hashes in the background
func (m *UnorderedTxManager) purgeLoop() error {
for {
blocks := channelBatchRecv(m.blockCh)
if len(blocks) == 0 {
// channel closed
break
}
latestTime, ok := m.batchReceive()
if !ok {
// channel closed
return
}
latest := *blocks[len(blocks)-1]
hashes := m.expired(latest)
if len(hashes) > 0 {
m.purge(hashes)
}
// avoid burning cpu in catching up phase
time.Sleep(PurgeLoopSleepMS * time.Millisecond)
}
hashes := m.expiredTxs(latestTime)
if len(hashes) > 0 {
m.purge(hashes)
}
}
}
@ -237,14 +233,14 @@ verification and map lookup.
```golang
const (
// DefaultMaxUnOrderedTTL defines the default maximum TTL an un-ordered transaction
// DefaultMaxTimeoutDuration defines the default maximum duration an un-ordered transaction
// can set.
DefaultMaxUnOrderedTTL = 1024
DefaultMaxTimeoutDuration = time.Minute * 40
)
type DedupTxDecorator struct {
m *UnorderedTxManager
maxUnOrderedTTL uint64
maxTimeoutDuration time.Time
}
func (d *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
@ -253,13 +249,17 @@ func (d *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
return next(ctx, tx, simulate)
}
if tx.TimeoutHeight() == 0 {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "unordered tx must set timeout-height")
}
if tx.TimeoutHeight() > ctx.BlockHeight() + d.maxUnOrderedTTL {
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "unordered tx ttl exceeds %d", d.maxUnOrderedTTL)
}
headerInfo := d.env.HeaderService.HeaderInfo(ctx)
timeoutTimestamp := unorderedTx.GetTimeoutTimeStamp()
if timeoutTimestamp.IsZero() || timeoutTimestamp.Unix() == 0 {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction must have timeout_timestamp set")
}
if timeoutTimestamp.Before(headerInfo.Time) {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction has a timeout_timestamp that has already passed")
}
if timeoutTimestamp.After(headerInfo.Time.Add(d.maxTimeoutDuration)) {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "unordered tx ttl exceeds %s", d.maxTimeoutDuration.String())
}
// in order to create a deterministic hash based on the tx, we need to hash the contents of the tx with signature
// Get a Buffer from the pool
@ -302,7 +302,7 @@ encoding is not malleable. If a given transaction, which is otherwise valid, can
be encoded to produce different hashes, which reflect the same valid transaction,
then a duplicate unordered transaction can be submitted and included in a block.
In order to prevent this, the decoded transaction contents is taken. Starting with the content of the transaction we marshal the transaction in order to prevent a client reordering the transaction. Next we include the gas and timeout height as part of the identifier. All these fields are signed over in the transaction payload. If one of them changes the signature will not match the transaction.
In order to prevent this, the decoded transaction contents is taken. Starting with the content of the transaction we marshal the transaction in order to prevent a client reordering the transaction. Next we include the gas and timeout timestamp as part of the identifier. All these fields are signed over in the transaction payload. If one of them changes the signature will not match the transaction.
### State Management

View File

@ -480,7 +480,7 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/baseapp/abci.go#L623
When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends the transactions in FinalizeBlock message to the application for each transaction in a sequential order.
Since `FinalizeBlock` is an ABCI call, `Tx` is received in the encoded `[]byte` form. Nodes first unmarshal the transaction, using the [`TxConfig`](./00-app-anatomy.md#register-codec) defined in the app, then call `runTx` in `execModeFinalize`, which is very similar to `CheckTx` but also executes and writes state changes.
Since `FinalizeBlock` is an ABCI call, `Tx` is received in the encoded `[]byte` form. Nodes first unmarshal the transaction, using the [`TxConfig`](../beginner/00-app-anatomy.md#register-codec) defined in the app, then call `runTx` in `execModeFinalize`, which is very similar to `CheckTx` but also executes and writes state changes.
![blockprocessing](./blockprocessing-1.png)

View File

@ -8,6 +8,7 @@ import "cosmos/crypto/multisig/v1beta1/multisig.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/tx/signing/v1beta1/signing.proto";
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/cosmos/cosmos-sdk/types/tx";
@ -110,10 +111,6 @@ message TxBody {
// timeout_height is the block height after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
uint64 timeout_height = 3;
// unordered, when set to true, indicates that the transaction signer(s)
@ -122,11 +119,19 @@ message TxBody {
// incremented, which allows for fire-and-forget as well as concurrent
// transaction execution.
//
// Note, when set to true, the existing 'timeout_height' value must be set and
// will be used to correspond to a height in which the transaction is deemed
// Note, when set to true, the existing 'timeout_height' value must
// be set and will be used to correspond to a time_stamp in which the transaction is deemed
// valid.
bool unordered = 4;
// timeout_timestamp is the block time after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
google.protobuf.Timestamp timeout_timestamp = 5 [(gogoproto.nullable) = true, (gogoproto.stdtime) = true];
// 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

View File

@ -39,7 +39,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
ante.NewValidateBasicDecorator(options.Environment),
ante.NewTxTimeoutHeightDecorator(options.Environment),
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, options.TxManager, options.Environment, ante.DefaultSha256Cost),
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, options.TxManager, options.Environment, ante.DefaultSha256Cost),
ante.NewValidateMemoDecorator(options.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),

View File

@ -174,7 +174,7 @@ func TestBaseApp_BlockGas(t *testing.T) {
require.Equal(t, []byte("ok"), okValue)
}
// check block gas is always consumed
baseGas := uint64(38012) // baseGas is the gas consumed before tx msg
baseGas := uint64(38142) // baseGas is the gas consumed before tx msg
expGasConsumed := addUint64Saturating(tc.gasToConsume, baseGas)
if expGasConsumed > uint64(simtestutil.DefaultConsensusParams.Block.MaxGas) {
// capped by gasLimit

View File

@ -3,8 +3,9 @@
package systemtests
import (
"strconv"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -27,11 +28,9 @@ func TestUnorderedTXDuplicate(t *testing.T) {
sut.StartChain(t)
height := sut.CurrentHeight()
timeoutHeight := height + 15
timeoutHeightStr := strconv.Itoa(int(timeoutHeight))
timeoutTimestamp := time.Now().Add(time.Minute)
// send tokens
rsp1 := cli.Run("tx", "bank", "send", account1Addr, account2Addr, "5000stake", "--from="+account1Addr, "--fees=1stake", "--timeout-height="+timeoutHeightStr, "--unordered", "--sequence=1", "--note=1")
rsp1 := cli.Run("tx", "bank", "send", account1Addr, account2Addr, "5000stake", "--from="+account1Addr, "--fees=1stake", fmt.Sprintf("--timeout-timestamp=%v", timeoutTimestamp.Unix()), "--unordered", "--sequence=1", "--note=1")
RequireTxSuccess(t, rsp1)
assertDuplicateErr := func(xt assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
@ -39,14 +38,10 @@ func TestUnorderedTXDuplicate(t *testing.T) {
assert.Contains(t, gotOutputs[0], "is duplicated: invalid request")
return false // always abort
}
rsp2 := cli.WithRunErrorMatcher(assertDuplicateErr).Run("tx", "bank", "send", account1Addr, account2Addr, "5000stake", "--from="+account1Addr, "--fees=1stake", "--timeout-height="+timeoutHeightStr, "--unordered", "--sequence=1")
rsp2 := cli.WithRunErrorMatcher(assertDuplicateErr).Run("tx", "bank", "send", account1Addr, account2Addr, "5000stake", "--from="+account1Addr, "--fees=1stake", fmt.Sprintf("--timeout-timestamp=%v", timeoutTimestamp.Unix()), "--unordered", "--sequence=1")
RequireTxFailure(t, rsp2)
// assert TX executed before timeout
for cli.QueryBalance(account2Addr, "stake") != 5000 {
t.Log("query balance")
if current := sut.AwaitNextBlock(t); current > timeoutHeight {
t.Fatal("TX was not executed before timeout")
}
}
require.Eventually(t, func() bool {
return cli.QueryBalance(account2Addr, "stake") == 5000
}, time.Minute, time.Microsecond*500, "TX was not executed before timeout")
}

View File

@ -142,4 +142,8 @@ var (
// ErrPanic should only be set when we recovering from a panic
ErrPanic = errorsmod.ErrPanic
// ErrTxTimeout defines an error for when a tx is rejected out due to an
// explicitly set timeout timestamp.
ErrTxTimeout = errorsmod.Register(RootCodespace, 42, "tx timeout")
)

View File

@ -13,16 +13,20 @@ import (
signing "github.com/cosmos/cosmos-sdk/types/tx/signing"
_ "github.com/cosmos/gogoproto/gogoproto"
proto "github.com/cosmos/gogoproto/proto"
github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types"
any "github.com/cosmos/gogoproto/types/any"
_ "google.golang.org/protobuf/types/known/timestamppb"
io "io"
math "math"
math_bits "math/bits"
time "time"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
var _ = time.Kitchen
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
@ -359,10 +363,6 @@ type TxBody struct {
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
// timeout_height is the block height after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
TimeoutHeight uint64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"`
// unordered, when set to true, indicates that the transaction signer(s)
// intend for the transaction to be evaluated and executed in an un-ordered
@ -370,10 +370,17 @@ type TxBody struct {
// incremented, which allows for fire-and-forget as well as concurrent
// transaction execution.
//
// Note, when set to true, the existing 'timeout_height' value must be set and
// will be used to correspond to a height in which the transaction is deemed
// Note, when set to true, the existing 'timeout_height' value must
// be set and will be used to correspond to a time_stamp in which the transaction is deemed
// valid.
Unordered bool `protobuf:"varint,4,opt,name=unordered,proto3" json:"unordered,omitempty"`
// timeout_timestamp is the block time after which this transaction will not
// be processed by the chain.
//
// Note, if unordered=true this value MUST be set
// and will act as a short-lived TTL in which the transaction is deemed valid
// and kept in memory to prevent duplicates.
TimeoutTimestamp *time.Time `protobuf:"bytes,5,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3,stdtime" json:"timeout_timestamp,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
@ -445,6 +452,13 @@ func (m *TxBody) GetUnordered() bool {
return false
}
func (m *TxBody) GetTimeoutTimestamp() *time.Time {
if m != nil {
return m.TimeoutTimestamp
}
return nil
}
func (m *TxBody) GetExtensionOptions() []*any.Any {
if m != nil {
return m.ExtensionOptions
@ -1036,77 +1050,79 @@ func init() {
func init() { proto.RegisterFile("cosmos/tx/v1beta1/tx.proto", fileDescriptor_96d1575ffde80842) }
var fileDescriptor_96d1575ffde80842 = []byte{
// 1106 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0xcf, 0x6f, 0x1b, 0xc5,
0x17, 0xf7, 0x7a, 0x6d, 0xc7, 0x7e, 0x4d, 0xfa, 0x63, 0xbe, 0xed, 0x57, 0x8e, 0x4b, 0xdd, 0xe0,
0xaa, 0x60, 0x55, 0x64, 0xb7, 0x4d, 0x11, 0x94, 0x08, 0x51, 0xec, 0x86, 0x28, 0x55, 0x29, 0x48,
0x9b, 0x9c, 0x7a, 0x59, 0x8d, 0x77, 0x27, 0xeb, 0x51, 0xbd, 0x33, 0xcb, 0xce, 0x2c, 0x78, 0x8f,
0x9c, 0x38, 0x21, 0x55, 0x5c, 0x90, 0xf8, 0x0b, 0x10, 0xa7, 0x4a, 0x54, 0xe2, 0x5f, 0x28, 0xb7,
0x2a, 0x27, 0xc4, 0x01, 0xaa, 0xe4, 0xd0, 0x3f, 0x03, 0xb4, 0xb3, 0xb3, 0x9b, 0xb4, 0x75, 0x1d,
0x10, 0x48, 0x5c, 0x56, 0x33, 0x6f, 0x3f, 0xef, 0xcd, 0x67, 0xde, 0xfb, 0xcc, 0x7b, 0xd0, 0xf1,
0xb8, 0x08, 0xb9, 0xb0, 0xe5, 0xd4, 0xfe, 0xfc, 0xda, 0x88, 0x48, 0x7c, 0xcd, 0x96, 0x53, 0x2b,
0x8a, 0xb9, 0xe4, 0xe8, 0x4c, 0xfe, 0xcf, 0x92, 0x53, 0x4b, 0xff, 0xeb, 0x2c, 0xe7, 0x26, 0x57,
0x01, 0x6c, 0xfd, 0x5f, 0x6d, 0x3a, 0x67, 0x70, 0x48, 0x19, 0xb7, 0xd5, 0x57, 0x9b, 0xce, 0x06,
0x3c, 0xe0, 0x39, 0x34, 0x5b, 0x69, 0xeb, 0xaa, 0x3e, 0xd2, 0x8b, 0xd3, 0x48, 0x72, 0x3b, 0x4c,
0x26, 0x92, 0x0a, 0x1a, 0x94, 0xe7, 0x17, 0x06, 0x0d, 0xef, 0x6a, 0xf8, 0x08, 0x0b, 0x52, 0x62,
0x3c, 0x4e, 0x99, 0xfe, 0xff, 0xe6, 0xe1, 0x0d, 0x04, 0x0d, 0x18, 0x65, 0x87, 0x91, 0xf4, 0x5e,
0x03, 0x97, 0x03, 0xce, 0x83, 0x09, 0xb1, 0xd5, 0x6e, 0x94, 0xec, 0xda, 0x98, 0xa5, 0xf9, 0xaf,
0xde, 0xd7, 0x06, 0x54, 0x77, 0xa6, 0x68, 0x15, 0x6a, 0x23, 0xee, 0xa7, 0x6d, 0x63, 0xc5, 0xe8,
0x9f, 0x58, 0x5b, 0xb6, 0x5e, 0xba, 0xbf, 0xb5, 0x33, 0x1d, 0x72, 0x3f, 0x75, 0x14, 0x0c, 0xdd,
0x80, 0x16, 0x4e, 0xe4, 0xd8, 0xa5, 0x6c, 0x97, 0xb7, 0xab, 0xca, 0xe7, 0xfc, 0x0c, 0x9f, 0x41,
0x22, 0xc7, 0xb7, 0xd9, 0x2e, 0x77, 0x9a, 0x58, 0xaf, 0x50, 0x17, 0x20, 0xe3, 0x86, 0x65, 0x12,
0x13, 0xd1, 0x36, 0x57, 0xcc, 0xfe, 0xa2, 0x73, 0xc4, 0xd2, 0x63, 0x50, 0xdf, 0x99, 0x3a, 0xf8,
0x0b, 0x74, 0x01, 0x20, 0x3b, 0xca, 0x1d, 0xa5, 0x92, 0x08, 0xc5, 0x6b, 0xd1, 0x69, 0x65, 0x96,
0x61, 0x66, 0x40, 0x6f, 0xc0, 0xa9, 0x92, 0x81, 0xc6, 0x54, 0x15, 0x66, 0xa9, 0x38, 0x2a, 0xc7,
0x1d, 0x77, 0xde, 0x37, 0x06, 0x2c, 0x6c, 0xd3, 0x80, 0x6d, 0x70, 0xef, 0xdf, 0x3a, 0x72, 0x19,
0x9a, 0xde, 0x18, 0x53, 0xe6, 0x52, 0xbf, 0x6d, 0xae, 0x18, 0xfd, 0x96, 0xb3, 0xa0, 0xf6, 0xb7,
0x7d, 0x74, 0x19, 0x4e, 0x62, 0xcf, 0xe3, 0x09, 0x93, 0x2e, 0x4b, 0xc2, 0x11, 0x89, 0xdb, 0xb5,
0x15, 0xa3, 0x5f, 0x73, 0x96, 0xb4, 0xf5, 0x13, 0x65, 0xec, 0x7d, 0x55, 0x85, 0xd3, 0x9a, 0xd4,
0x06, 0x8d, 0x89, 0x27, 0x07, 0xc9, 0xf4, 0x38, 0x76, 0xd7, 0x01, 0xa2, 0x64, 0x34, 0xa1, 0x9e,
0x7b, 0x9f, 0xa4, 0xba, 0x26, 0x67, 0xad, 0xbc, 0xf0, 0x56, 0x51, 0x78, 0x6b, 0xc0, 0x52, 0xa7,
0x95, 0xe3, 0xee, 0x90, 0xf4, 0x9f, 0x53, 0x45, 0x1d, 0x68, 0x0a, 0xf2, 0x59, 0x42, 0x98, 0x47,
0xda, 0x75, 0x05, 0x28, 0xf7, 0xe8, 0x2d, 0x30, 0x25, 0x8d, 0xda, 0x0d, 0xc5, 0xe5, 0xff, 0xb3,
0x34, 0x45, 0xa3, 0x61, 0xb5, 0x6d, 0x38, 0x19, 0x6c, 0xfd, 0x7f, 0x7b, 0x8f, 0x56, 0x4f, 0xe5,
0x98, 0x55, 0xe1, 0xdf, 0x5f, 0xb9, 0x6a, 0xbd, 0xfd, 0x4e, 0xef, 0xc7, 0x2a, 0x34, 0x72, 0xe5,
0xa1, 0xab, 0xd0, 0x0c, 0x89, 0x10, 0x38, 0x50, 0xb7, 0x37, 0x5f, 0x79, 0xbd, 0x12, 0x85, 0x10,
0xd4, 0x42, 0x12, 0xe6, 0x02, 0x6d, 0x39, 0x6a, 0x9d, 0x5d, 0x4b, 0xd2, 0x90, 0xf0, 0x44, 0xba,
0x63, 0x42, 0x83, 0xb1, 0x54, 0xf7, 0xae, 0x39, 0x4b, 0xda, 0xba, 0xa5, 0x8c, 0xe8, 0x35, 0x68,
0x25, 0x8c, 0xc7, 0x3e, 0x89, 0x89, 0xaf, 0x2e, 0xde, 0x74, 0x0e, 0x0d, 0x68, 0x08, 0x67, 0xc8,
0x54, 0x12, 0x26, 0x28, 0x67, 0x2e, 0x8f, 0x24, 0xe5, 0x4c, 0xb4, 0xff, 0x58, 0x98, 0x43, 0xea,
0x74, 0x89, 0xff, 0x34, 0x87, 0xa3, 0x7b, 0xd0, 0x65, 0x9c, 0xb9, 0x5e, 0x4c, 0x25, 0xf5, 0xf0,
0xc4, 0x9d, 0x11, 0xf0, 0xd4, 0x9c, 0x80, 0xe7, 0x19, 0x67, 0xb7, 0xb4, 0xef, 0x47, 0x2f, 0xc4,
0xee, 0xfd, 0x64, 0x40, 0xb3, 0x78, 0x7b, 0xe8, 0x43, 0x58, 0xcc, 0xf4, 0x4e, 0x62, 0x25, 0xdc,
0x22, 0x77, 0x17, 0x66, 0x94, 0x63, 0x5b, 0xc1, 0xd4, 0x83, 0x3d, 0x21, 0xca, 0xb5, 0x40, 0x7d,
0x30, 0x77, 0x09, 0xd1, 0x9a, 0x9a, 0x55, 0xc7, 0x4d, 0x42, 0x9c, 0x0c, 0x82, 0x6e, 0xe6, 0x15,
0x37, 0xe7, 0x56, 0xfc, 0xdc, 0xaf, 0x2f, 0x17, 0x5a, 0x8b, 0xa0, 0xf7, 0xad, 0x01, 0x70, 0x48,
0xe3, 0x05, 0x51, 0x1b, 0x7f, 0x4d, 0xd4, 0x37, 0xa0, 0x15, 0x72, 0x9f, 0x1c, 0xd7, 0x9c, 0xee,
0x72, 0x9f, 0xe4, 0xcd, 0x29, 0xd4, 0xab, 0xe7, 0xc4, 0x6c, 0x3e, 0x2f, 0xe6, 0xde, 0xd3, 0x2a,
0x34, 0x0b, 0x17, 0xf4, 0x3e, 0x34, 0x04, 0x65, 0xc1, 0x84, 0x68, 0x4e, 0xbd, 0x39, 0xf1, 0xad,
0x6d, 0x85, 0xdc, 0xaa, 0x38, 0xda, 0x07, 0xbd, 0x07, 0x75, 0xd5, 0xe9, 0x35, 0xb9, 0xd7, 0xe7,
0x39, 0xdf, 0xcd, 0x80, 0x5b, 0x15, 0x27, 0xf7, 0xe8, 0x0c, 0xa0, 0x91, 0x87, 0x43, 0xef, 0x42,
0x2d, 0xe3, 0xad, 0x08, 0x9c, 0x5c, 0xbb, 0x74, 0x24, 0x46, 0xd1, 0xfb, 0x8f, 0x96, 0x35, 0x8b,
0xe7, 0x28, 0x87, 0xce, 0x03, 0x03, 0xea, 0x2a, 0x2a, 0xba, 0x03, 0xcd, 0x11, 0x95, 0x38, 0x8e,
0x71, 0x91, 0x5b, 0xbb, 0x08, 0x93, 0x4f, 0x28, 0xab, 0x1c, 0x48, 0x45, 0xac, 0x5b, 0x3c, 0x8c,
0xb0, 0x27, 0x87, 0x54, 0x0e, 0x32, 0x37, 0xa7, 0x0c, 0x80, 0xd6, 0x01, 0xca, 0xac, 0x67, 0x8d,
0xd1, 0x3c, 0x2e, 0xed, 0xad, 0x22, 0xed, 0x62, 0x58, 0x07, 0x53, 0x24, 0x61, 0xef, 0xcb, 0x2a,
0x98, 0x9b, 0x84, 0xa0, 0x14, 0x1a, 0x38, 0xcc, 0x7a, 0x8c, 0xd6, 0x6a, 0x39, 0x8e, 0xb2, 0x41,
0x78, 0x84, 0x0a, 0x65, 0xc3, 0xcd, 0xc7, 0xbf, 0x5d, 0xac, 0xfc, 0xf0, 0xfb, 0xc5, 0x7e, 0x40,
0xe5, 0x38, 0x19, 0x59, 0x1e, 0x0f, 0xed, 0x62, 0xc8, 0x96, 0x0a, 0xb3, 0x65, 0x1a, 0x11, 0xa1,
0x1c, 0xc4, 0x77, 0xcf, 0x1e, 0x5e, 0x59, 0x9c, 0x90, 0x00, 0x7b, 0xa9, 0x9b, 0x8d, 0x52, 0xf1,
0xfd, 0xb3, 0x87, 0x57, 0x0c, 0x47, 0x1f, 0x88, 0xce, 0x43, 0x2b, 0xc0, 0xc2, 0x9d, 0xd0, 0x90,
0x4a, 0x55, 0x9e, 0x9a, 0xd3, 0x0c, 0xb0, 0xf8, 0x38, 0xdb, 0x23, 0x0b, 0xea, 0x11, 0x4e, 0x49,
0x9c, 0xb7, 0xca, 0x61, 0x7b, 0xef, 0xd1, 0xea, 0x59, 0xcd, 0x6c, 0xe0, 0xfb, 0x31, 0x11, 0x62,
0x5b, 0xc6, 0x94, 0x05, 0x4e, 0x0e, 0x43, 0x6b, 0xb0, 0x10, 0xc4, 0x98, 0x49, 0xdd, 0x3b, 0xe7,
0x79, 0x14, 0xc0, 0xde, 0xcf, 0x06, 0x98, 0x3b, 0x34, 0xfa, 0x2f, 0x73, 0x70, 0x15, 0x1a, 0x92,
0x46, 0x11, 0x89, 0xf3, 0xc6, 0x39, 0x87, 0xb5, 0xc6, 0xad, 0x9f, 0xdb, 0x9b, 0xf5, 0xa2, 0x7b,
0x07, 0x06, 0x2c, 0x0d, 0x92, 0x69, 0xfe, 0x9e, 0x37, 0xb0, 0xc4, 0x59, 0x46, 0x70, 0x1e, 0x41,
0x09, 0x6e, 0x6e, 0x46, 0x34, 0x10, 0x7d, 0x00, 0xcd, 0x4c, 0xd1, 0xae, 0xcf, 0x3d, 0xfd, 0x60,
0x2e, 0xbd, 0xa2, 0x77, 0x1d, 0x1d, 0x97, 0xce, 0x82, 0xd0, 0x53, 0xbd, 0x78, 0x28, 0xe6, 0xdf,
0x7c, 0x28, 0xe8, 0x34, 0x98, 0x82, 0x06, 0xaa, 0x74, 0x8b, 0x4e, 0xb6, 0x9c, 0x39, 0xa2, 0x86,
0x37, 0x1f, 0xef, 0x77, 0x8d, 0x27, 0xfb, 0x5d, 0xe3, 0xe9, 0x7e, 0xd7, 0x78, 0x70, 0xd0, 0xad,
0x3c, 0x39, 0xe8, 0x56, 0x7e, 0x39, 0xe8, 0x56, 0xee, 0x5d, 0x3e, 0xbe, 0x20, 0xb6, 0x9c, 0x8e,
0x1a, 0xaa, 0x91, 0x5d, 0xff, 0x33, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xc2, 0x59, 0x2a, 0x91, 0x0a,
0x00, 0x00,
// 1147 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0x4f, 0x8f, 0xdb, 0x44,
0x14, 0x8f, 0xe3, 0x24, 0x9b, 0xbc, 0xee, 0xb6, 0xbb, 0x43, 0x8b, 0xb2, 0x29, 0xcd, 0x2e, 0xa9,
0x0a, 0x51, 0xc5, 0xda, 0xed, 0x16, 0x41, 0xa9, 0x10, 0x25, 0x69, 0xa9, 0x5a, 0x95, 0x82, 0xf0,
0xee, 0xa9, 0x17, 0x6b, 0x62, 0xcf, 0x3a, 0xa3, 0xc6, 0x33, 0xc6, 0x33, 0x86, 0xf8, 0xc8, 0x05,
0x4e, 0x48, 0x15, 0x17, 0x24, 0x3e, 0x01, 0xe2, 0xd4, 0x43, 0x25, 0xbe, 0x42, 0xb9, 0x55, 0x3d,
0x21, 0x0e, 0x6d, 0xd5, 0x3d, 0xf4, 0x63, 0x80, 0x3c, 0x1e, 0x7b, 0xb7, 0xdd, 0x34, 0x0b, 0x02,
0x89, 0x4b, 0xe4, 0x79, 0xf3, 0x7b, 0x6f, 0x7e, 0xef, 0x7f, 0xa0, 0xe3, 0x71, 0x11, 0x72, 0x61,
0xcb, 0xa9, 0xfd, 0xd5, 0xf9, 0x11, 0x91, 0xf8, 0xbc, 0x2d, 0xa7, 0x56, 0x14, 0x73, 0xc9, 0xd1,
0x4a, 0x7e, 0x67, 0xc9, 0xa9, 0xa5, 0xef, 0x3a, 0xab, 0xb9, 0xc8, 0x55, 0x00, 0x5b, 0xdf, 0xab,
0x43, 0x67, 0x05, 0x87, 0x94, 0x71, 0x5b, 0xfd, 0x6a, 0xd1, 0xf1, 0x80, 0x07, 0x3c, 0x87, 0x66,
0x5f, 0x5a, 0xba, 0xa1, 0x9f, 0xf4, 0xe2, 0x34, 0x92, 0xdc, 0x0e, 0x93, 0x89, 0xa4, 0x82, 0x06,
0xe5, 0xfb, 0x85, 0x40, 0xc3, 0xbb, 0x1a, 0x3e, 0xc2, 0x82, 0x94, 0x18, 0x8f, 0x53, 0xa6, 0xef,
0xdf, 0xde, 0xf3, 0x40, 0xd0, 0x80, 0x51, 0xb6, 0x67, 0x49, 0x9f, 0x35, 0x70, 0x35, 0xe0, 0x3c,
0x98, 0x10, 0x5b, 0x9d, 0x46, 0xc9, 0x8e, 0x8d, 0x59, 0xaa, 0xaf, 0xd6, 0x5e, 0xbe, 0x92, 0x34,
0x24, 0x42, 0xe2, 0x30, 0xca, 0x01, 0xbd, 0xef, 0x0d, 0xa8, 0x6e, 0x4f, 0xd1, 0x06, 0xd4, 0x46,
0xdc, 0x4f, 0xdb, 0xc6, 0xba, 0xd1, 0x3f, 0xb2, 0xb9, 0x6a, 0x1d, 0x08, 0x90, 0xb5, 0x3d, 0x1d,
0x72, 0x3f, 0x75, 0x14, 0x0c, 0x5d, 0x84, 0x16, 0x4e, 0xe4, 0xd8, 0xa5, 0x6c, 0x87, 0xb7, 0xab,
0x4a, 0xe7, 0xe4, 0x0c, 0x9d, 0x41, 0x22, 0xc7, 0x37, 0xd8, 0x0e, 0x77, 0x9a, 0x58, 0x7f, 0xa1,
0x2e, 0x40, 0x46, 0x1e, 0xcb, 0x24, 0x26, 0xa2, 0x6d, 0xae, 0x9b, 0xfd, 0x45, 0x67, 0x9f, 0xa4,
0xc7, 0xa0, 0xbe, 0x3d, 0x75, 0xf0, 0xd7, 0xe8, 0x14, 0x40, 0xf6, 0x94, 0x3b, 0x4a, 0x25, 0x11,
0x8a, 0xd7, 0xa2, 0xd3, 0xca, 0x24, 0xc3, 0x4c, 0x80, 0xde, 0x82, 0x63, 0x25, 0x03, 0x8d, 0xa9,
0x2a, 0xcc, 0x52, 0xf1, 0x54, 0x8e, 0x3b, 0xec, 0xbd, 0x1f, 0x0c, 0x58, 0xd8, 0xa2, 0x01, 0xbb,
0xca, 0xbd, 0xff, 0xea, 0xc9, 0x55, 0x68, 0x7a, 0x63, 0x4c, 0x99, 0x4b, 0xfd, 0xb6, 0xb9, 0x6e,
0xf4, 0x5b, 0xce, 0x82, 0x3a, 0xdf, 0xf0, 0xd1, 0x19, 0x38, 0x8a, 0x3d, 0x8f, 0x27, 0x4c, 0xba,
0x2c, 0x09, 0x47, 0x24, 0x6e, 0xd7, 0xd6, 0x8d, 0x7e, 0xcd, 0x59, 0xd2, 0xd2, 0xcf, 0x94, 0xb0,
0xf7, 0x5d, 0x15, 0x96, 0x35, 0xa9, 0xab, 0x34, 0x26, 0x9e, 0x1c, 0x24, 0xd3, 0xc3, 0xd8, 0x5d,
0x00, 0x88, 0x92, 0xd1, 0x84, 0x7a, 0xee, 0x1d, 0x92, 0xea, 0x9c, 0x1c, 0xb7, 0xf2, 0xf4, 0x5b,
0x45, 0xfa, 0xad, 0x01, 0x4b, 0x9d, 0x56, 0x8e, 0xbb, 0x49, 0xd2, 0x7f, 0x4f, 0x15, 0x75, 0xa0,
0x29, 0xc8, 0x97, 0x09, 0x61, 0x1e, 0x69, 0xd7, 0x15, 0xa0, 0x3c, 0xa3, 0x77, 0xc0, 0x94, 0x34,
0x6a, 0x37, 0x14, 0x97, 0xd7, 0x67, 0xd5, 0x14, 0x8d, 0x86, 0xd5, 0xb6, 0xe1, 0x64, 0xb0, 0x4b,
0xaf, 0x3d, 0xba, 0xbf, 0x71, 0x2c, 0xc7, 0x6c, 0x08, 0xff, 0xce, 0xfa, 0x39, 0xeb, 0xdd, 0xf7,
0x7a, 0xdf, 0x9a, 0xd0, 0xc8, 0x2b, 0x0f, 0x9d, 0x83, 0x66, 0x48, 0x84, 0xc0, 0x81, 0xf2, 0xde,
0x7c, 0xa5, 0x7b, 0x25, 0x0a, 0x21, 0xa8, 0x85, 0x24, 0xcc, 0x0b, 0xb4, 0xe5, 0xa8, 0xef, 0xcc,
0xad, 0xac, 0x05, 0x78, 0x22, 0xdd, 0x31, 0xa1, 0xc1, 0x58, 0x2a, 0xbf, 0x6b, 0xce, 0x92, 0x96,
0x5e, 0x57, 0x42, 0xf4, 0x06, 0xb4, 0x12, 0xc6, 0x63, 0x9f, 0xc4, 0xc4, 0x57, 0x8e, 0x37, 0x9d,
0x3d, 0x01, 0xfa, 0x02, 0x56, 0x0a, 0x23, 0x65, 0x3f, 0x29, 0xef, 0x8f, 0x6c, 0x76, 0x0e, 0x70,
0xda, 0x2e, 0x10, 0xc3, 0xe6, 0x83, 0xc7, 0x6b, 0xc6, 0xdd, 0x27, 0x6b, 0x86, 0xb3, 0xac, 0xd5,
0xcb, 0x3b, 0x34, 0x84, 0x15, 0x32, 0x95, 0x84, 0x09, 0xca, 0x99, 0xcb, 0x23, 0x49, 0x39, 0x13,
0xed, 0x3f, 0x17, 0xe6, 0xf8, 0xb9, 0x5c, 0xe2, 0x3f, 0xcf, 0xe1, 0xe8, 0x36, 0x74, 0x19, 0x67,
0xae, 0x17, 0x53, 0x49, 0x3d, 0x3c, 0x71, 0x67, 0x18, 0x3c, 0x36, 0xc7, 0xe0, 0x49, 0xc6, 0xd9,
0x15, 0xad, 0xfb, 0xc9, 0x4b, 0xb6, 0x7b, 0xbf, 0x1a, 0xd0, 0x2c, 0xda, 0x19, 0x7d, 0x0c, 0x8b,
0x59, 0x0b, 0x91, 0x58, 0xf5, 0x42, 0x91, 0x8e, 0x53, 0x33, 0x32, 0xbc, 0xa5, 0x60, 0x6a, 0x06,
0x1c, 0x11, 0xe5, 0xb7, 0x40, 0x7d, 0x30, 0x77, 0x08, 0xd1, 0x65, 0x3a, 0xab, 0x34, 0xae, 0x11,
0xe2, 0x64, 0x10, 0x74, 0x39, 0x2f, 0x22, 0x73, 0x6e, 0x11, 0x9d, 0xf8, 0xe3, 0x60, 0xed, 0xe8,
0xba, 0xea, 0xfd, 0x68, 0x00, 0xec, 0xd1, 0x78, 0xa9, 0x4f, 0x8c, 0xbf, 0xd7, 0x27, 0x17, 0xa1,
0x15, 0x72, 0x9f, 0x1c, 0x36, 0xef, 0x6e, 0x71, 0x9f, 0xe4, 0xf3, 0x2e, 0xd4, 0x5f, 0x2f, 0xf4,
0x87, 0xf9, 0x62, 0x7f, 0xf4, 0x9e, 0x56, 0xa1, 0x59, 0xa8, 0xa0, 0x0f, 0xa1, 0x21, 0x28, 0x0b,
0x26, 0x44, 0x73, 0xea, 0xcd, 0xb1, 0x6f, 0x6d, 0x29, 0xe4, 0xf5, 0x8a, 0xa3, 0x75, 0xd0, 0x07,
0x50, 0x57, 0xdb, 0x45, 0x93, 0x7b, 0x73, 0x9e, 0xf2, 0xad, 0x0c, 0x78, 0xbd, 0xe2, 0xe4, 0x1a,
0x9d, 0x01, 0x34, 0x72, 0x73, 0xe8, 0x7d, 0xa8, 0x65, 0xbc, 0x15, 0x81, 0xa3, 0x9b, 0xa7, 0xf7,
0xd9, 0x28, 0xf6, 0xcd, 0xfe, 0xb4, 0x66, 0xf6, 0x1c, 0xa5, 0xd0, 0xb9, 0x6b, 0x40, 0x5d, 0x59,
0x45, 0x37, 0xa1, 0x39, 0xa2, 0x12, 0xc7, 0x31, 0x2e, 0x62, 0x6b, 0x17, 0x66, 0xf2, 0xad, 0x68,
0x95, 0x4b, 0xb0, 0xb0, 0x75, 0x85, 0x87, 0x11, 0xf6, 0xe4, 0x90, 0xca, 0x41, 0xa6, 0xe6, 0x94,
0x06, 0xd0, 0x25, 0x80, 0x32, 0xea, 0xd9, 0xac, 0x35, 0x0f, 0x0b, 0x7b, 0xab, 0x08, 0xbb, 0x18,
0xd6, 0xc1, 0x14, 0x49, 0xd8, 0xfb, 0xa6, 0x0a, 0xe6, 0x35, 0x42, 0x50, 0x0a, 0x0d, 0x1c, 0x66,
0x63, 0x4b, 0xd7, 0x6a, 0xb9, 0xe1, 0xb2, 0xe5, 0xbb, 0x8f, 0x0a, 0x65, 0xc3, 0x6b, 0x0f, 0x1e,
0xaf, 0x55, 0x7e, 0x79, 0xb2, 0xd6, 0x0f, 0xa8, 0x1c, 0x27, 0x23, 0xcb, 0xe3, 0xa1, 0x5d, 0x2c,
0xf6, 0xb2, 0xc2, 0x6c, 0x99, 0x46, 0x44, 0x28, 0x05, 0xf1, 0xd3, 0xf3, 0x7b, 0x67, 0x17, 0x27,
0x24, 0xc0, 0x5e, 0xea, 0x66, 0xeb, 0x5b, 0xfc, 0xfc, 0xfc, 0xde, 0x59, 0xc3, 0xd1, 0x0f, 0xa2,
0x93, 0xd0, 0x0a, 0xb0, 0x70, 0x27, 0x34, 0xa4, 0x52, 0xa5, 0xa7, 0xe6, 0x34, 0x03, 0x2c, 0x3e,
0xcd, 0xce, 0xc8, 0x82, 0x7a, 0x84, 0x53, 0x12, 0xe7, 0xd3, 0x77, 0xd8, 0x7e, 0x74, 0x7f, 0xe3,
0xb8, 0x66, 0x36, 0xf0, 0xfd, 0x98, 0x08, 0xb1, 0x25, 0x63, 0xca, 0x02, 0x27, 0x87, 0xa1, 0x4d,
0x58, 0x08, 0x62, 0xcc, 0xa4, 0x1e, 0xc7, 0xf3, 0x34, 0x0a, 0x60, 0xef, 0x37, 0x03, 0xcc, 0x6d,
0x1a, 0xfd, 0x9f, 0x31, 0x38, 0x07, 0x0d, 0x49, 0xa3, 0x88, 0xc4, 0xf9, 0x2c, 0x9e, 0xc3, 0x5a,
0xe3, 0x2e, 0x9d, 0x78, 0x34, 0xab, 0xa3, 0x7b, 0xbb, 0x06, 0x2c, 0x0d, 0x92, 0x69, 0xde, 0xcf,
0x57, 0xb1, 0xc4, 0x59, 0x44, 0x70, 0x6e, 0x41, 0x15, 0xdc, 0xdc, 0x88, 0x68, 0x20, 0xfa, 0x08,
0x9a, 0x59, 0x45, 0xbb, 0x3e, 0xf7, 0x74, 0xc3, 0x9c, 0x7e, 0xc5, 0xec, 0xda, 0xbf, 0x81, 0x9d,
0x05, 0xa1, 0xff, 0x28, 0x14, 0x8d, 0x62, 0xfe, 0xc3, 0x46, 0x41, 0xcb, 0x60, 0x0a, 0x1a, 0xa8,
0xd4, 0x2d, 0x3a, 0xd9, 0xe7, 0xcc, 0xad, 0x37, 0xbc, 0xfc, 0xe0, 0x59, 0xd7, 0x78, 0xf8, 0xac,
0x6b, 0x3c, 0x7d, 0xd6, 0x35, 0xee, 0xee, 0x76, 0x2b, 0x0f, 0x77, 0xbb, 0x95, 0xdf, 0x77, 0xbb,
0x95, 0xdb, 0x67, 0x0e, 0x4f, 0x88, 0x2d, 0xa7, 0xa3, 0x86, 0x1a, 0x64, 0x17, 0xfe, 0x0a, 0x00,
0x00, 0xff, 0xff, 0x72, 0x63, 0x4f, 0x0e, 0x05, 0x0b, 0x00, 0x00,
}
func (m *Tx) Marshal() (dAtA []byte, err error) {
@ -1383,6 +1399,16 @@ func (m *TxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) {
dAtA[i] = 0xfa
}
}
if m.TimeoutTimestamp != nil {
n5, err5 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.TimeoutTimestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.TimeoutTimestamp):])
if err5 != nil {
return 0, err5
}
i -= n5
i = encodeVarintTx(dAtA, i, uint64(n5))
i--
dAtA[i] = 0x2a
}
if m.Unordered {
i--
if m.Unordered {
@ -1974,6 +2000,10 @@ func (m *TxBody) Size() (n int) {
if m.Unordered {
n += 2
}
if m.TimeoutTimestamp != nil {
l = github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.TimeoutTimestamp)
n += 1 + l + sovTx(uint64(l))
}
if len(m.ExtensionOptions) > 0 {
for _, e := range m.ExtensionOptions {
l = e.Size()
@ -3007,6 +3037,42 @@ func (m *TxBody) Unmarshal(dAtA []byte) error {
}
}
m.Unordered = bool(v != 0)
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TimeoutTimestamp", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthTx
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthTx
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.TimeoutTimestamp == nil {
m.TimeoutTimestamp = new(time.Time)
}
if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(m.TimeoutTimestamp, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 1023:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType)

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"strings"
"time"
"google.golang.org/protobuf/reflect/protoreflect"
@ -79,6 +80,14 @@ type (
GetMemo() string
}
// TxWithTimeoutTimeStamp extends the Tx interface by allowing a transaction to
// set a timeout timestamp.
TxWithTimeoutTimeStamp interface {
Tx
GetTimeoutTimeStamp() time.Time
}
// TxWithTimeoutHeight extends the Tx interface by allowing a transaction to
// set a height timeout.
TxWithTimeoutHeight interface {
@ -88,9 +97,9 @@ type (
}
// TxWithUnordered extends the Tx interface by allowing a transaction to set
// the unordered field, which implicitly relies on TxWithTimeoutHeight.
// the unordered field, which implicitly relies on TxWithTimeoutTimeStamp.
TxWithUnordered interface {
TxWithTimeoutHeight
TxWithTimeoutTimeStamp
GetUnordered() bool
}

View File

@ -2,6 +2,7 @@ package ante
import (
"context"
"time"
"cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/transaction"
@ -227,6 +228,7 @@ type (
sdk.Tx
GetTimeoutHeight() uint64
GetTimeoutTimeStamp() time.Time
}
)
@ -266,5 +268,12 @@ func (txh TxTimeoutHeightDecorator) ValidateTx(ctx context.Context, tx sdk.Tx) e
)
}
timeoutTimestamp := timeoutTx.GetTimeoutTimeStamp()
if !timeoutTimestamp.IsZero() && timeoutTimestamp.Unix() != 0 && timeoutTimestamp.Before(headerInfo.Time) {
return errorsmod.Wrapf(
sdkerrors.ErrTxTimeout, "block time: %s, timeout timestamp: %s", headerInfo.Time.String(), timeoutTimestamp.String(),
)
}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
@ -193,21 +194,30 @@ func TestTxHeightTimeoutDecorator(t *testing.T) {
// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()
currentTime := time.Now()
// msg and signatures
msg := testdata.NewTestMsg(addr1)
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
testCases := []struct {
name string
timeout uint64
height int64
expectedErr error
name string
timeout uint64
height int64
timeoutTimestamp time.Time
timestamp time.Time
expectedErr error
}{
{"default value", 0, 10, nil},
{"no timeout (greater height)", 15, 10, nil},
{"no timeout (same height)", 10, 10, nil},
{"timeout (smaller height)", 9, 10, sdkerrors.ErrTxTimeoutHeight},
{"default value", 0, 10, time.Time{}, time.Time{}, nil},
{"no timeout (greater height)", 15, 10, time.Time{}, time.Time{}, nil},
{"no timeout (same height)", 10, 10, time.Time{}, time.Time{}, nil},
{"timeout (smaller height)", 9, 10, time.Time{}, time.Time{}, sdkerrors.ErrTxTimeoutHeight},
{"no timeout (timeout after timestamp)", 0, 20, currentTime.Add(time.Minute), currentTime, nil},
{"no timeout (current time)", 0, 20, currentTime, currentTime, nil},
{"timeout before timestamp", 0, 20, currentTime, currentTime.Add(time.Minute), sdkerrors.ErrTxTimeout},
{"tx contain both timeouts, timeout (timeout before timestamp)", 15, 10, currentTime, currentTime.Add(time.Minute), sdkerrors.ErrTxTimeout},
{"tx contain both timeout, no timeout", 15, 10, currentTime.Add(time.Minute), currentTime, nil},
}
for _, tc := range testCases {
@ -222,12 +232,14 @@ func TestTxHeightTimeoutDecorator(t *testing.T) {
suite.txBuilder.SetGasLimit(gasLimit)
suite.txBuilder.SetMemo(strings.Repeat("01234567890", 10))
suite.txBuilder.SetTimeoutHeight(tc.timeout)
suite.txBuilder.SetTimeoutTimestamp(tc.timeoutTimestamp)
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
mockHeaderService.WithBlockHeight(tc.height)
mockHeaderService.WithBlockTime(tc.timestamp)
_, err = antehandler(suite.ctx, tx, true)
require.ErrorIs(t, err, tc.expectedErr)
})
@ -247,3 +259,7 @@ func (m *mockHeaderService) HeaderInfo(_ context.Context) header.Info {
func (m *mockHeaderService) WithBlockHeight(height int64) {
m.exp.Height = height
}
func (m *mockHeaderService) WithBlockTime(blocktime time.Time) {
m.exp.Time = blocktime
}

View File

@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/binary"
"sync"
"time"
"github.com/golang/protobuf/proto" // nolint: staticcheck // for proto.Message
@ -42,18 +43,18 @@ var _ sdk.AnteDecorator = (*UnorderedTxDecorator)(nil)
// chain to ensure that during DeliverTx, the transaction is added to the UnorderedTxManager.
type UnorderedTxDecorator struct {
// maxUnOrderedTTL defines the maximum TTL a transaction can define.
maxUnOrderedTTL uint64
txManager *unorderedtx.Manager
env appmodule.Environment
sha256Cost uint64
maxTimeoutDuration time.Duration
txManager *unorderedtx.Manager
env appmodule.Environment
sha256Cost uint64
}
func NewUnorderedTxDecorator(maxTTL uint64, m *unorderedtx.Manager, env appmodule.Environment, gasCost uint64) *UnorderedTxDecorator {
func NewUnorderedTxDecorator(maxDuration time.Duration, m *unorderedtx.Manager, env appmodule.Environment, gasCost uint64) *UnorderedTxDecorator {
return &UnorderedTxDecorator{
maxUnOrderedTTL: maxTTL,
txManager: m,
env: env,
sha256Cost: gasCost,
maxTimeoutDuration: maxDuration,
txManager: m,
env: env,
sha256Cost: gasCost,
}
}
@ -65,17 +66,16 @@ func (d *UnorderedTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, ne
return next(ctx, tx, false)
}
// TTL is defined as a specific block height at which this tx is no longer valid
ttl := unorderedTx.GetTimeoutHeight()
if ttl == 0 {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction must have timeout_height set")
headerInfo := d.env.HeaderService.HeaderInfo(ctx)
timeoutTimestamp := unorderedTx.GetTimeoutTimeStamp()
if timeoutTimestamp.IsZero() || timeoutTimestamp.Unix() == 0 {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction must have timeout_timestamp set")
}
if ttl < uint64(ctx.BlockHeight()) {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction has a timeout_height that has already passed")
if timeoutTimestamp.Before(headerInfo.Time) {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction has a timeout_timestamp that has already passed")
}
if ttl > uint64(ctx.BlockHeight())+d.maxUnOrderedTTL {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "unordered tx ttl exceeds %d", d.maxUnOrderedTTL)
if timeoutTimestamp.After(headerInfo.Time.Add(d.maxTimeoutDuration)) {
return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "unordered tx ttl exceeds %s", d.maxTimeoutDuration.String())
}
// consume gas in all exec modes to avoid gas estimation discrepancies
@ -90,7 +90,7 @@ func (d *UnorderedTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, ne
}
// calculate the tx hash
txHash, err := TxIdentifier(ttl, tx)
txHash, err := TxIdentifier(uint64(timeoutTimestamp.Unix()), tx)
if err != nil {
return ctx, err
}
@ -101,7 +101,7 @@ func (d *UnorderedTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, ne
}
if d.env.TransactionService.ExecMode(ctx) == transaction.ExecModeFinalize {
// a new tx included in the block, add the hash to the unordered tx manager
d.txManager.Add(txHash, ttl)
d.txManager.Add(txHash, timeoutTimestamp)
}
return next(ctx, tx, false)

View File

@ -2,9 +2,11 @@ package ante_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/auth/ante"
"cosmossdk.io/x/auth/ante/unorderedtx"
@ -27,10 +29,10 @@ func TestUnorderedTxDecorator_OrderedTx(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, false, 0)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
tx, txBz := genUnorderedTx(t, false, time.Time{})
ctx := sdk.Context{}.WithTxBytes(txBz)
_, err := chain(ctx, tx, false)
require.NoError(t, err)
@ -46,10 +48,10 @@ func TestUnorderedTxDecorator_UnorderedTx_NoTTL(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, 0)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
tx, txBz := genUnorderedTx(t, true, time.Time{})
ctx := sdk.Context{}.WithTxBytes(txBz)
_, err := chain(ctx, tx, false)
require.Error(t, err)
@ -65,11 +67,10 @@ func TestUnorderedTxDecorator_UnorderedTx_InvalidTTL(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, 100+unorderedtx.DefaultMaxUnOrderedTTL+1)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, time.Now().Add(unorderedtx.DefaultMaxTimeoutDuration+time.Second))
ctx := sdk.Context{}.WithTxBytes(txBz).WithHeaderInfo(header.Info{Time: time.Now()})
_, err := chain(ctx, tx, false)
require.Error(t, err)
}
@ -84,14 +85,14 @@ func TestUnorderedTxDecorator_UnorderedTx_AlreadyExists(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, 150)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
tx, txBz := genUnorderedTx(t, true, time.Now().Add(time.Minute))
ctx := sdk.Context{}.WithTxBytes(txBz).WithHeaderInfo(header.Info{Time: time.Now()}).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
bz := [32]byte{}
copy(bz[:], txBz[:32])
txm.Add(bz, 150)
txm.Add(bz, time.Now().Add(time.Minute))
_, err := chain(ctx, tx, false)
require.Error(t, err)
@ -107,10 +108,10 @@ func TestUnorderedTxDecorator_UnorderedTx_ValidCheckTx(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, 150)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100).WithExecMode(sdk.ExecModeCheck).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
tx, txBz := genUnorderedTx(t, true, time.Now().Add(time.Minute))
ctx := sdk.Context{}.WithTxBytes(txBz).WithHeaderInfo(header.Info{Time: time.Now()}).WithExecMode(sdk.ExecModeCheck).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
_, err := chain(ctx, tx, false)
require.NoError(t, err)
@ -126,10 +127,10 @@ func TestUnorderedTxDecorator_UnorderedTx_ValidDeliverTx(t *testing.T) {
suite := SetupTestSuite(t, false)
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, txm, suite.accountKeeper.GetEnvironment(), ante.DefaultSha256Cost))
tx, txBz := genUnorderedTx(t, true, 150)
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100).WithExecMode(sdk.ExecModeFinalize).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
tx, txBz := genUnorderedTx(t, true, time.Now().Add(time.Minute))
ctx := sdk.Context{}.WithTxBytes(txBz).WithHeaderInfo(header.Info{Time: time.Now()}).WithExecMode(sdk.ExecModeFinalize).WithGasMeter(storetypes.NewGasMeter(gasConsumed))
_, err := chain(ctx, tx, false)
require.NoError(t, err)
@ -140,7 +141,7 @@ func TestUnorderedTxDecorator_UnorderedTx_ValidDeliverTx(t *testing.T) {
require.True(t, txm.Contains(bz))
}
func genUnorderedTx(t *testing.T, unordered bool, ttl uint64) (sdk.Tx, []byte) {
func genUnorderedTx(t *testing.T, unordered bool, timestamp time.Time) (sdk.Tx, []byte) {
t.Helper()
s := SetupTestSuite(t, true)
@ -158,13 +159,13 @@ func genUnorderedTx(t *testing.T, unordered bool, ttl uint64) (sdk.Tx, []byte) {
s.txBuilder.SetFeeAmount(feeAmount)
s.txBuilder.SetGasLimit(gasLimit)
s.txBuilder.SetUnordered(unordered)
s.txBuilder.SetTimeoutHeight(ttl)
s.txBuilder.SetTimeoutTimestamp(timestamp)
privKeys, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := s.CreateTestTx(s.ctx, privKeys, accNums, accSeqs, s.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
txBz, err := ante.TxIdentifier(ttl, tx)
txBz, err := ante.TxIdentifier(uint64(timestamp.Unix()), tx)
require.NoError(t, err)

View File

@ -18,9 +18,9 @@ import (
)
const (
// DefaultMaxUnOrderedTTL defines the default maximum TTL an un-ordered transaction
// DefaultmaxTimeoutDuration defines the default maximum duration an un-ordered transaction
// can set.
DefaultMaxUnOrderedTTL = 1024
DefaultMaxTimeoutDuration = time.Minute * 40
dirName = "unordered_txs"
fileName = "data"
@ -33,7 +33,7 @@ type TxHash [32]byte
// them when block production progresses.
type Manager struct {
// blockCh defines a channel to receive newly committed block heights
blockCh chan uint64
blockCh chan time.Time
// doneCh allows us to ensure the purgeLoop has gracefully terminated prior to closing
doneCh chan struct{}
@ -48,10 +48,11 @@ type Manager struct {
dataDir string
mu sync.RWMutex
// txHashes defines a map from tx hash -> TTL value, which is used for duplicate
// txHashes defines a map from tx hash -> TTL value defined as block time, which is used for duplicate
// checking and replay protection, as well as purging the map when the TTL is
// expired.
txHashes map[TxHash]uint64
txHashes map[TxHash]time.Time
}
func NewManager(dataDir string) *Manager {
@ -62,9 +63,9 @@ func NewManager(dataDir string) *Manager {
m := &Manager{
dataDir: dataDir,
blockCh: make(chan uint64, 16),
blockCh: make(chan time.Time, 16),
doneCh: make(chan struct{}),
txHashes: make(map[TxHash]uint64),
txHashes: make(map[TxHash]time.Time),
}
return m
@ -91,7 +92,6 @@ func (m *Manager) Close() error {
func (m *Manager) Contains(hash TxHash) bool {
m.mu.RLock()
defer m.mu.RUnlock()
_, ok := m.txHashes[hash]
return ok
}
@ -103,11 +103,11 @@ func (m *Manager) Size() int {
return len(m.txHashes)
}
func (m *Manager) Add(txHash TxHash, ttl uint64) {
func (m *Manager) Add(txHash TxHash, timestamp time.Time) {
m.mu.Lock()
defer m.mu.Unlock()
m.txHashes[txHash] = ttl
m.txHashes[txHash] = timestamp
}
// OnInit must be called when a node starts up. Typically, this should be called
@ -145,7 +145,8 @@ func (m *Manager) OnInit() error {
var txHash TxHash
copy(txHash[:], buf[:txHashSize])
m.Add(txHash, binary.BigEndian.Uint64(buf[txHashSize:]))
timeStamp := binary.BigEndian.Uint64(buf[txHashSize:])
m.Add(txHash, time.Unix(int64(timeStamp), 0))
}
return nil
@ -153,8 +154,8 @@ func (m *Manager) OnInit() error {
// OnNewBlock sends the latest block number to the background purge loop, which
// should be called in ABCI Commit event.
func (m *Manager) OnNewBlock(blockHeight uint64) {
m.blockCh <- blockHeight
func (m *Manager) OnNewBlock(blockTime time.Time) {
m.blockCh <- blockTime
}
func (m *Manager) exportSnapshot(height uint64, snapshotWriter func([]byte) error) error {
@ -164,14 +165,16 @@ func (m *Manager) exportSnapshot(height uint64, snapshotWriter func([]byte) erro
keys := maps.Keys(m.txHashes)
sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i][:], keys[j][:]) < 0 })
timestamp := time.Unix(int64(height), 0)
for _, txHash := range keys {
ttl := m.txHashes[txHash]
if height > ttl {
timeoutTime := m.txHashes[txHash]
if timestamp.After(timeoutTime) {
// skip expired txs that have yet to be purged
continue
}
chunk := unorderedTxToBytes(txHash, ttl)
// right now we dont have access block time at this flow, so we would just include the expired txs
// and let it be purge during purge loop
chunk := unorderedTxToBytes(txHash, uint64(timeoutTime.Unix()))
if _, err := w.Write(chunk); err != nil {
return fmt.Errorf("failed to write unordered tx to buffer: %w", err)
@ -195,8 +198,8 @@ func (m *Manager) flushToFile() error {
defer f.Close()
w := bufio.NewWriter(f)
for txHash, ttl := range m.txHashes {
chunk := unorderedTxToBytes(txHash, ttl)
for txHash, timestamp := range m.txHashes {
chunk := unorderedTxToBytes(txHash, uint64(timestamp.Unix()))
if _, err = w.Write(chunk); err != nil {
return fmt.Errorf("failed to write unordered tx to buffer: %w", err)
@ -211,13 +214,13 @@ func (m *Manager) flushToFile() error {
}
// expiredTxs returns expired tx hashes based on the provided block height.
func (m *Manager) expiredTxs(blockHeight uint64) []TxHash {
func (m *Manager) expiredTxs(blockTime time.Time) []TxHash {
m.mu.RLock()
defer m.mu.RUnlock()
var result []TxHash
for txHash, ttl := range m.txHashes {
if blockHeight > ttl {
for txHash, timestamp := range m.txHashes {
if blockTime.After(timestamp) {
result = append(result, txHash)
}
}
@ -237,37 +240,38 @@ func (m *Manager) purge(txHashes []TxHash) {
// purgeLoop removes expired tx hashes in the background
func (m *Manager) purgeLoop() {
for {
latestHeight, ok := m.batchReceive()
latestTime, ok := m.batchReceive()
if !ok {
// channel closed
m.doneCh <- struct{}{}
return
}
hashes := m.expiredTxs(latestHeight)
hashes := m.expiredTxs(latestTime)
if len(hashes) > 0 {
m.purge(hashes)
}
}
}
func (m *Manager) batchReceive() (uint64, bool) {
func (m *Manager) batchReceive() (time.Time, bool) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var latestHeight uint64
var latestTime time.Time
for {
select {
case <-ctx.Done():
return latestHeight, true
return latestTime, true
case blockHeight, ok := <-m.blockCh:
case blockTime, ok := <-m.blockCh:
if !ok {
// channel is closed
return 0, false
return time.Time{}, false
}
if blockHeight > latestHeight {
latestHeight = blockHeight
if blockTime.After(latestTime) {
latestTime = blockTime
}
}
}
@ -277,7 +281,7 @@ func unorderedTxToBytes(txHash TxHash, ttl uint64) []byte {
chunk := make([]byte, chunkSize)
copy(chunk[:txHashSize], txHash[:])
ttlBz := make([]byte, ttlSize)
ttlBz := make([]byte, timeoutSize)
binary.BigEndian.PutUint64(ttlBz, ttl)
copy(chunk[txHashSize:], ttlBz)

View File

@ -25,9 +25,9 @@ func TestUnorderedTxManager_SimpleSize(t *testing.T) {
txm.Start()
txm.Add([32]byte{0xFF}, 100)
txm.Add([32]byte{0xAA}, 100)
txm.Add([32]byte{0xCC}, 100)
txm.Add([32]byte{0xFF}, time.Now())
txm.Add([32]byte{0xAA}, time.Now())
txm.Add([32]byte{0xCC}, time.Now())
require.Equal(t, 3, txm.Size())
}
@ -42,7 +42,7 @@ func TestUnorderedTxManager_SimpleContains(t *testing.T) {
for i := 0; i < 10; i++ {
txHash := [32]byte{byte(i)}
txm.Add(txHash, 100)
txm.Add(txHash, time.Now())
require.True(t, txm.Contains(txHash))
}
@ -70,7 +70,7 @@ func TestUnorderedTxManager_CloseInit(t *testing.T) {
// add a handful of unordered txs
for i := 0; i < 100; i++ {
txm.Add([32]byte{byte(i)}, 100)
txm.Add([32]byte{byte(i)}, time.Now())
}
// close the manager, which should flush all unexpired txs to file
@ -100,15 +100,17 @@ func TestUnorderedTxManager_Flow(t *testing.T) {
txm.Start()
currentTime := time.Now()
// Seed the manager with a txs, some of which should eventually be purged and
// the others will remain. Txs with TTL less than or equal to 50 should be purged.
for i := 1; i <= 100; i++ {
txHash := [32]byte{byte(i)}
if i <= 50 {
txm.Add(txHash, uint64(i))
txm.Add(txHash, currentTime.Add(time.Millisecond*500*time.Duration(i)))
} else {
txm.Add(txHash, 100)
txm.Add(txHash, currentTime.Add(time.Hour))
}
}
@ -118,19 +120,12 @@ func TestUnorderedTxManager_Flow(t *testing.T) {
ticker := time.NewTicker(time.Millisecond * 500)
defer ticker.Stop()
var (
height uint64 = 1
i = 101
)
for range ticker.C {
txm.OnNewBlock(height)
height++
for t := range ticker.C {
txm.OnNewBlock(t)
if height > 51 {
if t.After(currentTime.Add(time.Millisecond * 500 * time.Duration(50))) {
doneBlockCh <- true
return
} else {
txm.Add([32]byte{byte(i)}, 50)
}
}
}()

View File

@ -4,14 +4,15 @@ import (
"encoding/binary"
"errors"
"io"
"time"
snapshot "cosmossdk.io/store/snapshots/types"
)
const (
txHashSize = 32
ttlSize = 8
chunkSize = txHashSize + ttlSize
txHashSize = 32
timeoutSize = 8
chunkSize = txHashSize + timeoutSize
)
var _ snapshot.ExtensionSnapshotter = &Snapshotter{}
@ -78,11 +79,13 @@ func (s *Snapshotter) restore(height uint64, payloadReader snapshot.ExtensionPay
var txHash TxHash
copy(txHash[:], payload[i:i+txHashSize])
ttl := binary.BigEndian.Uint64(payload[i+txHashSize : i+chunkSize])
if height < ttl {
// only add unordered transactions that are still valid, i.e. unexpired
s.m.Add(txHash, ttl)
timestamp := binary.BigEndian.Uint64(payload[i+txHashSize : i+chunkSize])
// need to come up with a way to fetch blocktime to filter out expired txs
//
// right now we dont have access block time at this flow, so we would just include the expired txs
// and let it be purge during purge loop
if timestamp != 0 && timestamp > height {
s.m.Add(txHash, time.Unix(int64(timestamp), 0))
}
i += chunkSize

View File

@ -2,6 +2,7 @@ package unorderedtx_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
@ -12,9 +13,11 @@ func TestSnapshotter(t *testing.T) {
dataDir := t.TempDir()
txm := unorderedtx.NewManager(dataDir)
currentTime := time.Now()
// add a handful of unordered txs
for i := 0; i < 100; i++ {
txm.Add([32]byte{byte(i)}, 100)
txm.Add([32]byte{byte(i)}, currentTime.Add(time.Second*100))
}
var unorderedTxBz []byte
@ -36,17 +39,17 @@ func TestSnapshotter(t *testing.T) {
err = s.RestoreExtension(50, 2, pr)
require.Error(t, err)
// restore with height > ttl which should result in no unordered txs synced
// restore with timestamp > timeout time which should result in no unordered txs synced
txm2 := unorderedtx.NewManager(dataDir)
s2 := unorderedtx.NewSnapshotter(txm2)
err = s2.RestoreExtension(200, unorderedtx.SnapshotFormat, pr)
err = s2.RestoreExtension(uint64(currentTime.Add(time.Second*200).Unix()), unorderedtx.SnapshotFormat, pr)
require.NoError(t, err)
require.Empty(t, txm2.Size())
// restore with height < ttl which should result in all unordered txs synced
// restore with timestamp < timeout time which should result in all unordered txs synced
txm3 := unorderedtx.NewManager(dataDir)
s3 := unorderedtx.NewSnapshotter(txm3)
err = s3.RestoreExtension(50, unorderedtx.SnapshotFormat, pr)
err = s3.RestoreExtension(uint64(currentTime.Add(time.Second*50).Unix()), unorderedtx.SnapshotFormat, pr)
require.NoError(t, err)
require.Equal(t, 100, txm3.Size())

View File

@ -3,9 +3,11 @@ package tx
import (
"errors"
"fmt"
"time"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
multisigv1beta1 "cosmossdk.io/api/cosmos/crypto/multisig/v1beta1"
@ -79,16 +81,17 @@ type builder struct {
decoder *decode.Decoder
codec codec.BinaryCodec
msgs []sdk.Msg
timeoutHeight uint64
granter []byte
payer []byte
unordered bool
memo string
gasLimit uint64
fees sdk.Coins
signerInfos []*tx.SignerInfo
signatures [][]byte
msgs []sdk.Msg
timeoutHeight uint64
timeoutTimestamp time.Time
granter []byte
payer []byte
unordered bool
memo string
gasLimit uint64
fees sdk.Coins
signerInfos []*tx.SignerInfo
signatures [][]byte
extensionOptions []*codectypes.Any
nonCriticalExtensionOptions []*codectypes.Any
@ -115,6 +118,7 @@ func (w *builder) getTx() (*gogoTxWrapper, error) {
Messages: anyMsgs,
Memo: w.memo,
TimeoutHeight: w.timeoutHeight,
TimeoutTimestamp: timestamppb.New(w.timeoutTimestamp),
Unordered: w.unordered,
ExtensionOptions: intoAnyV2(w.extensionOptions),
NonCriticalExtensionOptions: intoAnyV2(w.nonCriticalExtensionOptions),
@ -189,6 +193,8 @@ func (w *builder) SetMsgs(msgs ...sdk.Msg) error {
// SetTimeoutHeight sets the transaction's height timeout.
func (w *builder) SetTimeoutHeight(height uint64) { w.timeoutHeight = height }
func (w *builder) SetTimeoutTimestamp(timestamp time.Time) { w.timeoutTimestamp = timestamp }
func (w *builder) SetUnordered(v bool) { w.unordered = v }
func (w *builder) SetMemo(memo string) { w.memo = memo }

View File

@ -4,6 +4,7 @@ import (
"fmt"
"reflect"
"strings"
"time"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/reflect/protoreflect"
@ -177,6 +178,11 @@ func (w *gogoTxWrapper) GetMemo() string { return w.Tx.Body.Memo }
// GetTimeoutHeight returns the transaction's timeout height (if set).
func (w *gogoTxWrapper) GetTimeoutHeight() uint64 { return w.Tx.Body.TimeoutHeight }
// GetTimeoutTimeStamp returns the transaction's timeout timestamp (if set).
func (w *gogoTxWrapper) GetTimeoutTimeStamp() time.Time {
return w.Tx.Body.TimeoutTimestamp.AsTime()
}
// GetUnordered returns the transaction's unordered field (if set).
func (w *gogoTxWrapper) GetUnordered() bool { return w.Tx.Body.Unordered }