feat: [ADR-070] Unordered Transactions (1/2) (#18641)
Co-authored-by: yihuang <huang@crypto.com> Co-authored-by: Facundo <facundomedica@gmail.com>
This commit is contained in:
parent
d520fcf7cc
commit
6ec53aaf54
@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Features
|
||||
|
||||
* (x/auth) Support the ability to broadcast unordered transactions per ADR-070. See UPGRADING.md for more details on integration.
|
||||
* (client) [#18557](https://github.com/cosmos/cosmos-sdk/pull/18557) Add `--qrcode` flag to `keys show` command to support displaying keys address QR code.
|
||||
* (x/staking) [#18142](https://github.com/cosmos/cosmos-sdk/pull/18142) Introduce `key_rotation_fee` param to calculate fees while rotating the keys
|
||||
* (client) [#18101](https://github.com/cosmos/cosmos-sdk/pull/18101) Add a `keyring-default-keyname` in `client.toml` for specifying a default key name, and skip the need to use the `--from` flag when signing transactions.
|
||||
@ -189,7 +190,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (x/auth) [#18351](https://github.com/cosmos/cosmos-sdk/pull/18351) Auth module was moved to its own go.mod `cosmossdk.io/x/auth`
|
||||
* (types) [#18372](https://github.com/cosmos/cosmos-sdk/pull/18372) Removed global configuration for coin type and purpose. Setters and getters should be removed and access directly to defined types.
|
||||
* (types) [#18695](https://github.com/cosmos/cosmos-sdk/pull/18695) Removed global configuration for txEncoder.
|
||||
* (server) [#18909](https://github.com/cosmos/cosmos-sdk/pull/18909) Remove configuration endpoint on grpc reflection endpoint in favour of auth module bech32prefix endpoint already exposed.
|
||||
* (server) [#18909](https://github.com/cosmos/cosmos-sdk/pull/18909) Remove configuration endpoint on grpc reflection endpoint in favour of auth module bech32prefix endpoint already exposed.
|
||||
|
||||
### CLI Breaking Changes
|
||||
|
||||
|
||||
75
UPGRADING.md
75
UPGRADING.md
@ -5,6 +5,81 @@ Note, always read the **SimApp** section for more information on application wir
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Unordered Transactions
|
||||
|
||||
The Cosmos SDK now supports unordered transactions. This means that transactions
|
||||
can be executed in any order and doesn't require the client to deal with or manage
|
||||
nonces. This also means the order of execution is not guaranteed. To enable unordered
|
||||
transactions in your application:
|
||||
|
||||
* Update the `App` constructor to create, load, and save the unordered transaction
|
||||
manager.
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
|
||||
// create, start, and load the unordered tx manager
|
||||
utxDataDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data")
|
||||
app.UnorderedTxManager = unorderedtx.NewManager(utxDataDir)
|
||||
app.UnorderedTxManager.Start()
|
||||
|
||||
if err := app.UnorderedTxManager.OnInit(); err != nil {
|
||||
panic(fmt.Errorf("failed to initialize unordered tx manager: %w", err))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Add the decorator to the existing AnteHandler chain, which should be as early
|
||||
as possible.
|
||||
|
||||
```go
|
||||
anteDecorators := []sdk.AnteDecorator{
|
||||
ante.NewSetUpContextDecorator(),
|
||||
// ...
|
||||
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, app.UnorderedTxManager),
|
||||
// ...
|
||||
}
|
||||
|
||||
return sdk.ChainAnteDecorators(anteDecorators...), nil
|
||||
```
|
||||
|
||||
* If the App has a SnapshotManager defined, you must also register the extension
|
||||
for the TxManager.
|
||||
|
||||
```go
|
||||
if manager := app.SnapshotManager(); manager != nil {
|
||||
err := manager.RegisterExtensions(unorderedtx.NewSnapshotter(app.UnorderedTxManager))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to register snapshot extension: %s", err))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Create or update the App's `Close()` method to close the unordered tx manager.
|
||||
Note, this is critical as it ensures the manager's state is written to file
|
||||
such that when the node restarts, it can recover the state to provide replay
|
||||
protection.
|
||||
|
||||
```go
|
||||
func (app *App) Close() error {
|
||||
// ...
|
||||
|
||||
// close the unordered tx manager
|
||||
if e := app.UnorderedTxManager.Close(); e != nil {
|
||||
err = errors.Join(err, e)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
To submit an unordered transaction, the client must set the `unordered` flag to
|
||||
`true` and ensure a reasonable `timeout_height` is set. The `timeout_height` is
|
||||
used as a TTL for the transaction and is used to provide replay protection. See
|
||||
[ADR-070](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-070-unordered-account.md)
|
||||
for more details.
|
||||
|
||||
### Params
|
||||
|
||||
* Params Migrations were removed. It is required to migrate to 0.50 prior to upgrading to .51.
|
||||
|
||||
@ -2767,6 +2767,7 @@ var (
|
||||
fd_TxBody_messages protoreflect.FieldDescriptor
|
||||
fd_TxBody_memo protoreflect.FieldDescriptor
|
||||
fd_TxBody_timeout_height protoreflect.FieldDescriptor
|
||||
fd_TxBody_unordered protoreflect.FieldDescriptor
|
||||
fd_TxBody_extension_options protoreflect.FieldDescriptor
|
||||
fd_TxBody_non_critical_extension_options protoreflect.FieldDescriptor
|
||||
)
|
||||
@ -2777,6 +2778,7 @@ func init() {
|
||||
fd_TxBody_messages = md_TxBody.Fields().ByName("messages")
|
||||
fd_TxBody_memo = md_TxBody.Fields().ByName("memo")
|
||||
fd_TxBody_timeout_height = md_TxBody.Fields().ByName("timeout_height")
|
||||
fd_TxBody_unordered = md_TxBody.Fields().ByName("unordered")
|
||||
fd_TxBody_extension_options = md_TxBody.Fields().ByName("extension_options")
|
||||
fd_TxBody_non_critical_extension_options = md_TxBody.Fields().ByName("non_critical_extension_options")
|
||||
}
|
||||
@ -2864,6 +2866,12 @@ func (x *fastReflection_TxBody) Range(f func(protoreflect.FieldDescriptor, proto
|
||||
return
|
||||
}
|
||||
}
|
||||
if x.Unordered != false {
|
||||
value := protoreflect.ValueOfBool(x.Unordered)
|
||||
if !f(fd_TxBody_unordered, value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(x.ExtensionOptions) != 0 {
|
||||
value := protoreflect.ValueOfList(&_TxBody_1023_list{list: &x.ExtensionOptions})
|
||||
if !f(fd_TxBody_extension_options, value) {
|
||||
@ -2897,6 +2905,8 @@ func (x *fastReflection_TxBody) Has(fd protoreflect.FieldDescriptor) bool {
|
||||
return x.Memo != ""
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
return x.TimeoutHeight != uint64(0)
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
return x.Unordered != false
|
||||
case "cosmos.tx.v1beta1.TxBody.extension_options":
|
||||
return len(x.ExtensionOptions) != 0
|
||||
case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options":
|
||||
@ -2923,6 +2933,8 @@ func (x *fastReflection_TxBody) Clear(fd protoreflect.FieldDescriptor) {
|
||||
x.Memo = ""
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
x.TimeoutHeight = uint64(0)
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
x.Unordered = false
|
||||
case "cosmos.tx.v1beta1.TxBody.extension_options":
|
||||
x.ExtensionOptions = nil
|
||||
case "cosmos.tx.v1beta1.TxBody.non_critical_extension_options":
|
||||
@ -2955,6 +2967,9 @@ func (x *fastReflection_TxBody) Get(descriptor protoreflect.FieldDescriptor) pro
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
value := x.TimeoutHeight
|
||||
return protoreflect.ValueOfUint64(value)
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
value := x.Unordered
|
||||
return protoreflect.ValueOfBool(value)
|
||||
case "cosmos.tx.v1beta1.TxBody.extension_options":
|
||||
if len(x.ExtensionOptions) == 0 {
|
||||
return protoreflect.ValueOfList(&_TxBody_1023_list{})
|
||||
@ -2995,6 +3010,8 @@ func (x *fastReflection_TxBody) Set(fd protoreflect.FieldDescriptor, value proto
|
||||
x.Memo = value.Interface().(string)
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
x.TimeoutHeight = value.Uint()
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
x.Unordered = value.Bool()
|
||||
case "cosmos.tx.v1beta1.TxBody.extension_options":
|
||||
lv := value.List()
|
||||
clv := lv.(*_TxBody_1023_list)
|
||||
@ -3045,6 +3062,8 @@ func (x *fastReflection_TxBody) Mutable(fd protoreflect.FieldDescriptor) protore
|
||||
panic(fmt.Errorf("field memo of message cosmos.tx.v1beta1.TxBody is not mutable"))
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
panic(fmt.Errorf("field timeout_height of message cosmos.tx.v1beta1.TxBody is not mutable"))
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
panic(fmt.Errorf("field unordered of message cosmos.tx.v1beta1.TxBody is not mutable"))
|
||||
default:
|
||||
if fd.IsExtension() {
|
||||
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.tx.v1beta1.TxBody"))
|
||||
@ -3065,6 +3084,8 @@ func (x *fastReflection_TxBody) NewField(fd protoreflect.FieldDescriptor) protor
|
||||
return protoreflect.ValueOfString("")
|
||||
case "cosmos.tx.v1beta1.TxBody.timeout_height":
|
||||
return protoreflect.ValueOfUint64(uint64(0))
|
||||
case "cosmos.tx.v1beta1.TxBody.unordered":
|
||||
return protoreflect.ValueOfBool(false)
|
||||
case "cosmos.tx.v1beta1.TxBody.extension_options":
|
||||
list := []*anypb.Any{}
|
||||
return protoreflect.ValueOfList(&_TxBody_1023_list{list: &list})
|
||||
@ -3153,6 +3174,9 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
|
||||
if x.TimeoutHeight != 0 {
|
||||
n += 1 + runtime.Sov(uint64(x.TimeoutHeight))
|
||||
}
|
||||
if x.Unordered {
|
||||
n += 2
|
||||
}
|
||||
if len(x.ExtensionOptions) > 0 {
|
||||
for _, e := range x.ExtensionOptions {
|
||||
l = options.Size(e)
|
||||
@ -3230,6 +3254,16 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
|
||||
dAtA[i] = 0xfa
|
||||
}
|
||||
}
|
||||
if x.Unordered {
|
||||
i--
|
||||
if x.Unordered {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if x.TimeoutHeight != 0 {
|
||||
i = runtime.EncodeVarint(dAtA, i, uint64(x.TimeoutHeight))
|
||||
i--
|
||||
@ -3392,6 +3426,26 @@ func (x *fastReflection_TxBody) ProtoMethods() *protoiface.Methods {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Unordered", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
x.Unordered = bool(v != 0)
|
||||
case 1023:
|
||||
if wireType != 2 {
|
||||
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType)
|
||||
@ -8414,11 +8468,26 @@ type TxBody struct {
|
||||
Messages []*anypb.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
// memo is any arbitrary note/comment to be added to the transaction.
|
||||
// WARNING: in clients, any publicly exposed text should not be called memo,
|
||||
// but should be called `note` instead (see https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
// but should be called `note` instead (see
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
|
||||
// timeout is the block height after which this transaction will not
|
||||
// be processed by the chain
|
||||
// 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
|
||||
// fashion. Specifically, the account's nonce will NOT be checked or
|
||||
// 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
|
||||
// valid.
|
||||
Unordered bool `protobuf:"varint,4,opt,name=unordered,proto3" json:"unordered,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
|
||||
@ -8470,6 +8539,13 @@ func (x *TxBody) GetTimeoutHeight() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TxBody) GetUnordered() bool {
|
||||
if x != nil {
|
||||
return x.Unordered
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TxBody) GetExtensionOptions() []*anypb.Any {
|
||||
if x != nil {
|
||||
return x.ExtensionOptions
|
||||
@ -8703,13 +8779,15 @@ type Fee struct {
|
||||
// gas_limit is the maximum gas that can be used in transaction processing
|
||||
// before an out of gas error occurs
|
||||
GasLimit uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"`
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees.
|
||||
// the payer must be a tx signer (and thus have signed this field in AuthInfo).
|
||||
// setting this field does *not* change the ordering of required signers for the transaction.
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the
|
||||
// specified account must pay the fees. the payer must be a tx signer (and
|
||||
// thus have signed this field in AuthInfo). setting this field does *not*
|
||||
// change the ordering of required signers for the transaction.
|
||||
Payer string `protobuf:"bytes,3,opt,name=payer,proto3" json:"payer,omitempty"`
|
||||
// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used
|
||||
// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does
|
||||
// not support fee grants, this will fail
|
||||
// if set, the fee payer (either the first signer or the value of the payer
|
||||
// field) requests that a fee grant be used to pay fees instead of the fee
|
||||
// payer's own balance. If an appropriate fee grant does not exist or the
|
||||
// chain does not support fee grants, this will fail
|
||||
Granter string `protobuf:"bytes,4,opt,name=granter,proto3" json:"granter,omitempty"`
|
||||
}
|
||||
|
||||
@ -9030,119 +9108,121 @@ var file_cosmos_tx_v1beta1_tx_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x22, 0x95, 0x02, 0x0a, 0x06, 0x54, 0x78, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x30, 0x0a, 0x08, 0x6d,
|
||||
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, 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, 0xa4, 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, 0x2c, 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, 0x02, 0x18, 0x01, 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,
|
||||
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, 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,
|
||||
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, 0xa4, 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,
|
||||
0x2c, 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, 0x02, 0x18, 0x01, 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, 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, 0xb6, 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, 0x02, 0x18, 0x01, 0x22, 0xce, 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, 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, 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,
|
||||
0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x72, 0x22, 0xb6,
|
||||
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, 0x02, 0x18, 0x01, 0x22, 0xce, 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, 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, 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 (
|
||||
|
||||
@ -74,6 +74,7 @@ const (
|
||||
FlagOffset = "offset"
|
||||
FlagCountTotal = "count-total"
|
||||
FlagTimeoutHeight = "timeout-height"
|
||||
FlagUnordered = "unordered"
|
||||
FlagKeyAlgorithm = "algo"
|
||||
FlagKeyType = "key-type"
|
||||
FlagFeePayer = "fee-payer"
|
||||
@ -136,6 +137,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
|
||||
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.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")
|
||||
|
||||
@ -36,6 +36,7 @@ type Factory struct {
|
||||
gasAdjustment float64
|
||||
chainID string
|
||||
fromName string
|
||||
unordered bool
|
||||
offline bool
|
||||
generateOnly bool
|
||||
memo string
|
||||
@ -86,6 +87,7 @@ 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)
|
||||
unordered := clientCtx.Viper.GetBool(flags.FlagUnordered)
|
||||
|
||||
gasStr := clientCtx.Viper.GetString(flags.FlagGas)
|
||||
gasSetting, _ := flags.ParseGasSetting(gasStr)
|
||||
@ -103,6 +105,7 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
|
||||
accountNumber: accNum,
|
||||
sequence: accSeq,
|
||||
timeoutHeight: timeoutHeight,
|
||||
unordered: unordered,
|
||||
gasAdjustment: gasAdj,
|
||||
memo: memo,
|
||||
signMode: signMode,
|
||||
@ -132,6 +135,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) Unordered() bool { return f.unordered }
|
||||
func (f Factory) FromName() string { return f.fromName }
|
||||
|
||||
// SimulateAndExecute returns the option to simulate and then execute the transaction
|
||||
@ -245,6 +249,12 @@ func (f Factory) WithTimeoutHeight(height uint64) Factory {
|
||||
return f
|
||||
}
|
||||
|
||||
// WithUnordered returns a copy of the Factory with an updated unordered field.
|
||||
func (f Factory) WithUnordered(v bool) Factory {
|
||||
f.unordered = v
|
||||
return f
|
||||
}
|
||||
|
||||
// WithFeeGranter returns a copy of the Factory with an updated fee granter.
|
||||
func (f Factory) WithFeeGranter(fg sdk.AccAddress) Factory {
|
||||
f.feeGranter = fg
|
||||
|
||||
@ -48,6 +48,7 @@ type (
|
||||
SetFeePayer(feePayer sdk.AccAddress)
|
||||
SetGasLimit(limit uint64)
|
||||
SetTimeoutHeight(height uint64)
|
||||
SetUnordered(v bool)
|
||||
SetFeeGranter(feeGranter sdk.AccAddress)
|
||||
AddAuxSignerData(tx.AuxSignerData) error
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## Changelog
|
||||
|
||||
* Dec 4, 2023: Initial Draft
|
||||
* Dec 4, 2023: Initial Draft (@yihuang, @tac0turtle, @alexanderbez)
|
||||
|
||||
## Status
|
||||
|
||||
@ -53,79 +53,140 @@ message TxBody {
|
||||
}
|
||||
```
|
||||
|
||||
### `DedupTxHashManager`
|
||||
### Replay Protection
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
during `DeliverTx`, the transaction's hash is set in the map along with it's TTL
|
||||
value.
|
||||
|
||||
This map is evaluated at the end of each block, e.g. ABCI `Commit`, and all stale
|
||||
transactions, i.e. transactions's TTL value who's now beyond the committed block,
|
||||
are purged from the map.
|
||||
|
||||
An important point to note is that in theory, it may be possible to submit an unordered
|
||||
transaction twice, or multiple times, before the transaction is included in a block.
|
||||
However, we'll note a few important layers of protection and mitigation:
|
||||
|
||||
* Assuming CometBFT is used as the underlying consensus engine and a non-noop mempool
|
||||
is used, CometBFT will reject the duplicate for you.
|
||||
* For applications that leverage ABCI++, `ProcessProposal` should evaluate and reject
|
||||
malicious proposals with duplicate transactions.
|
||||
* For applications that leverage their own application mempool, their mempool should
|
||||
reject the duplicate for you.
|
||||
* Finally, worst case if the duplicate transaction is somehow selected for a block
|
||||
proposal, 2nd and all further attempts to evaluate it, will fail during `DeliverTx`,
|
||||
so worst case you just end up filling up block space with a duplicate transaction.
|
||||
|
||||
```golang
|
||||
type TxHash [32]byte
|
||||
|
||||
const PurgeLoopSleepMS = 500
|
||||
|
||||
// DedupTxHashManager contains the tx hash dictionary for duplicates checking,
|
||||
// and expire them when block number progresses.
|
||||
type DedupTxHashManager struct {
|
||||
mutex sync.RWMutex
|
||||
// tx hash -> expire block number
|
||||
// for duplicates checking and expiration
|
||||
hashes map[TxHash]uint64
|
||||
// channel to receive latest block numbers
|
||||
// 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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func NewDedupTxHashManager() *DedupTxHashManager {
|
||||
m := &DedupTxHashManager{
|
||||
hashes: make(map[TxHash]uint64),
|
||||
blockCh: make(ch *uint64, 16),
|
||||
func NewUnorderedTxManager() *UnorderedTxManager {
|
||||
m := &UnorderedTxManager{
|
||||
blockCh: make(chan uint64, 16),
|
||||
txHashes: make(map[TxHash]uint64),
|
||||
}
|
||||
go m.purgeLoop()
|
||||
return m
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (dtm *DedupTxHashManager) Close() error {
|
||||
close(dtm.blockCh)
|
||||
dtm.blockCh = nil
|
||||
func (m *UnorderedTxManager) Start() {
|
||||
go m.purgeLoop()
|
||||
}
|
||||
|
||||
func (m *UnorderedTxManager) Close() error {
|
||||
close(m.blockCh)
|
||||
m.blockCh = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dtm *DedupTxHashManager) Contains(hash TxHash) (ok bool) {
|
||||
dtm.mutex.RLock()
|
||||
defer dtm.mutex.RUnlock()
|
||||
func (m *UnorderedTxManager) Contains(hash TxHash) bool{
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
_, ok = dtm.hashes[hash]
|
||||
return
|
||||
_, ok := m.txHashes[hash]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (dtm *DedupTxHashManager) Size() int {
|
||||
dtm.mutex.RLock()
|
||||
defer dtm.mutex.RUnlock()
|
||||
func (m *UnorderedTxManager) Size() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
return len(dtm.hashes)
|
||||
return len(m.txHashes)
|
||||
}
|
||||
|
||||
func (dtm *DedupTxHashManager) Add(hash TxHash, expire uint64) (ok bool) {
|
||||
dtm.mutex.Lock()
|
||||
defer dtm.mutex.Unlock()
|
||||
func (m *UnorderedTxManager) Add(hash TxHash, expire uint64) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
dtm.hashes[hash] = expire
|
||||
return
|
||||
m.txHashes[hash] = expire
|
||||
}
|
||||
|
||||
// OnNewBlock send the latest block number to the background purge loop,
|
||||
// it should be called in abci commit event.
|
||||
func (dtm *DedupTxHashManager) OnNewBlock(blockNumber uint64) {
|
||||
dtm.blockCh <- &blockNumber
|
||||
// OnNewBlock send the latest block number to the background purge loop, which
|
||||
// should be called in ABCI Commit event.
|
||||
func (m *UnorderedTxManager) OnNewBlock(blockHeight uint64) {
|
||||
m.blockCh <- blockHeight
|
||||
}
|
||||
|
||||
// purgeLoop removes expired tx hashes at background
|
||||
func (dtm *DedupTxHashManager) purgeLoop() error {
|
||||
// expiredTxs returns expired tx hashes based on the provided block height.
|
||||
func (m *UnorderedTxManager) expiredTxs(blockHeight uint64) []TxHash {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var result []TxHash
|
||||
for txHash, expire := range m.txHashes {
|
||||
if blockHeight > expire {
|
||||
result = append(result, txHash)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *UnorderedTxManager) purge(txHashes []TxHash) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, txHash := range txHashes {
|
||||
delete(m.txHashes, txHash)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// purgeLoop removes expired tx hashes in the background
|
||||
func (m *UnorderedTxManager) purgeLoop() error {
|
||||
for {
|
||||
blocks := channelBatchRecv(dtm.blockCh)
|
||||
blocks := channelBatchRecv(m.blockCh)
|
||||
if len(blocks) == 0 {
|
||||
// channel closed
|
||||
break
|
||||
}
|
||||
|
||||
latest := *blocks[len(blocks)-1]
|
||||
hashes := dtm.expired(latest)
|
||||
hashes := m.expired(latest)
|
||||
if len(hashes) > 0 {
|
||||
dtm.purge(hashes)
|
||||
m.purge(hashes)
|
||||
}
|
||||
|
||||
// avoid burning cpu in catching up phase
|
||||
@ -133,28 +194,6 @@ func (dtm *DedupTxHashManager) purgeLoop() error {
|
||||
}
|
||||
}
|
||||
|
||||
// expired find out expired tx hashes based on latest block number
|
||||
func (dtm *DedupTxHashManager) expired(block uint64) []TxHash {
|
||||
dtm.mutex.RLock()
|
||||
defer dtm.mutex.RUnlock()
|
||||
|
||||
var result []TxHash
|
||||
for h, expire := range dtm.hashes {
|
||||
if block > expire {
|
||||
result = append(result, h)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (dtm *DedupTxHashManager) purge(hashes []TxHash) {
|
||||
dtm.mutex.Lock()
|
||||
defer dtm.mutex.Unlock()
|
||||
|
||||
for _, hash := range hashes {
|
||||
delete(dtm.hashes, hash)
|
||||
}
|
||||
}
|
||||
|
||||
// channelBatchRecv try to exhaust the channel buffer when it's not empty,
|
||||
// and block when it's empty.
|
||||
@ -176,9 +215,11 @@ func channelBatchRecv[T any](ch <-chan *T) []*T {
|
||||
}
|
||||
```
|
||||
|
||||
### Ante Handlers
|
||||
### AnteHandler Decorator
|
||||
|
||||
Bypass the nonce decorator for un-ordered transactions.
|
||||
In order to facilitate bypassing nonce verification, we have to modify the existing
|
||||
`IncrementSequenceDecorator` AnteHandler decorator to skip the nonce verification
|
||||
when the transaction is marked as un-ordered.
|
||||
|
||||
```golang
|
||||
func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
@ -186,25 +227,26 @@ func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// the previous logic
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
A decorator for the new logic.
|
||||
In addition, we need to introduce a new decorator to perform the un-ordered transaction
|
||||
verification and map lookup.
|
||||
|
||||
```golang
|
||||
type TxHash [32]byte
|
||||
|
||||
const (
|
||||
// MaxUnOrderedTTL defines the maximum ttl an un-order tx can set
|
||||
MaxUnOrderedTTL = 1024
|
||||
// DefaultMaxUnOrderedTTL defines the default maximum TTL an un-ordered transaction
|
||||
// can set.
|
||||
DefaultMaxUnOrderedTTL = 1024
|
||||
)
|
||||
|
||||
type DedupTxDecorator struct {
|
||||
m *DedupTxHashManager
|
||||
m *UnorderedTxManager
|
||||
maxUnOrderedTTL uint64
|
||||
}
|
||||
|
||||
func (dtd *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
func (d *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
// only apply to un-ordered transactions
|
||||
if !tx.UnOrdered() {
|
||||
return next(ctx, tx, simulate)
|
||||
@ -214,18 +256,18 @@ func (dtd *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate boo
|
||||
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "unordered tx must set timeout-height")
|
||||
}
|
||||
|
||||
if tx.TimeoutHeight() > ctx.BlockHeight() + MaxUnOrderedTTL {
|
||||
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "unordered tx ttl exceeds %d", MaxUnOrderedTTL)
|
||||
if tx.TimeoutHeight() > ctx.BlockHeight() + d.maxUnOrderedTTL {
|
||||
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "unordered tx ttl exceeds %d", d.maxUnOrderedTTL)
|
||||
}
|
||||
|
||||
// check for duplicates
|
||||
if dtd.m.Contains(tx.Hash()) {
|
||||
if d.m.Contains(tx.Hash()) {
|
||||
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "tx is duplicated")
|
||||
}
|
||||
|
||||
if !ctx.IsCheckTx() {
|
||||
// a new tx included in the block, add the hash to the dictionary
|
||||
dtd.m.Add(tx.Hash(), tx.TimeoutHeight())
|
||||
// a new tx included in the block, add the hash to the unordered tx manager
|
||||
d.m.Add(tx.Hash(), tx.TimeoutHeight())
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
@ -234,16 +276,24 @@ func (dtd *DedupTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate boo
|
||||
|
||||
### `OnNewBlock`
|
||||
|
||||
Wire the `OnNewBlock` method of `DedupTxHashManager` into the BaseApp's ABCI Commit event.
|
||||
Wire the `OnNewBlock` method of `UnorderedTxManager` into the BaseApp's ABCI `Commit` event.
|
||||
|
||||
### Start Up
|
||||
### State Management
|
||||
|
||||
On start up, the node needs to re-fill the tx hash dictionary of `DedupTxHashManager`
|
||||
by scanning `MaxUnOrderedTTL` number of historical blocks for existing un-expired
|
||||
un-ordered transactions.
|
||||
On start up, the node needs to ensure the TxManager's state contains all un-expired
|
||||
transactions that have been committed to the chain. This is critical since if the
|
||||
state is not properly initialized, the node will not reject duplicate transactions
|
||||
and thus will not provide replay protection, and will likely get an app hash mismatch error.
|
||||
|
||||
An alternative design is to store the tx hash dictionary in kv store, then no need
|
||||
to warm up on start up.
|
||||
We propose to write all un-expired unordered transactions from the TxManager's to
|
||||
file on disk. On start up, the node will read this file and re-populate the TxManager's
|
||||
map. The write to file will happen when the node gracefully shuts down on `Close()`.
|
||||
|
||||
Note, this is not a perfect solution, in the context of store v1. With store v2,
|
||||
we can omit explicit file handling altogether and simply write the all the transactions
|
||||
to non-consensus state, i.e State Storage (SS).
|
||||
|
||||
Alternatively, we can write all the transactions to consensus state.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@ -105,13 +105,29 @@ message TxBody {
|
||||
|
||||
// memo is any arbitrary note/comment to be added to the transaction.
|
||||
// WARNING: in clients, any publicly exposed text should not be called memo,
|
||||
// but should be called `note` instead (see https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
// but should be called `note` instead (see
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
string memo = 2;
|
||||
|
||||
// timeout is the block height after which this transaction will not
|
||||
// be processed by the chain
|
||||
// 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)
|
||||
// intend for the transaction to be evaluated and executed in an un-ordered
|
||||
// fashion. Specifically, the account's nonce will NOT be checked or
|
||||
// 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
|
||||
// valid.
|
||||
bool unordered = 4;
|
||||
|
||||
// extension_options are arbitrary options that can be added by chains
|
||||
// when the default options are not sufficient. If any of these are present
|
||||
// and can't be handled, the transaction will be rejected
|
||||
@ -211,14 +227,16 @@ message Fee {
|
||||
// before an out of gas error occurs
|
||||
uint64 gas_limit = 2;
|
||||
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees.
|
||||
// the payer must be a tx signer (and thus have signed this field in AuthInfo).
|
||||
// setting this field does *not* change the ordering of required signers for the transaction.
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the
|
||||
// specified account must pay the fees. the payer must be a tx signer (and
|
||||
// thus have signed this field in AuthInfo). setting this field does *not*
|
||||
// change the ordering of required signers for the transaction.
|
||||
string payer = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
|
||||
// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used
|
||||
// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does
|
||||
// not support fee grants, this will fail
|
||||
// if set, the fee payer (either the first signer or the value of the payer
|
||||
// field) requests that a fee grant be used to pay fees instead of the fee
|
||||
// payer's own balance. If an appropriate fee grant does not exist or the
|
||||
// chain does not support fee grants, this will fail
|
||||
string granter = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"cosmossdk.io/x/auth/ante"
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
circuitante "cosmossdk.io/x/circuit/ante"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -13,6 +14,7 @@ import (
|
||||
type HandlerOptions struct {
|
||||
ante.HandlerOptions
|
||||
CircuitKeeper circuitante.CircuitBreaker
|
||||
TxManager *unorderedtx.Manager
|
||||
}
|
||||
|
||||
// NewAnteHandler returns an AnteHandler that checks and increments sequence
|
||||
@ -37,6 +39,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
|
||||
ante.NewValidateBasicDecorator(),
|
||||
ante.NewTxTimeoutHeightDecorator(),
|
||||
ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, options.TxManager),
|
||||
ante.NewValidateMemoDecorator(options.AccountKeeper),
|
||||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
|
||||
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"cosmossdk.io/x/accounts/testing/counter"
|
||||
"cosmossdk.io/x/auth"
|
||||
"cosmossdk.io/x/auth/ante"
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
authcodec "cosmossdk.io/x/auth/codec"
|
||||
authkeeper "cosmossdk.io/x/auth/keeper"
|
||||
"cosmossdk.io/x/auth/posthandler"
|
||||
@ -169,6 +170,8 @@ type SimApp struct {
|
||||
ModuleManager *module.Manager
|
||||
BasicModuleManager module.BasicManager
|
||||
|
||||
UnorderedTxManager *unorderedtx.Manager
|
||||
|
||||
// simulation manager
|
||||
sm *module.SimulationManager
|
||||
|
||||
@ -519,6 +522,25 @@ func NewSimApp(
|
||||
}
|
||||
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules)
|
||||
|
||||
// create, start, and load the unordered tx manager
|
||||
utxDataDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data")
|
||||
app.UnorderedTxManager = unorderedtx.NewManager(utxDataDir)
|
||||
app.UnorderedTxManager.Start()
|
||||
|
||||
if err := app.UnorderedTxManager.OnInit(); err != nil {
|
||||
panic(fmt.Errorf("failed to initialize unordered tx manager: %w", err))
|
||||
}
|
||||
|
||||
// register custom snapshot extensions (if any)
|
||||
if manager := app.SnapshotManager(); manager != nil {
|
||||
err := manager.RegisterExtensions(
|
||||
unorderedtx.NewSnapshotter(app.UnorderedTxManager),
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to register snapshot extension: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
app.sm.RegisterStoreDecoders()
|
||||
|
||||
// initialize stores
|
||||
@ -579,6 +601,7 @@ func (app *SimApp) setAnteHandler(txConfig client.TxConfig) {
|
||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
||||
},
|
||||
&app.CircuitKeeper,
|
||||
app.UnorderedTxManager,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@ -600,6 +623,12 @@ func (app *SimApp) setPostHandler() {
|
||||
app.SetPostHandler(postHandler)
|
||||
}
|
||||
|
||||
// Close implements the Application interface and closes all necessary application
|
||||
// resources.
|
||||
func (app *SimApp) Close() error {
|
||||
return app.UnorderedTxManager.Close()
|
||||
}
|
||||
|
||||
// Name returns the name of the App
|
||||
func (app *SimApp) Name() string { return app.BaseApp.Name() }
|
||||
|
||||
|
||||
@ -3,16 +3,19 @@
|
||||
package simapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
"cosmossdk.io/x/auth"
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
authkeeper "cosmossdk.io/x/auth/keeper"
|
||||
authsims "cosmossdk.io/x/auth/simulation"
|
||||
authtypes "cosmossdk.io/x/auth/types"
|
||||
@ -34,6 +37,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
@ -64,6 +68,8 @@ type SimApp struct {
|
||||
txConfig client.TxConfig
|
||||
interfaceRegistry codectypes.InterfaceRegistry
|
||||
|
||||
UnorderedTxManager *unorderedtx.Manager
|
||||
|
||||
// keepers
|
||||
AuthKeeper authkeeper.AccountKeeper
|
||||
BankKeeper bankkeeper.Keeper
|
||||
@ -256,6 +262,25 @@ func NewSimApp(
|
||||
// return app.App.InitChainer(ctx, req)
|
||||
// })
|
||||
|
||||
// create, start, and load the unordered tx manager
|
||||
utxDataDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data")
|
||||
app.UnorderedTxManager = unorderedtx.NewManager(utxDataDir)
|
||||
app.UnorderedTxManager.Start()
|
||||
|
||||
if err := app.UnorderedTxManager.OnInit(); err != nil {
|
||||
panic(fmt.Errorf("failed to initialize unordered tx manager: %w", err))
|
||||
}
|
||||
|
||||
// register custom snapshot extensions (if any)
|
||||
if manager := app.SnapshotManager(); manager != nil {
|
||||
err := manager.RegisterExtensions(
|
||||
unorderedtx.NewSnapshotter(app.UnorderedTxManager),
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to register snapshot extension: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.Load(loadLatest); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -263,6 +288,12 @@ func NewSimApp(
|
||||
return app
|
||||
}
|
||||
|
||||
// Close implements the Application interface and closes all necessary application
|
||||
// resources.
|
||||
func (app *SimApp) Close() error {
|
||||
return app.UnorderedTxManager.Close()
|
||||
}
|
||||
|
||||
// LegacyAmino returns SimApp's amino codec.
|
||||
//
|
||||
// NOTE: This is solely to be used for testing purposes as it may be desirable
|
||||
|
||||
2
testutil/testdata/testpb/unknonwnproto.proto
vendored
2
testutil/testdata/testpb/unknonwnproto.proto
vendored
@ -290,7 +290,7 @@ message TestUpdatedTxBody {
|
||||
repeated google.protobuf.Any messages = 1;
|
||||
string memo = 2;
|
||||
int64 timeout_height = 3;
|
||||
uint64 some_new_field = 4;
|
||||
uint64 some_new_field = 5;
|
||||
string some_new_field_non_critical_field = 1050;
|
||||
repeated google.protobuf.Any extension_options = 1023;
|
||||
repeated google.protobuf.Any non_critical_extension_options = 2047;
|
||||
|
||||
@ -23036,7 +23036,7 @@ func (x *fastReflection_TestUpdatedTxBody) ProtoMethods() *protoiface.Methods {
|
||||
if x.SomeNewField != 0 {
|
||||
i = runtime.EncodeVarint(dAtA, i, uint64(x.SomeNewField))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if x.TimeoutHeight != 0 {
|
||||
i = runtime.EncodeVarint(dAtA, i, uint64(x.TimeoutHeight))
|
||||
@ -23200,7 +23200,7 @@ func (x *fastReflection_TestUpdatedTxBody) ProtoMethods() *protoiface.Methods {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SomeNewField", wireType)
|
||||
}
|
||||
@ -26508,7 +26508,7 @@ type TestUpdatedTxBody struct {
|
||||
Messages []*anypb.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
|
||||
TimeoutHeight int64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"`
|
||||
SomeNewField uint64 `protobuf:"varint,4,opt,name=some_new_field,json=someNewField,proto3" json:"some_new_field,omitempty"`
|
||||
SomeNewField uint64 `protobuf:"varint,5,opt,name=some_new_field,json=someNewField,proto3" json:"some_new_field,omitempty"`
|
||||
SomeNewFieldNonCriticalField string `protobuf:"bytes,1050,opt,name=some_new_field_non_critical_field,json=someNewFieldNonCriticalField,proto3" json:"some_new_field_non_critical_field,omitempty"`
|
||||
ExtensionOptions []*anypb.Any `protobuf:"bytes,1023,rep,name=extension_options,json=extensionOptions,proto3" json:"extension_options,omitempty"`
|
||||
NonCriticalExtensionOptions []*anypb.Any `protobuf:"bytes,2047,rep,name=non_critical_extension_options,json=nonCriticalExtensionOptions,proto3" json:"non_critical_extension_options,omitempty"`
|
||||
@ -27410,7 +27410,7 @@ var file_testpb_unknonwnproto_proto_rawDesc = []byte{
|
||||
0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68,
|
||||
0x74, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x66, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x6f, 0x6d, 0x65, 0x4e,
|
||||
0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x6f, 0x6d, 0x65, 0x4e,
|
||||
0x65, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x73, 0x6f, 0x6d, 0x65, 0x5f,
|
||||
0x6e, 0x65, 0x77, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x63, 0x72,
|
||||
0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x9a, 0x08, 0x20,
|
||||
|
||||
10
testutil/testdata/unknonwnproto.pb.go
vendored
10
testutil/testdata/unknonwnproto.pb.go
vendored
@ -2578,7 +2578,7 @@ type TestUpdatedTxBody struct {
|
||||
Messages []*types.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
|
||||
TimeoutHeight int64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"`
|
||||
SomeNewField uint64 `protobuf:"varint,4,opt,name=some_new_field,json=someNewField,proto3" json:"some_new_field,omitempty"`
|
||||
SomeNewField uint64 `protobuf:"varint,5,opt,name=some_new_field,json=someNewField,proto3" json:"some_new_field,omitempty"`
|
||||
SomeNewFieldNonCriticalField string `protobuf:"bytes,1050,opt,name=some_new_field_non_critical_field,json=someNewFieldNonCriticalField,proto3" json:"some_new_field_non_critical_field,omitempty"`
|
||||
ExtensionOptions []*types.Any `protobuf:"bytes,1023,rep,name=extension_options,json=extensionOptions,proto3" json:"extension_options,omitempty"`
|
||||
NonCriticalExtensionOptions []*types.Any `protobuf:"bytes,2047,rep,name=non_critical_extension_options,json=nonCriticalExtensionOptions,proto3" json:"non_critical_extension_options,omitempty"`
|
||||
@ -2906,7 +2906,7 @@ var fileDescriptor_fe4560133be9209a = []byte{
|
||||
0x1a, 0x5f, 0x6a, 0x70, 0x6d, 0xc5, 0x85, 0x3e, 0x73, 0x17, 0xf8, 0x0e, 0x14, 0x66, 0x84, 0x73,
|
||||
0x7b, 0xac, 0x3c, 0xd0, 0x36, 0xa6, 0x56, 0x82, 0x92, 0xd5, 0x3c, 0x23, 0x33, 0x16, 0x57, 0xb3,
|
||||
0x1c, 0x4b, 0x13, 0x84, 0x37, 0x23, 0x2c, 0x10, 0x83, 0x09, 0xf1, 0xc6, 0x13, 0x11, 0xf1, 0x78,
|
||||
0x25, 0x92, 0x1e, 0x2a, 0x21, 0x7e, 0x1f, 0xca, 0x9c, 0xcd, 0xc8, 0x60, 0x79, 0x6d, 0xca, 0xaa,
|
||||
0x25, 0x92, 0x1e, 0x2a, 0x21, 0x7e, 0x1f, 0xca, 0x9c, 0xcd, 0xc8, 0x60, 0x79, 0x6d, 0xca, 0xa9,
|
||||
0x6b, 0x53, 0x49, 0x4a, 0x8f, 0x22, 0x63, 0xf1, 0x21, 0xfc, 0x60, 0x15, 0x35, 0x58, 0xd3, 0x82,
|
||||
0x7f, 0x17, 0xb6, 0xe0, 0xf7, 0xd2, 0x3b, 0x8f, 0x5e, 0x6f, 0xc7, 0x7d, 0xb8, 0x46, 0xe6, 0x82,
|
||||
0x50, 0x99, 0x23, 0x03, 0xa6, 0x3e, 0xe5, 0x72, 0xfd, 0xdf, 0xbb, 0xe7, 0xb8, 0x59, 0x49, 0xf0,
|
||||
@ -2921,7 +2921,7 @@ var fileDescriptor_fe4560133be9209a = []byte{
|
||||
0x3c, 0x7f, 0x55, 0xdb, 0xf9, 0xeb, 0xab, 0xda, 0xce, 0x67, 0xcd, 0xb1, 0x27, 0x26, 0xc1, 0xb0,
|
||||
0xe9, 0xb0, 0x59, 0x2b, 0xfa, 0xc8, 0x1f, 0xfe, 0xdd, 0xe6, 0xee, 0x71, 0x4b, 0x56, 0x7d, 0x20,
|
||||
0xbc, 0xa9, 0x1a, 0xb8, 0xb6, 0xb0, 0x87, 0x79, 0x45, 0x74, 0xe7, 0x3f, 0x01, 0x00, 0x00, 0xff,
|
||||
0xff, 0x3a, 0xea, 0x0d, 0xa7, 0x67, 0x18, 0x00, 0x00,
|
||||
0xff, 0x33, 0x3d, 0xcf, 0x3a, 0x67, 0x18, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Customer1) Marshal() (dAtA []byte, err error) {
|
||||
@ -5261,7 +5261,7 @@ func (m *TestUpdatedTxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
if m.SomeNewField != 0 {
|
||||
i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.SomeNewField))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.TimeoutHeight != 0 {
|
||||
i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.TimeoutHeight))
|
||||
@ -12602,7 +12602,7 @@ func (m *TestUpdatedTxBody) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field SomeNewField", wireType)
|
||||
}
|
||||
|
||||
@ -356,11 +356,26 @@ type TxBody struct {
|
||||
Messages []*types.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
// memo is any arbitrary note/comment to be added to the transaction.
|
||||
// WARNING: in clients, any publicly exposed text should not be called memo,
|
||||
// but should be called `note` instead (see https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
// but should be called `note` instead (see
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/9122).
|
||||
Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"`
|
||||
// timeout is the block height after which this transaction will not
|
||||
// be processed by the chain
|
||||
// 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
|
||||
// fashion. Specifically, the account's nonce will NOT be checked or
|
||||
// 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
|
||||
// valid.
|
||||
Unordered bool `protobuf:"varint,4,opt,name=unordered,proto3" json:"unordered,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
|
||||
@ -425,6 +440,13 @@ func (m *TxBody) GetTimeoutHeight() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *TxBody) GetUnordered() bool {
|
||||
if m != nil {
|
||||
return m.Unordered
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *TxBody) GetExtensionOptions() []*types.Any {
|
||||
if m != nil {
|
||||
return m.ExtensionOptions
|
||||
@ -789,13 +811,15 @@ type Fee struct {
|
||||
// gas_limit is the maximum gas that can be used in transaction processing
|
||||
// before an out of gas error occurs
|
||||
GasLimit uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"`
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees.
|
||||
// the payer must be a tx signer (and thus have signed this field in AuthInfo).
|
||||
// setting this field does *not* change the ordering of required signers for the transaction.
|
||||
// if unset, the first signer is responsible for paying the fees. If set, the
|
||||
// specified account must pay the fees. the payer must be a tx signer (and
|
||||
// thus have signed this field in AuthInfo). setting this field does *not*
|
||||
// change the ordering of required signers for the transaction.
|
||||
Payer string `protobuf:"bytes,3,opt,name=payer,proto3" json:"payer,omitempty"`
|
||||
// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used
|
||||
// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does
|
||||
// not support fee grants, this will fail
|
||||
// if set, the fee payer (either the first signer or the value of the payer
|
||||
// field) requests that a fee grant be used to pay fees instead of the fee
|
||||
// payer's own balance. If an appropriate fee grant does not exist or the
|
||||
// chain does not support fee grants, this will fail
|
||||
Granter string `protobuf:"bytes,4,opt,name=granter,proto3" json:"granter,omitempty"`
|
||||
}
|
||||
|
||||
@ -1020,74 +1044,75 @@ func init() {
|
||||
func init() { proto.RegisterFile("cosmos/tx/v1beta1/tx.proto", fileDescriptor_96d1575ffde80842) }
|
||||
|
||||
var fileDescriptor_96d1575ffde80842 = []byte{
|
||||
// 1059 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0x41, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xf6, 0x7a, 0x6d, 0xc7, 0x7e, 0x4d, 0xda, 0x64, 0x14, 0x21, 0xc7, 0x51, 0xdd, 0xe0, 0xaa,
|
||||
0x60, 0x55, 0x64, 0xb7, 0x4d, 0x0f, 0x94, 0x0a, 0x01, 0x76, 0x43, 0x94, 0xaa, 0x14, 0xa4, 0x4d,
|
||||
0x4e, 0xbd, 0xac, 0xc6, 0xeb, 0xc9, 0x7a, 0x54, 0xef, 0xcc, 0xb2, 0x33, 0x0b, 0xde, 0x23, 0x3f,
|
||||
0x00, 0xa9, 0x42, 0x42, 0x48, 0x9c, 0x39, 0x20, 0x4e, 0x3d, 0x20, 0x7e, 0x43, 0x4f, 0xa8, 0xe2,
|
||||
0xc4, 0x09, 0xaa, 0xe4, 0xd0, 0x3b, 0x7f, 0x00, 0x34, 0xb3, 0xb3, 0x9b, 0xb4, 0xa4, 0x4e, 0x11,
|
||||
0x48, 0x5c, 0xec, 0x99, 0xb7, 0xdf, 0x7b, 0xf3, 0xbd, 0x37, 0xdf, 0xbc, 0x07, 0x9d, 0x80, 0x8b,
|
||||
0x88, 0x0b, 0x57, 0xce, 0xdc, 0xcf, 0xae, 0x8f, 0x88, 0xc4, 0xd7, 0x5d, 0x39, 0x73, 0xe2, 0x84,
|
||||
0x4b, 0x8e, 0x56, 0xf2, 0x6f, 0x8e, 0x9c, 0x39, 0xe6, 0x5b, 0x67, 0x05, 0x47, 0x94, 0x71, 0x57,
|
||||
0xff, 0xe6, 0xa8, 0xce, 0x6a, 0xc8, 0x43, 0xae, 0x97, 0xae, 0x5a, 0x19, 0xeb, 0xa6, 0x89, 0x1b,
|
||||
0x24, 0x59, 0x2c, 0xb9, 0x1b, 0xa5, 0x53, 0x49, 0x05, 0x0d, 0xcb, 0x43, 0x0a, 0x83, 0x81, 0x77,
|
||||
0x0d, 0x7c, 0x84, 0x05, 0x29, 0x31, 0x01, 0xa7, 0xcc, 0x7c, 0x7f, 0xf3, 0x98, 0xa6, 0xa0, 0x21,
|
||||
0xa3, 0xec, 0x38, 0x92, 0xd9, 0x1b, 0xe0, 0x5a, 0xc8, 0x79, 0x38, 0x25, 0xae, 0xde, 0x8d, 0xd2,
|
||||
0x03, 0x17, 0xb3, 0xac, 0xf8, 0x94, 0xc7, 0xf0, 0x73, 0xae, 0x26, 0x37, 0xbd, 0xe9, 0x7d, 0x69,
|
||||
0x41, 0x75, 0x7f, 0x86, 0x36, 0xa1, 0x36, 0xe2, 0xe3, 0xac, 0x6d, 0x6d, 0x58, 0xfd, 0x73, 0x5b,
|
||||
0x6b, 0xce, 0xdf, 0xf2, 0x77, 0xf6, 0x67, 0x43, 0x3e, 0xce, 0x3c, 0x0d, 0x43, 0x37, 0xa1, 0x85,
|
||||
0x53, 0x39, 0xf1, 0x29, 0x3b, 0xe0, 0xed, 0xaa, 0xf6, 0x59, 0x3f, 0xc5, 0x67, 0x90, 0xca, 0xc9,
|
||||
0x1d, 0x76, 0xc0, 0xbd, 0x26, 0x36, 0x2b, 0xd4, 0x05, 0x50, 0xb4, 0xb1, 0x4c, 0x13, 0x22, 0xda,
|
||||
0xf6, 0x86, 0xdd, 0x5f, 0xf4, 0x4e, 0x58, 0x7a, 0x0c, 0xea, 0xfb, 0x33, 0x0f, 0x7f, 0x8e, 0x2e,
|
||||
0x02, 0xa8, 0xa3, 0xfc, 0x51, 0x26, 0x89, 0xd0, 0xbc, 0x16, 0xbd, 0x96, 0xb2, 0x0c, 0x95, 0x01,
|
||||
0xbd, 0x01, 0x17, 0x4a, 0x06, 0x06, 0x53, 0xd5, 0x98, 0xa5, 0xe2, 0xa8, 0x1c, 0x77, 0xd6, 0x79,
|
||||
0x5f, 0x59, 0xb0, 0xb0, 0x47, 0x43, 0xb6, 0xcd, 0x83, 0xff, 0xea, 0xc8, 0x35, 0x68, 0x06, 0x13,
|
||||
0x4c, 0x99, 0x4f, 0xc7, 0x6d, 0x7b, 0xc3, 0xea, 0xb7, 0xbc, 0x05, 0xbd, 0xbf, 0x33, 0x46, 0x57,
|
||||
0xe0, 0x3c, 0x0e, 0x02, 0x9e, 0x32, 0xe9, 0xb3, 0x34, 0x1a, 0x91, 0xa4, 0x5d, 0xdb, 0xb0, 0xfa,
|
||||
0x35, 0x6f, 0xc9, 0x58, 0x3f, 0xd6, 0xc6, 0xde, 0x1f, 0x16, 0x2c, 0x1b, 0x52, 0xdb, 0x34, 0x21,
|
||||
0x81, 0x1c, 0xa4, 0xb3, 0xb3, 0xd8, 0xdd, 0x00, 0x88, 0xd3, 0xd1, 0x94, 0x06, 0xfe, 0x03, 0x92,
|
||||
0x99, 0x3b, 0x59, 0x75, 0x72, 0x4d, 0x38, 0x85, 0x26, 0x9c, 0x01, 0xcb, 0xbc, 0x56, 0x8e, 0xbb,
|
||||
0x4b, 0xb2, 0x7f, 0x4f, 0x15, 0x75, 0xa0, 0x29, 0xc8, 0xa7, 0x29, 0x61, 0x01, 0x69, 0xd7, 0x35,
|
||||
0xa0, 0xdc, 0xa3, 0xb7, 0xc0, 0x96, 0x34, 0x6e, 0x37, 0x34, 0x97, 0xd7, 0x4e, 0xd3, 0x14, 0x8d,
|
||||
0x87, 0xd5, 0xb6, 0xe5, 0x29, 0x58, 0xef, 0xeb, 0x2a, 0x34, 0x72, 0x91, 0xa1, 0x6b, 0xd0, 0x8c,
|
||||
0x88, 0x10, 0x38, 0xd4, 0x89, 0xda, 0x2f, 0xcd, 0xa4, 0x44, 0x21, 0x04, 0xb5, 0x88, 0x44, 0xb9,
|
||||
0x16, 0x5b, 0x9e, 0x5e, 0xab, 0x0c, 0x24, 0x8d, 0x08, 0x4f, 0xa5, 0x3f, 0x21, 0x34, 0x9c, 0x48,
|
||||
0x9d, 0x62, 0xcd, 0x5b, 0x32, 0xd6, 0x5d, 0x6d, 0x44, 0x43, 0x58, 0x21, 0x33, 0x49, 0x98, 0xa0,
|
||||
0x9c, 0xf9, 0x3c, 0x96, 0x94, 0x33, 0xd1, 0xfe, 0x73, 0x61, 0xce, 0xb1, 0xcb, 0x25, 0xfe, 0x93,
|
||||
0x1c, 0x8e, 0xee, 0x43, 0x97, 0x71, 0xe6, 0x07, 0x09, 0x95, 0x34, 0xc0, 0x53, 0xff, 0x94, 0x80,
|
||||
0x17, 0xe6, 0x04, 0x5c, 0x67, 0x9c, 0xdd, 0x36, 0xbe, 0x1f, 0xbe, 0x10, 0xbb, 0xf7, 0x9d, 0x05,
|
||||
0xcd, 0xe2, 0x21, 0xa1, 0x0f, 0x60, 0x51, 0x89, 0x97, 0x24, 0x5a, 0x85, 0x45, 0x75, 0x2e, 0x9e,
|
||||
0x52, 0xdb, 0x3d, 0x0d, 0xd3, 0xaf, 0xef, 0x9c, 0x28, 0xd7, 0x02, 0xf5, 0xc1, 0x3e, 0x20, 0xc4,
|
||||
0x08, 0xe4, 0xb4, 0x4b, 0xd9, 0x21, 0xc4, 0x53, 0x90, 0xe2, 0xfa, 0xec, 0x57, 0xbb, 0xbe, 0x6f,
|
||||
0x2c, 0x80, 0xe3, 0x33, 0x5f, 0x90, 0xa3, 0xf5, 0x6a, 0x72, 0xbc, 0x09, 0xad, 0x88, 0x8f, 0xc9,
|
||||
0x59, 0x6d, 0xe5, 0x1e, 0x1f, 0x93, 0xbc, 0xad, 0x44, 0x66, 0xf5, 0x9c, 0x0c, 0xed, 0xe7, 0x65,
|
||||
0xd8, 0x7b, 0x5a, 0x85, 0x66, 0xe1, 0x82, 0xde, 0x85, 0x86, 0xa0, 0x2c, 0x9c, 0x12, 0xc3, 0xa9,
|
||||
0x37, 0x27, 0xbe, 0xb3, 0xa7, 0x91, 0xbb, 0x15, 0xcf, 0xf8, 0xa0, 0x77, 0xa0, 0xae, 0xdb, 0xb7,
|
||||
0x21, 0xf7, 0xfa, 0x3c, 0xe7, 0x7b, 0x0a, 0xb8, 0x5b, 0xf1, 0x72, 0x8f, 0xce, 0x00, 0x1a, 0x79,
|
||||
0x38, 0xf4, 0x36, 0xd4, 0x14, 0x6f, 0x4d, 0xe0, 0xfc, 0xd6, 0xe5, 0x13, 0x31, 0x8a, 0x86, 0x7e,
|
||||
0xf2, 0x0e, 0x55, 0x3c, 0x4f, 0x3b, 0x74, 0x1e, 0x5a, 0x50, 0xd7, 0x51, 0xd1, 0x5d, 0x68, 0x8e,
|
||||
0xa8, 0xc4, 0x49, 0x82, 0x8b, 0xda, 0xba, 0x45, 0x98, 0x7c, 0xec, 0x38, 0xe5, 0x94, 0x29, 0x62,
|
||||
0xdd, 0xe6, 0x51, 0x8c, 0x03, 0x39, 0xa4, 0x72, 0xa0, 0xdc, 0xbc, 0x32, 0x00, 0xba, 0x05, 0x50,
|
||||
0x56, 0x5d, 0xb5, 0x34, 0xfb, 0xac, 0xb2, 0xb7, 0x8a, 0xb2, 0x8b, 0x61, 0x1d, 0x6c, 0x91, 0x46,
|
||||
0xbd, 0x2f, 0xaa, 0x60, 0xef, 0x10, 0x82, 0x32, 0x68, 0xe0, 0x48, 0x75, 0x07, 0x23, 0xcc, 0x72,
|
||||
0x90, 0xa8, 0xe9, 0x76, 0x82, 0x0a, 0x65, 0xc3, 0x9d, 0xc7, 0xbf, 0x5d, 0xaa, 0xfc, 0xf0, 0xfb,
|
||||
0xa5, 0x7e, 0x48, 0xe5, 0x24, 0x1d, 0x39, 0x01, 0x8f, 0xdc, 0x62, 0x72, 0xea, 0xbf, 0x4d, 0x31,
|
||||
0x7e, 0xe0, 0xca, 0x2c, 0x26, 0x42, 0x3b, 0x88, 0x6f, 0x9f, 0x3d, 0xba, 0xba, 0x38, 0x25, 0x21,
|
||||
0x0e, 0x32, 0x5f, 0xcd, 0x47, 0xf1, 0xfd, 0xb3, 0x47, 0x57, 0x2d, 0xcf, 0x1c, 0x88, 0xd6, 0xa1,
|
||||
0x15, 0x62, 0xe1, 0x4f, 0x69, 0x44, 0xa5, 0xbe, 0x9e, 0x9a, 0xd7, 0x0c, 0xb1, 0xf8, 0x48, 0xed,
|
||||
0x91, 0x03, 0xf5, 0x18, 0x67, 0x24, 0xc9, 0x9b, 0xdc, 0xb0, 0xfd, 0xcb, 0x8f, 0x9b, 0xab, 0x86,
|
||||
0xd9, 0x60, 0x3c, 0x4e, 0x88, 0x10, 0x7b, 0x32, 0xa1, 0x2c, 0xf4, 0x72, 0x18, 0xda, 0x82, 0x85,
|
||||
0x30, 0xc1, 0x4c, 0x9a, 0xae, 0x37, 0xcf, 0xa3, 0x00, 0xf6, 0x7e, 0xb2, 0xc0, 0xde, 0xa7, 0xf1,
|
||||
0xff, 0x59, 0x83, 0x6b, 0xd0, 0x90, 0x34, 0x8e, 0x49, 0x92, 0xf7, 0xc1, 0x39, 0xac, 0x0d, 0xee,
|
||||
0x56, 0xb5, 0x6d, 0xf5, 0x7e, 0xb6, 0x60, 0x69, 0x90, 0xce, 0xf2, 0xc7, 0xbb, 0x8d, 0x25, 0x56,
|
||||
0xe9, 0xe3, 0x1c, 0xae, 0xd5, 0x35, 0x37, 0x7d, 0x03, 0x44, 0xef, 0x41, 0x53, 0xc9, 0xd7, 0x1f,
|
||||
0xf3, 0xc0, 0xbc, 0x8e, 0xcb, 0x2f, 0xe9, 0x4a, 0x27, 0xa7, 0x9a, 0xb7, 0x20, 0xcc, 0xf0, 0x2d,
|
||||
0x5e, 0x85, 0xfd, 0x0f, 0x5f, 0x05, 0x5a, 0x06, 0x5b, 0xd0, 0x50, 0xdf, 0xd3, 0xa2, 0xa7, 0x96,
|
||||
0xc3, 0xf7, 0x1f, 0x1f, 0x76, 0xad, 0x27, 0x87, 0x5d, 0xeb, 0xe9, 0x61, 0xd7, 0x7a, 0x78, 0xd4,
|
||||
0xad, 0x3c, 0x39, 0xea, 0x56, 0x7e, 0x3d, 0xea, 0x56, 0xee, 0x5f, 0x39, 0xbb, 0xd0, 0xae, 0x9c,
|
||||
0x8d, 0x1a, 0xba, 0x41, 0xdd, 0xf8, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x24, 0x52, 0x64, 0xe6, 0x23,
|
||||
0x0a, 0x00, 0x00,
|
||||
// 1076 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0x41, 0x6f, 0x1b, 0xc5,
|
||||
0x17, 0xf7, 0x7a, 0x6d, 0xc7, 0x7e, 0x4d, 0xda, 0x64, 0x14, 0xfd, 0xe5, 0x38, 0xff, 0xba, 0xc1,
|
||||
0x55, 0xc1, 0xaa, 0xc8, 0x6e, 0x9b, 0x1e, 0x28, 0x15, 0x02, 0xec, 0x86, 0x28, 0x55, 0x29, 0x48,
|
||||
0x93, 0x9c, 0x7a, 0x59, 0x8d, 0x77, 0x27, 0xeb, 0x51, 0xbd, 0x33, 0xcb, 0xce, 0x2c, 0x78, 0x8f,
|
||||
0x7c, 0x00, 0xa4, 0x8a, 0x0b, 0x12, 0x67, 0x0e, 0x88, 0x53, 0x25, 0x10, 0x9f, 0xa1, 0x27, 0x54,
|
||||
0x71, 0xe2, 0x04, 0x55, 0x72, 0xe8, 0x9d, 0x2f, 0x00, 0xda, 0xd9, 0x59, 0x27, 0x2d, 0xa9, 0x53,
|
||||
0x04, 0x12, 0x17, 0x7b, 0xe6, 0xed, 0xef, 0xbd, 0xf9, 0xbd, 0x37, 0xbf, 0x79, 0x0f, 0x3a, 0xbe,
|
||||
0x90, 0x91, 0x90, 0xae, 0x9a, 0xba, 0x9f, 0x5e, 0x1f, 0x51, 0x45, 0xae, 0xbb, 0x6a, 0xea, 0xc4,
|
||||
0x89, 0x50, 0x02, 0xad, 0x14, 0xdf, 0x1c, 0x35, 0x75, 0xcc, 0xb7, 0xce, 0x0a, 0x89, 0x18, 0x17,
|
||||
0xae, 0xfe, 0x2d, 0x50, 0x9d, 0xd5, 0x50, 0x84, 0x42, 0x2f, 0xdd, 0x7c, 0x65, 0xac, 0x9b, 0x26,
|
||||
0xae, 0x9f, 0x64, 0xb1, 0x12, 0x6e, 0x94, 0x4e, 0x14, 0x93, 0x2c, 0x9c, 0x1d, 0x52, 0x1a, 0x0c,
|
||||
0xbc, 0x6b, 0xe0, 0x23, 0x22, 0xe9, 0x0c, 0xe3, 0x0b, 0xc6, 0xcd, 0xf7, 0x37, 0x8e, 0x69, 0x4a,
|
||||
0x16, 0x72, 0xc6, 0x8f, 0x23, 0x99, 0xbd, 0x01, 0xae, 0x85, 0x42, 0x84, 0x13, 0xea, 0xea, 0xdd,
|
||||
0x28, 0x3d, 0x70, 0x09, 0xcf, 0xca, 0x4f, 0x45, 0x0c, 0xaf, 0xe0, 0x6a, 0x72, 0xd3, 0x9b, 0xde,
|
||||
0x17, 0x16, 0x54, 0xf7, 0xa7, 0x68, 0x13, 0x6a, 0x23, 0x11, 0x64, 0x6d, 0x6b, 0xc3, 0xea, 0x9f,
|
||||
0xdb, 0x5a, 0x73, 0xfe, 0x92, 0xbf, 0xb3, 0x3f, 0x1d, 0x8a, 0x20, 0xc3, 0x1a, 0x86, 0x6e, 0x42,
|
||||
0x8b, 0xa4, 0x6a, 0xec, 0x31, 0x7e, 0x20, 0xda, 0x55, 0xed, 0xb3, 0x7e, 0x8a, 0xcf, 0x20, 0x55,
|
||||
0xe3, 0x3b, 0xfc, 0x40, 0xe0, 0x26, 0x31, 0x2b, 0xd4, 0x05, 0xc8, 0x69, 0x13, 0x95, 0x26, 0x54,
|
||||
0xb6, 0xed, 0x0d, 0xbb, 0xbf, 0x88, 0x4f, 0x58, 0x7a, 0x1c, 0xea, 0xfb, 0x53, 0x4c, 0x3e, 0x43,
|
||||
0x17, 0x01, 0xf2, 0xa3, 0xbc, 0x51, 0xa6, 0xa8, 0xd4, 0xbc, 0x16, 0x71, 0x2b, 0xb7, 0x0c, 0x73,
|
||||
0x03, 0x7a, 0x1d, 0x2e, 0xcc, 0x18, 0x18, 0x4c, 0x55, 0x63, 0x96, 0xca, 0xa3, 0x0a, 0xdc, 0x59,
|
||||
0xe7, 0x7d, 0x69, 0xc1, 0xc2, 0x1e, 0x0b, 0xf9, 0xb6, 0xf0, 0xff, 0xad, 0x23, 0xd7, 0xa0, 0xe9,
|
||||
0x8f, 0x09, 0xe3, 0x1e, 0x0b, 0xda, 0xf6, 0x86, 0xd5, 0x6f, 0xe1, 0x05, 0xbd, 0xbf, 0x13, 0xa0,
|
||||
0x2b, 0x70, 0x9e, 0xf8, 0xbe, 0x48, 0xb9, 0xf2, 0x78, 0x1a, 0x8d, 0x68, 0xd2, 0xae, 0x6d, 0x58,
|
||||
0xfd, 0x1a, 0x5e, 0x32, 0xd6, 0x8f, 0xb4, 0xb1, 0xf7, 0xbb, 0x05, 0xcb, 0x86, 0xd4, 0x36, 0x4b,
|
||||
0xa8, 0xaf, 0x06, 0xe9, 0xf4, 0x2c, 0x76, 0x37, 0x00, 0xe2, 0x74, 0x34, 0x61, 0xbe, 0xf7, 0x80,
|
||||
0x66, 0xe6, 0x4e, 0x56, 0x9d, 0x42, 0x13, 0x4e, 0xa9, 0x09, 0x67, 0xc0, 0x33, 0xdc, 0x2a, 0x70,
|
||||
0x77, 0x69, 0xf6, 0xcf, 0xa9, 0xa2, 0x0e, 0x34, 0x25, 0xfd, 0x24, 0xa5, 0xdc, 0xa7, 0xed, 0xba,
|
||||
0x06, 0xcc, 0xf6, 0xe8, 0x4d, 0xb0, 0x15, 0x8b, 0xdb, 0x0d, 0xcd, 0xe5, 0x7f, 0xa7, 0x69, 0x8a,
|
||||
0xc5, 0xc3, 0x6a, 0xdb, 0xc2, 0x39, 0xac, 0xf7, 0x7d, 0x15, 0x1a, 0x85, 0xc8, 0xd0, 0x35, 0x68,
|
||||
0x46, 0x54, 0x4a, 0x12, 0xea, 0x44, 0xed, 0x97, 0x66, 0x32, 0x43, 0x21, 0x04, 0xb5, 0x88, 0x46,
|
||||
0x85, 0x16, 0x5b, 0x58, 0xaf, 0xf3, 0x0c, 0x14, 0x8b, 0xa8, 0x48, 0x95, 0x37, 0xa6, 0x2c, 0x1c,
|
||||
0x2b, 0x9d, 0x62, 0x0d, 0x2f, 0x19, 0xeb, 0xae, 0x36, 0xa2, 0xff, 0x43, 0x2b, 0xe5, 0x22, 0x09,
|
||||
0x68, 0x42, 0x03, 0x9d, 0x63, 0x13, 0x1f, 0x1b, 0xd0, 0x10, 0x56, 0xe8, 0x54, 0x51, 0x2e, 0x99,
|
||||
0xe0, 0x9e, 0x88, 0x15, 0x13, 0x5c, 0xb6, 0xff, 0x58, 0x98, 0x43, 0x6a, 0x79, 0x86, 0xff, 0xb8,
|
||||
0x80, 0xa3, 0xfb, 0xd0, 0xe5, 0x82, 0x7b, 0x7e, 0xc2, 0x14, 0xf3, 0xc9, 0xc4, 0x3b, 0x25, 0xe0,
|
||||
0x85, 0x39, 0x01, 0xd7, 0xb9, 0xe0, 0xb7, 0x8d, 0xef, 0x07, 0x2f, 0xc4, 0xee, 0x7d, 0x63, 0x41,
|
||||
0xb3, 0x7c, 0x66, 0xe8, 0x7d, 0x58, 0xcc, 0xa5, 0x4d, 0x13, 0xad, 0xd1, 0xb2, 0x76, 0x17, 0x4f,
|
||||
0xa9, 0xfc, 0x9e, 0x86, 0xe9, 0xb7, 0x79, 0x4e, 0xce, 0xd6, 0x12, 0xf5, 0xc1, 0x3e, 0xa0, 0xd4,
|
||||
0xc8, 0xe7, 0xb4, 0x2b, 0xdb, 0xa1, 0x14, 0xe7, 0x90, 0xf2, 0x72, 0xed, 0x57, 0xbb, 0xdc, 0xaf,
|
||||
0x2c, 0x80, 0xe3, 0x33, 0x5f, 0x10, 0xab, 0xf5, 0x6a, 0x62, 0xbd, 0x09, 0xad, 0x48, 0x04, 0xf4,
|
||||
0xac, 0xa6, 0x73, 0x4f, 0x04, 0xb4, 0x68, 0x3a, 0x91, 0x59, 0x3d, 0x27, 0x52, 0xfb, 0x79, 0x91,
|
||||
0xf6, 0x9e, 0x56, 0xa1, 0x59, 0xba, 0xa0, 0x77, 0xa0, 0x21, 0x19, 0x0f, 0x27, 0xd4, 0x70, 0xea,
|
||||
0xcd, 0x89, 0xef, 0xec, 0x69, 0xe4, 0x6e, 0x05, 0x1b, 0x1f, 0xf4, 0x36, 0xd4, 0x75, 0x73, 0x37,
|
||||
0xe4, 0x5e, 0x9b, 0xe7, 0x7c, 0x2f, 0x07, 0xee, 0x56, 0x70, 0xe1, 0xd1, 0x19, 0x40, 0xa3, 0x08,
|
||||
0x87, 0xde, 0x82, 0x5a, 0xce, 0x5b, 0x13, 0x38, 0xbf, 0x75, 0xf9, 0x44, 0x8c, 0xb2, 0xdd, 0x9f,
|
||||
0xbc, 0xc3, 0x3c, 0x1e, 0xd6, 0x0e, 0x9d, 0x87, 0x16, 0xd4, 0x75, 0x54, 0x74, 0x17, 0x9a, 0x23,
|
||||
0xa6, 0x48, 0x92, 0x90, 0xb2, 0xb6, 0x6e, 0x19, 0xa6, 0x18, 0x4a, 0xce, 0x6c, 0x06, 0x95, 0xb1,
|
||||
0x6e, 0x8b, 0x28, 0x26, 0xbe, 0x1a, 0x32, 0x35, 0xc8, 0xdd, 0xf0, 0x2c, 0x00, 0xba, 0x05, 0x30,
|
||||
0xab, 0x7a, 0xde, 0xf0, 0xec, 0xb3, 0xca, 0xde, 0x2a, 0xcb, 0x2e, 0x87, 0x75, 0xb0, 0x65, 0x1a,
|
||||
0xf5, 0x3e, 0xaf, 0x82, 0xbd, 0x43, 0x29, 0xca, 0xa0, 0x41, 0xa2, 0xbc, 0x77, 0x18, 0x61, 0xce,
|
||||
0xc6, 0x4c, 0x3e, 0xfb, 0x4e, 0x50, 0x61, 0x7c, 0xb8, 0xf3, 0xf8, 0xd7, 0x4b, 0x95, 0xef, 0x7e,
|
||||
0xbb, 0xd4, 0x0f, 0x99, 0x1a, 0xa7, 0x23, 0xc7, 0x17, 0x91, 0x5b, 0xce, 0x55, 0xfd, 0xb7, 0x29,
|
||||
0x83, 0x07, 0xae, 0xca, 0x62, 0x2a, 0xb5, 0x83, 0xfc, 0xfa, 0xd9, 0xa3, 0xab, 0x8b, 0x13, 0x1a,
|
||||
0x12, 0x3f, 0xf3, 0xf2, 0xe9, 0x29, 0xbf, 0x7d, 0xf6, 0xe8, 0xaa, 0x85, 0xcd, 0x81, 0x68, 0x1d,
|
||||
0x5a, 0x21, 0x91, 0xde, 0x84, 0x45, 0x4c, 0xe9, 0xeb, 0xa9, 0xe1, 0x66, 0x48, 0xe4, 0x87, 0xf9,
|
||||
0x1e, 0x39, 0x50, 0x8f, 0x49, 0x46, 0x93, 0xa2, 0x05, 0x0e, 0xdb, 0x3f, 0xff, 0xb0, 0xb9, 0x6a,
|
||||
0x98, 0x0d, 0x82, 0x20, 0xa1, 0x52, 0xee, 0xa9, 0x84, 0xf1, 0x10, 0x17, 0x30, 0xb4, 0x05, 0x0b,
|
||||
0x61, 0x42, 0xb8, 0x32, 0x3d, 0x71, 0x9e, 0x47, 0x09, 0xec, 0xfd, 0x68, 0x81, 0xbd, 0xcf, 0xe2,
|
||||
0xff, 0xb2, 0x06, 0xd7, 0xa0, 0xa1, 0x58, 0x1c, 0xd3, 0xa4, 0xe8, 0x92, 0x73, 0x58, 0x1b, 0xdc,
|
||||
0xad, 0x6a, 0xdb, 0xea, 0xfd, 0x64, 0xc1, 0xd2, 0x20, 0x9d, 0x16, 0x8f, 0x77, 0x9b, 0x28, 0x92,
|
||||
0xa7, 0x4f, 0x0a, 0xb8, 0x56, 0xd7, 0xdc, 0xf4, 0x0d, 0x10, 0xbd, 0x0b, 0xcd, 0x5c, 0xbe, 0x5e,
|
||||
0x20, 0x7c, 0xf3, 0x3a, 0x2e, 0xbf, 0xa4, 0x2b, 0x9d, 0x9c, 0x79, 0x78, 0x41, 0x9a, 0xd1, 0x5c,
|
||||
0xbe, 0x0a, 0xfb, 0x6f, 0xbe, 0x0a, 0xb4, 0x0c, 0xb6, 0x64, 0xa1, 0xbe, 0xa7, 0x45, 0x9c, 0x2f,
|
||||
0x87, 0xef, 0x3d, 0x3e, 0xec, 0x5a, 0x4f, 0x0e, 0xbb, 0xd6, 0xd3, 0xc3, 0xae, 0xf5, 0xf0, 0xa8,
|
||||
0x5b, 0x79, 0x72, 0xd4, 0xad, 0xfc, 0x72, 0xd4, 0xad, 0xdc, 0xbf, 0x72, 0x76, 0xa1, 0x5d, 0x35,
|
||||
0x1d, 0x35, 0x74, 0x83, 0xba, 0xf1, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x48, 0xf7, 0x44,
|
||||
0x41, 0x0a, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Tx) Marshal() (dAtA []byte, err error) {
|
||||
@ -1364,6 +1389,16 @@ func (m *TxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
dAtA[i] = 0xfa
|
||||
}
|
||||
}
|
||||
if m.Unordered {
|
||||
i--
|
||||
if m.Unordered {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.TimeoutHeight != 0 {
|
||||
i = encodeVarintTx(dAtA, i, uint64(m.TimeoutHeight))
|
||||
i--
|
||||
@ -1942,6 +1977,9 @@ func (m *TxBody) Size() (n int) {
|
||||
if m.TimeoutHeight != 0 {
|
||||
n += 1 + sovTx(uint64(m.TimeoutHeight))
|
||||
}
|
||||
if m.Unordered {
|
||||
n += 2
|
||||
}
|
||||
if len(m.ExtensionOptions) > 0 {
|
||||
for _, e := range m.ExtensionOptions {
|
||||
l = e.Size()
|
||||
@ -2955,6 +2993,26 @@ func (m *TxBody) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Unordered", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTx
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Unordered = bool(v != 0)
|
||||
case 1023:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType)
|
||||
|
||||
@ -79,6 +79,14 @@ type (
|
||||
GetTimeoutHeight() uint64
|
||||
}
|
||||
|
||||
// TxWithUnordered extends the Tx interface by allowing a transaction to set
|
||||
// the unordered field, which implicitly relies on TxWithTimeoutHeight.
|
||||
TxWithUnordered interface {
|
||||
TxWithTimeoutHeight
|
||||
|
||||
GetUnordered() bool
|
||||
}
|
||||
|
||||
// HasValidateBasic defines a type that has a ValidateBasic method.
|
||||
// ValidateBasic is deprecated and now facultative.
|
||||
// Prefer validating messages directly in the msg server.
|
||||
|
||||
@ -146,9 +146,14 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// SigVerificationDecorator verifies all signatures for a tx and return an error if any are invalid. Note,
|
||||
// the SigVerificationDecorator will not check signatures on ReCheck.
|
||||
// It will also increase the sequence number, and consume gas for signature verification.
|
||||
// SigVerificationDecorator verifies all signatures for a tx and returns an
|
||||
// error if any are invalid. Note, the SigVerificationDecorator will not check
|
||||
// signatures on ReCheckTx. It will also increase the sequence number, and consume
|
||||
// gas for signature verification.
|
||||
//
|
||||
// In cases where unordered or parallel transactions are desired, it is recommended
|
||||
// to to set unordered=true with a reasonable timeout_height value, in which case
|
||||
// this nonce verification and increment will be skipped.
|
||||
//
|
||||
// CONTRACT: Pubkeys are set in context for all signers before this decorator runs
|
||||
// CONTRACT: Tx must implement SigVerifiableTx interface
|
||||
@ -277,11 +282,15 @@ func (svd SigVerificationDecorator) authenticate(ctx sdk.Context, tx sdk.Tx, sim
|
||||
return err
|
||||
}
|
||||
|
||||
err = svd.increaseSequence(ctx, acc)
|
||||
if err != nil {
|
||||
return err
|
||||
// Bypass incrementing sequence for transactions with unordered set to true.
|
||||
// The actual parameters of the un-ordered tx will be checked in a separate
|
||||
// decorator.
|
||||
unorderedTx, ok := tx.(sdk.TxWithUnordered)
|
||||
if ok && unorderedTx.GetUnordered() {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return svd.increaseSequence(ctx, acc)
|
||||
}
|
||||
|
||||
// consumeSignatureGas will consume gas according to the pub-key being verified.
|
||||
@ -419,8 +428,7 @@ func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim
|
||||
for _, pk := range pubKeys {
|
||||
sigCount += CountSubKeys(pk)
|
||||
if uint64(sigCount) > params.TxSigLimit {
|
||||
return ctx, errorsmod.Wrapf(sdkerrors.ErrTooManySignatures,
|
||||
"signatures: %d, limit: %d", sigCount, params.TxSigLimit)
|
||||
return ctx, errorsmod.Wrapf(sdkerrors.ErrTooManySignatures, "signatures: %d, limit: %d", sigCount, params.TxSigLimit)
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,10 +438,9 @@ func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim
|
||||
// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas
|
||||
// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
|
||||
// by the concrete type.
|
||||
func DefaultSigVerificationGasConsumer(
|
||||
meter storetypes.GasMeter, sig signing.SignatureV2, params types.Params,
|
||||
) error {
|
||||
func DefaultSigVerificationGasConsumer(meter storetypes.GasMeter, sig signing.SignatureV2, params types.Params) error {
|
||||
pubkey := sig.PubKey
|
||||
|
||||
switch pubkey := pubkey.(type) {
|
||||
case *ed25519.PubKey:
|
||||
meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
|
||||
@ -452,10 +459,12 @@ func DefaultSigVerificationGasConsumer(
|
||||
if !ok {
|
||||
return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
|
||||
}
|
||||
|
||||
err := ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
@ -480,10 +489,12 @@ func ConsumeMultisignatureVerificationGas(
|
||||
Data: sig.Signatures[sigIndex],
|
||||
Sequence: accSeq,
|
||||
}
|
||||
|
||||
err := DefaultSigVerificationGasConsumer(meter, sigV2, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigIndex++
|
||||
}
|
||||
|
||||
@ -507,6 +518,7 @@ func CountSubKeys(pub cryptotypes.PubKey) int {
|
||||
if pub == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
v, ok := pub.(*kmultisig.LegacyAminoPubKey)
|
||||
if !ok {
|
||||
return 1
|
||||
@ -532,6 +544,7 @@ func signatureDataToBz(data signing.SignatureData) ([][]byte, error) {
|
||||
switch data := data.(type) {
|
||||
case *signing.SingleSignatureData:
|
||||
return [][]byte{data.Signature}, nil
|
||||
|
||||
case *signing.MultiSignatureData:
|
||||
sigs := [][]byte{}
|
||||
var err error
|
||||
@ -541,19 +554,22 @@ func signatureDataToBz(data signing.SignatureData) ([][]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigs = append(sigs, nestedSigs...)
|
||||
}
|
||||
|
||||
multiSignature := cryptotypes.MultiSignature{
|
||||
Signatures: sigs,
|
||||
}
|
||||
|
||||
aggregatedSig, err := multiSignature.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sigs = append(sigs, aggregatedSig)
|
||||
|
||||
sigs = append(sigs, aggregatedSig)
|
||||
return sigs, nil
|
||||
|
||||
default:
|
||||
return nil, sdkerrors.ErrInvalidType.Wrapf("unexpected signature data type %T", data)
|
||||
}
|
||||
|
||||
74
x/auth/ante/unordered.go
Normal file
74
x/auth/ante/unordered.go
Normal file
@ -0,0 +1,74 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
var _ sdk.AnteDecorator = (*UnorderedTxDecorator)(nil)
|
||||
|
||||
// UnorderedTxDecorator defines an AnteHandler decorator that is responsible for
|
||||
// checking if a transaction is intended to be unordered and if so, evaluates
|
||||
// the transaction accordingly. An unordered transaction will bypass having it's
|
||||
// nonce incremented, which allows fire-and-forget along with possible parallel
|
||||
// transaction processing, without having to deal with nonces.
|
||||
//
|
||||
// The transaction sender must ensure that unordered=true and a timeout_height
|
||||
// is appropriately set. The AnteHandler will check that the transaction is not
|
||||
// a duplicate and will evict it from memory when the timeout is reached.
|
||||
//
|
||||
// The UnorderedTxDecorator should be placed as early as possible in the AnteHandler
|
||||
// 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
|
||||
}
|
||||
|
||||
func NewUnorderedTxDecorator(maxTTL uint64, m *unorderedtx.Manager) *UnorderedTxDecorator {
|
||||
return &UnorderedTxDecorator{
|
||||
maxUnOrderedTTL: maxTTL,
|
||||
txManager: m,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *UnorderedTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
|
||||
unorderedTx, ok := tx.(sdk.TxWithUnordered)
|
||||
if !ok || !unorderedTx.GetUnordered() {
|
||||
// If the transaction does not implement unordered capabilities or has the
|
||||
// unordered value as false, we bypass.
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
if ttl < uint64(ctx.BlockHeight()) {
|
||||
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unordered transaction has a timeout_height that has already passed")
|
||||
}
|
||||
if ttl > uint64(ctx.BlockHeight())+d.maxUnOrderedTTL {
|
||||
return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "unordered tx ttl exceeds %d", d.maxUnOrderedTTL)
|
||||
}
|
||||
|
||||
txHash := sha256.Sum256(ctx.TxBytes())
|
||||
|
||||
// check for duplicates
|
||||
if d.txManager.Contains(txHash) {
|
||||
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "tx %X is duplicated")
|
||||
}
|
||||
|
||||
if ctx.ExecMode() == sdk.ExecModeFinalize {
|
||||
// a new tx included in the block, add the hash to the unordered tx manager
|
||||
d.txManager.Add(txHash, ttl)
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
154
x/auth/ante/unordered_test.go
Normal file
154
x/auth/ante/unordered_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/auth/ante"
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
)
|
||||
|
||||
func TestUnorderedTxDecorator_OrderedTx(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, false, 0)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUnorderedTxDecorator_UnorderedTx_NoTTL(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, true, 0)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestUnorderedTxDecorator_UnorderedTx_InvalidTTL(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, true, 100+unorderedtx.DefaultMaxUnOrderedTTL+1)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestUnorderedTxDecorator_UnorderedTx_AlreadyExists(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, true, 150)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100)
|
||||
|
||||
txHash := sha256.Sum256(txBz)
|
||||
txm.Add(txHash, 150)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestUnorderedTxDecorator_UnorderedTx_ValidCheckTx(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, true, 150)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100).WithExecMode(sdk.ExecModeCheck)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUnorderedTxDecorator_UnorderedTx_ValidDeliverTx(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
chain := sdk.ChainAnteDecorators(ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxUnOrderedTTL, txm))
|
||||
|
||||
tx, txBz := genUnorderedTx(t, true, 150)
|
||||
ctx := sdk.Context{}.WithTxBytes(txBz).WithBlockHeight(100).WithExecMode(sdk.ExecModeFinalize)
|
||||
|
||||
_, err := chain(ctx, tx, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
txHash := sha256.Sum256(txBz)
|
||||
require.True(t, txm.Contains(txHash))
|
||||
}
|
||||
|
||||
func genUnorderedTx(t *testing.T, unordered bool, ttl uint64) (sdk.Tx, []byte) {
|
||||
t.Helper()
|
||||
|
||||
s := SetupTestSuite(t, true)
|
||||
s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder()
|
||||
|
||||
// keys and addresses
|
||||
priv1, _, addr1 := testdata.KeyTestPubAddr()
|
||||
|
||||
// msg and signatures
|
||||
msg := testdata.NewTestMsg(addr1)
|
||||
feeAmount := testdata.NewTestFeeAmount()
|
||||
gasLimit := testdata.NewTestGasLimit()
|
||||
require.NoError(t, s.txBuilder.SetMsgs(msg))
|
||||
|
||||
s.txBuilder.SetFeeAmount(feeAmount)
|
||||
s.txBuilder.SetGasLimit(gasLimit)
|
||||
s.txBuilder.SetUnordered(unordered)
|
||||
s.txBuilder.SetTimeoutHeight(ttl)
|
||||
|
||||
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 := s.encCfg.TxConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
return tx, txBz
|
||||
}
|
||||
285
x/auth/ante/unorderedtx/manager.go
Normal file
285
x/auth/ante/unorderedtx/manager.go
Normal file
@ -0,0 +1,285 @@
|
||||
package unorderedtx
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMaxUnOrderedTTL defines the default maximum TTL an un-ordered transaction
|
||||
// can set.
|
||||
DefaultMaxUnOrderedTTL = 1024
|
||||
|
||||
dirName = "unordered_txs"
|
||||
fileName = "data"
|
||||
)
|
||||
|
||||
// TxHash defines a transaction hash type alias, which is a fixed array of 32 bytes.
|
||||
type TxHash [32]byte
|
||||
|
||||
// Manager contains the tx hash dictionary for duplicates checking, and expire
|
||||
// them when block production progresses.
|
||||
type Manager struct {
|
||||
// blockCh defines a channel to receive newly committed block heights
|
||||
blockCh chan uint64
|
||||
// doneCh allows us to ensure the purgeLoop has gracefully terminated prior to closing
|
||||
doneCh chan struct{}
|
||||
|
||||
// dataDir defines the directory to store unexpired unordered transactions
|
||||
//
|
||||
// XXX: Note, ideally we avoid the need to store unexpired unordered transactions
|
||||
// directly to file. However, store v1 does not allow such a primitive. But,
|
||||
// once store v2 is fully integrated, we can remove manual file handling and
|
||||
// store the unexpired unordered transactions directly to SS.
|
||||
//
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/18467
|
||||
dataDir string
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func NewManager(dataDir string) *Manager {
|
||||
path := filepath.Join(dataDir, dirName)
|
||||
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||
_ = os.Mkdir(path, os.ModePerm)
|
||||
}
|
||||
|
||||
m := &Manager{
|
||||
dataDir: dataDir,
|
||||
blockCh: make(chan uint64, 16),
|
||||
doneCh: make(chan struct{}),
|
||||
txHashes: make(map[TxHash]uint64),
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manager) Start() {
|
||||
go m.purgeLoop()
|
||||
}
|
||||
|
||||
// Close must be called when a node gracefully shuts down. Typically, this should
|
||||
// be called in an application's Close() function, which is called by the server.
|
||||
// Note, Start() must be called in order for Close() to not hang.
|
||||
//
|
||||
// It will free all necessary resources as well as writing all unexpired unordered
|
||||
// transactions along with their TTL values to file.
|
||||
func (m *Manager) Close() error {
|
||||
close(m.blockCh)
|
||||
<-m.doneCh
|
||||
m.blockCh = nil
|
||||
|
||||
return m.flushToFile()
|
||||
}
|
||||
|
||||
func (m *Manager) Contains(hash TxHash) bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
_, ok := m.txHashes[hash]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *Manager) Size() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
return len(m.txHashes)
|
||||
}
|
||||
|
||||
func (m *Manager) Add(txHash TxHash, ttl uint64) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.txHashes[txHash] = ttl
|
||||
}
|
||||
|
||||
// OnInit must be called when a node starts up. Typically, this should be called
|
||||
// in an application's constructor, which is called by the server.
|
||||
func (m *Manager) OnInit() error {
|
||||
f, err := os.Open(filepath.Join(m.dataDir, dirName, fileName))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// File does not exist, which we can assume that there are no unexpired
|
||||
// unordered transactions.
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to open unconfirmed txs file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
r = bufio.NewReader(f)
|
||||
buf = make([]byte, chunkSize)
|
||||
)
|
||||
for {
|
||||
n, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else {
|
||||
return fmt.Errorf("failed to read unconfirmed txs file: %w", err)
|
||||
}
|
||||
}
|
||||
if n != 32+8 {
|
||||
return fmt.Errorf("read unexpected number of bytes from unconfirmed txs file: %d", n)
|
||||
}
|
||||
|
||||
var txHash TxHash
|
||||
copy(txHash[:], buf[:txHashSize])
|
||||
|
||||
m.Add(txHash, binary.BigEndian.Uint64(buf[txHashSize:]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) exportSnapshot(height uint64, snapshotWriter func([]byte) error) error {
|
||||
var buf bytes.Buffer
|
||||
w := bufio.NewWriter(&buf)
|
||||
|
||||
keys := maps.Keys(m.txHashes)
|
||||
sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i][:], keys[j][:]) < 0 })
|
||||
|
||||
for _, txHash := range keys {
|
||||
ttl := m.txHashes[txHash]
|
||||
if height > ttl {
|
||||
// skip expired txs that have yet to be purged
|
||||
continue
|
||||
}
|
||||
|
||||
chunk := unorderedTxToBytes(txHash, ttl)
|
||||
|
||||
if _, err := w.Write(chunk); err != nil {
|
||||
return fmt.Errorf("failed to write unordered tx to buffer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush unordered txs buffer: %w", err)
|
||||
}
|
||||
|
||||
return snapshotWriter(buf.Bytes())
|
||||
}
|
||||
|
||||
// flushToFile writes all unexpired unordered transactions along with their TTL
|
||||
// to file, overwriting the existing file if it exists.
|
||||
func (m *Manager) flushToFile() error {
|
||||
f, err := os.Create(filepath.Join(m.dataDir, dirName, fileName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create unordered txs file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
for txHash, ttl := range m.txHashes {
|
||||
chunk := unorderedTxToBytes(txHash, ttl)
|
||||
|
||||
if _, err = w.Write(chunk); err != nil {
|
||||
return fmt.Errorf("failed to write unordered tx to buffer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush unordered txs buffer: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// expiredTxs returns expired tx hashes based on the provided block height.
|
||||
func (m *Manager) expiredTxs(blockHeight uint64) []TxHash {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
var result []TxHash
|
||||
for txHash, ttl := range m.txHashes {
|
||||
if blockHeight > ttl {
|
||||
result = append(result, txHash)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Manager) purge(txHashes []TxHash) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, txHash := range txHashes {
|
||||
delete(m.txHashes, txHash)
|
||||
}
|
||||
}
|
||||
|
||||
// purgeLoop removes expired tx hashes in the background
|
||||
func (m *Manager) purgeLoop() {
|
||||
for {
|
||||
latestHeight, ok := m.batchReceive()
|
||||
if !ok {
|
||||
// channel closed
|
||||
m.doneCh <- struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
hashes := m.expiredTxs(latestHeight)
|
||||
if len(hashes) > 0 {
|
||||
m.purge(hashes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) batchReceive() (uint64, bool) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var latestHeight uint64
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return latestHeight, true
|
||||
|
||||
case blockHeight, ok := <-m.blockCh:
|
||||
if !ok {
|
||||
// channel is closed
|
||||
return 0, false
|
||||
}
|
||||
if blockHeight > latestHeight {
|
||||
latestHeight = blockHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unorderedTxToBytes(txHash TxHash, ttl uint64) []byte {
|
||||
chunk := make([]byte, chunkSize)
|
||||
copy(chunk[:txHashSize], txHash[:])
|
||||
|
||||
ttlBz := make([]byte, ttlSize)
|
||||
binary.BigEndian.PutUint64(ttlBz, ttl)
|
||||
copy(chunk[txHashSize:], ttlBz)
|
||||
|
||||
return chunk
|
||||
}
|
||||
150
x/auth/ante/unorderedtx/manager_test.go
Normal file
150
x/auth/ante/unorderedtx/manager_test.go
Normal file
@ -0,0 +1,150 @@
|
||||
package unorderedtx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
)
|
||||
|
||||
func TestUnorderedTxManager_Close(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
txm.Start()
|
||||
|
||||
require.NoError(t, txm.Close())
|
||||
require.Panics(t, func() { txm.Close() })
|
||||
}
|
||||
|
||||
func TestUnorderedTxManager_SimpleSize(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
txm.Add([32]byte{0xFF}, 100)
|
||||
txm.Add([32]byte{0xAA}, 100)
|
||||
txm.Add([32]byte{0xCC}, 100)
|
||||
|
||||
require.Equal(t, 3, txm.Size())
|
||||
}
|
||||
|
||||
func TestUnorderedTxManager_SimpleContains(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
txHash := [32]byte{byte(i)}
|
||||
txm.Add(txHash, 100)
|
||||
require.True(t, txm.Contains(txHash))
|
||||
}
|
||||
|
||||
for i := 10; i < 20; i++ {
|
||||
txHash := [32]byte{byte(i)}
|
||||
require.False(t, txm.Contains(txHash))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnorderedTxManager_InitEmpty(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
require.NoError(t, txm.OnInit())
|
||||
}
|
||||
|
||||
func TestUnorderedTxManager_CloseInit(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
txm := unorderedtx.NewManager(dataDir)
|
||||
txm.Start()
|
||||
|
||||
// add a handful of unordered txs
|
||||
for i := 0; i < 100; i++ {
|
||||
txm.Add([32]byte{byte(i)}, 100)
|
||||
}
|
||||
|
||||
// close the manager, which should flush all unexpired txs to file
|
||||
require.NoError(t, txm.Close())
|
||||
|
||||
// create a new manager, start it
|
||||
txm2 := unorderedtx.NewManager(dataDir)
|
||||
defer func() {
|
||||
require.NoError(t, txm2.Close())
|
||||
}()
|
||||
|
||||
// start and execute OnInit, which should load the unexpired txs from file
|
||||
txm2.Start()
|
||||
require.NoError(t, txm2.OnInit())
|
||||
require.Equal(t, 100, txm2.Size())
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
require.True(t, txm2.Contains([32]byte{byte(i)}))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnorderedTxManager_Flow(t *testing.T) {
|
||||
txm := unorderedtx.NewManager(t.TempDir())
|
||||
defer func() {
|
||||
require.NoError(t, txm.Close())
|
||||
}()
|
||||
|
||||
txm.Start()
|
||||
|
||||
// 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))
|
||||
} else {
|
||||
txm.Add(txHash, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// start a goroutine that mimics new blocks being made every 500ms
|
||||
doneBlockCh := make(chan bool)
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Millisecond * 500)
|
||||
defer ticker.Stop()
|
||||
|
||||
var (
|
||||
height uint64 = 1
|
||||
i = 101
|
||||
)
|
||||
for range ticker.C {
|
||||
txm.OnNewBlock(height)
|
||||
height++
|
||||
|
||||
if height > 51 {
|
||||
doneBlockCh <- true
|
||||
return
|
||||
} else {
|
||||
txm.Add([32]byte{byte(i)}, 50)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Eventually all the txs that should be expired by block 50 should be purged.
|
||||
// The remaining txs should remain.
|
||||
require.Eventually(
|
||||
t,
|
||||
func() bool {
|
||||
return txm.Size() == 50
|
||||
},
|
||||
2*time.Minute,
|
||||
5*time.Second,
|
||||
)
|
||||
|
||||
<-doneBlockCh
|
||||
}
|
||||
92
x/auth/ante/unorderedtx/snapshotter.go
Normal file
92
x/auth/ante/unorderedtx/snapshotter.go
Normal file
@ -0,0 +1,92 @@
|
||||
package unorderedtx
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
snapshot "cosmossdk.io/store/snapshots/types"
|
||||
)
|
||||
|
||||
const (
|
||||
txHashSize = 32
|
||||
ttlSize = 8
|
||||
chunkSize = txHashSize + ttlSize
|
||||
)
|
||||
|
||||
var _ snapshot.ExtensionSnapshotter = &Snapshotter{}
|
||||
|
||||
const (
|
||||
// SnapshotFormat defines the snapshot format of exported unordered transactions.
|
||||
// No protobuf envelope, no metadata.
|
||||
SnapshotFormat = 1
|
||||
|
||||
// SnapshotName defines the snapshot name of exported unordered transactions.
|
||||
SnapshotName = "unordered_txs"
|
||||
)
|
||||
|
||||
type Snapshotter struct {
|
||||
m *Manager
|
||||
}
|
||||
|
||||
func NewSnapshotter(m *Manager) *Snapshotter {
|
||||
return &Snapshotter{m: m}
|
||||
}
|
||||
|
||||
func (s *Snapshotter) SnapshotName() string {
|
||||
return SnapshotName
|
||||
}
|
||||
|
||||
func (s *Snapshotter) SnapshotFormat() uint32 {
|
||||
return SnapshotFormat
|
||||
}
|
||||
|
||||
func (s *Snapshotter) SupportedFormats() []uint32 {
|
||||
return []uint32{SnapshotFormat}
|
||||
}
|
||||
|
||||
func (s *Snapshotter) SnapshotExtension(height uint64, payloadWriter snapshot.ExtensionPayloadWriter) error {
|
||||
// export all unordered transactions as a single blob
|
||||
return s.m.exportSnapshot(height, payloadWriter)
|
||||
}
|
||||
|
||||
func (s *Snapshotter) RestoreExtension(height uint64, format uint32, payloadReader snapshot.ExtensionPayloadReader) error {
|
||||
if format == SnapshotFormat {
|
||||
return s.restore(height, payloadReader)
|
||||
}
|
||||
|
||||
return snapshot.ErrUnknownFormat
|
||||
}
|
||||
|
||||
func (s *Snapshotter) restore(height uint64, payloadReader snapshot.ExtensionPayloadReader) error {
|
||||
// the payload should be the entire set of unordered transactions
|
||||
payload, err := payloadReader()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if len(payload)%chunkSize != 0 {
|
||||
return errors.New("invalid unordered txs payload length")
|
||||
}
|
||||
|
||||
var i int
|
||||
for i < len(payload) {
|
||||
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)
|
||||
}
|
||||
|
||||
i += chunkSize
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
56
x/auth/ante/unorderedtx/snapshotter_test.go
Normal file
56
x/auth/ante/unorderedtx/snapshotter_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package unorderedtx_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/auth/ante/unorderedtx"
|
||||
)
|
||||
|
||||
func TestSnapshotter(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
txm := unorderedtx.NewManager(dataDir)
|
||||
|
||||
// add a handful of unordered txs
|
||||
for i := 0; i < 100; i++ {
|
||||
txm.Add([32]byte{byte(i)}, 100)
|
||||
}
|
||||
|
||||
var unorderedTxBz []byte
|
||||
s := unorderedtx.NewSnapshotter(txm)
|
||||
w := func(bz []byte) error {
|
||||
unorderedTxBz = bz
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.SnapshotExtension(50, w)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, unorderedTxBz)
|
||||
|
||||
pr := func() ([]byte, error) {
|
||||
return unorderedTxBz, nil
|
||||
}
|
||||
|
||||
// restore with an invalid format which should result in an error
|
||||
err = s.RestoreExtension(50, 2, pr)
|
||||
require.Error(t, err)
|
||||
|
||||
// restore with height > ttl which should result in no unordered txs synced
|
||||
txm2 := unorderedtx.NewManager(dataDir)
|
||||
s2 := unorderedtx.NewSnapshotter(txm2)
|
||||
err = s2.RestoreExtension(200, unorderedtx.SnapshotFormat, pr)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, txm2.Size())
|
||||
|
||||
// restore with with height < ttl which should result in all unordered txs synced
|
||||
txm3 := unorderedtx.NewManager(dataDir)
|
||||
s3 := unorderedtx.NewSnapshotter(txm3)
|
||||
err = s3.RestoreExtension(50, unorderedtx.SnapshotFormat, pr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 100, txm3.Size())
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
require.True(t, txm3.Contains([32]byte{byte(i)}))
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,6 @@ type Tx interface {
|
||||
|
||||
types.TxWithMemo
|
||||
types.FeeTx
|
||||
types.TxWithTimeoutHeight
|
||||
types.TxWithUnordered
|
||||
types.HasValidateBasic
|
||||
}
|
||||
|
||||
@ -221,6 +221,11 @@ func (w *wrapper) GetTimeoutHeight() uint64 {
|
||||
return w.tx.Body.TimeoutHeight
|
||||
}
|
||||
|
||||
// GetUnordered returns the transaction's unordered field (if set).
|
||||
func (w *wrapper) GetUnordered() bool {
|
||||
return w.tx.Body.Unordered
|
||||
}
|
||||
|
||||
func (w *wrapper) GetSignaturesV2() ([]signing.SignatureV2, error) {
|
||||
signerInfos := w.tx.AuthInfo.SignerInfos
|
||||
sigs := w.tx.Signatures
|
||||
@ -283,6 +288,13 @@ func (w *wrapper) SetTimeoutHeight(height uint64) {
|
||||
w.bodyBz = nil
|
||||
}
|
||||
|
||||
func (w *wrapper) SetUnordered(v bool) {
|
||||
w.tx.Body.Unordered = v
|
||||
|
||||
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
|
||||
w.bodyBz = nil
|
||||
}
|
||||
|
||||
func (w *wrapper) SetMemo(memo string) {
|
||||
w.tx.Body.Memo = memo
|
||||
|
||||
|
||||
@ -65,6 +65,8 @@ func TestUnknownFields(t *testing.T) {
|
||||
shouldErr: false,
|
||||
},
|
||||
{
|
||||
// If new fields are added to TxBody the number for some_new_field in the proto definition must be set to
|
||||
// one that it's not used in TxBody.
|
||||
name: "critical fields in TxBody should error on decode",
|
||||
body: &testdata.TestUpdatedTxBody{
|
||||
Memo: "foo",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user