feat: backport unordered transactions (#23708)
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: yihuang <huang@crypto.com> Co-authored-by: Facundo <facundomedica@gmail.com> Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com> Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
co-authored by
Aleksandr Bezobchuk
yihuang
Facundo
Facundo Medica
Alex | Interchain Labs
parent
ff779eca8d
commit
7f7c41e4aa
@@ -74,6 +74,8 @@ const (
|
||||
FlagOffset = "offset"
|
||||
FlagCountTotal = "count-total"
|
||||
FlagTimeoutHeight = "timeout-height"
|
||||
FlagTimeoutTimestamp = "timeout-timestamp"
|
||||
FlagUnordered = "unordered"
|
||||
FlagKeyAlgorithm = "algo"
|
||||
FlagKeyType = "key-type"
|
||||
FlagFeePayer = "fee-payer"
|
||||
@@ -135,7 +137,9 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
|
||||
f.Bool(FlagOffline, false, "Offline mode (does not allow any online functionality)")
|
||||
f.BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
|
||||
f.String(FlagSignMode, "", "Choose sign mode (direct|amino-json|direct-aux|textual), this is an advanced feature")
|
||||
f.Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height")
|
||||
f.Uint64(FlagTimeoutHeight, 0, "DEPRECATED: Please use --timeout-timestamp instead. Set a block timeout height to prevent the tx from being committed past a certain height")
|
||||
f.Int64(FlagTimeoutTimestamp, 0, "Set a block timeout timestamp to prevent the tx from being committed past a certain time")
|
||||
f.Bool(FlagUnordered, false, "Enable unordered transaction delivery; must be used in conjunction with --timeout-timestamp")
|
||||
f.String(FlagFeePayer, "", "Fee payer pays fees for the transaction instead of deducting from the signer")
|
||||
f.String(FlagFeeGranter, "", "Fee granter grants fees for the transaction")
|
||||
f.String(FlagTip, "", "Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator")
|
||||
@@ -145,6 +149,9 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
|
||||
f.String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically. Note: %q option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of %q. (default %d)",
|
||||
GasFlagAuto, GasFlagAuto, FlagFees, DefaultGasLimit))
|
||||
|
||||
cmd.MarkFlagsMutuallyExclusive(FlagTimeoutHeight, FlagTimeoutTimestamp)
|
||||
cmd.MarkFlagsRequiredTogether(FlagUnordered, FlagTimeoutTimestamp)
|
||||
|
||||
AddKeyringFlags(f)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ package tx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
txsigning "cosmossdk.io/x/tx/signing"
|
||||
@@ -58,6 +60,14 @@ func (b *AuxTxBuilder) SetTimeoutHeight(height uint64) {
|
||||
b.auxSignerData.SignDoc.BodyBytes = nil
|
||||
}
|
||||
|
||||
// SetTimeoutTimestamp sets a timeout timestamp in the tx.
|
||||
func (b *AuxTxBuilder) SetTimeoutTimestamp(timestamp time.Time) {
|
||||
b.checkEmptyFields()
|
||||
|
||||
b.body.TimeoutTimestamp = timestamppb.New(timestamp)
|
||||
b.auxSignerData.SignDoc.BodyBytes = nil
|
||||
}
|
||||
|
||||
// SetMsgs sets an array of Msgs in the tx.
|
||||
func (b *AuxTxBuilder) SetMsgs(msgs ...sdk.Msg) error {
|
||||
anys := make([]*anypb.Any, len(msgs))
|
||||
@@ -209,9 +219,10 @@ func (b *AuxTxBuilder) GetSignBytes() ([]byte, error) {
|
||||
})
|
||||
|
||||
auxBody := &txv1beta1.TxBody{
|
||||
Messages: body.Messages,
|
||||
Memo: body.Memo,
|
||||
TimeoutHeight: body.TimeoutHeight,
|
||||
Messages: body.Messages,
|
||||
Memo: body.Memo,
|
||||
TimeoutHeight: body.TimeoutHeight,
|
||||
TimeoutTimestamp: body.TimeoutTimestamp,
|
||||
// AuxTxBuilder has no concern with extension options, so we set them to nil.
|
||||
// This preserves pre-PR#16025 behavior where extension options were ignored, this code path:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/ac3c209326a26b46f65a6cc6f5b5ebf6beb79b38/client/tx/aux_builder.go#L193
|
||||
|
||||
+46
-11
@@ -3,8 +3,10 @@ package tx
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/go-bip39"
|
||||
"github.com/spf13/pflag"
|
||||
@@ -32,9 +34,11 @@ type Factory struct {
|
||||
sequence uint64
|
||||
gas uint64
|
||||
timeoutHeight uint64
|
||||
timeoutTimestamp time.Time
|
||||
gasAdjustment float64
|
||||
chainID string
|
||||
fromName string
|
||||
unordered bool
|
||||
offline bool
|
||||
generateOnly bool
|
||||
memo string
|
||||
@@ -50,6 +54,14 @@ type Factory struct {
|
||||
|
||||
// NewFactoryCLI creates a new Factory.
|
||||
func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, error) {
|
||||
if clientCtx.Viper == nil {
|
||||
clientCtx = clientCtx.WithViper("")
|
||||
}
|
||||
|
||||
if err := clientCtx.Viper.BindPFlags(flagSet); err != nil {
|
||||
return Factory{}, fmt.Errorf("failed to bind flags to viper: %w", err)
|
||||
}
|
||||
|
||||
signMode := signing.SignMode_SIGN_MODE_UNSPECIFIED
|
||||
switch clientCtx.SignModeStr {
|
||||
case flags.SignModeDirect:
|
||||
@@ -67,18 +79,21 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
|
||||
var accNum, accSeq uint64
|
||||
if clientCtx.Offline {
|
||||
if flagSet.Changed(flags.FlagAccountNumber) && flagSet.Changed(flags.FlagSequence) {
|
||||
accNum, _ = flagSet.GetUint64(flags.FlagAccountNumber)
|
||||
accSeq, _ = flagSet.GetUint64(flags.FlagSequence)
|
||||
accNum = clientCtx.Viper.GetUint64(flags.FlagAccountNumber)
|
||||
accSeq = clientCtx.Viper.GetUint64(flags.FlagSequence)
|
||||
} else {
|
||||
return Factory{}, errors.New("account-number and sequence must be set in offline mode")
|
||||
}
|
||||
}
|
||||
|
||||
gasAdj, _ := flagSet.GetFloat64(flags.FlagGasAdjustment)
|
||||
memo, _ := flagSet.GetString(flags.FlagNote)
|
||||
timeoutHeight, _ := flagSet.GetUint64(flags.FlagTimeoutHeight)
|
||||
gasAdj := clientCtx.Viper.GetFloat64(flags.FlagGasAdjustment)
|
||||
memo := clientCtx.Viper.GetString(flags.FlagNote)
|
||||
timestampUnix := clientCtx.Viper.GetInt64(flags.FlagTimeoutTimestamp)
|
||||
timeoutTimestamp := time.Unix(timestampUnix, 0)
|
||||
timeoutHeight := clientCtx.Viper.GetUint64(flags.FlagTimeoutHeight)
|
||||
unordered := clientCtx.Viper.GetBool(flags.FlagUnordered)
|
||||
|
||||
gasStr, _ := flagSet.GetString(flags.FlagGas)
|
||||
gasStr := clientCtx.Viper.GetString(flags.FlagGas)
|
||||
gasSetting, _ := flags.ParseGasSetting(gasStr)
|
||||
|
||||
f := Factory{
|
||||
@@ -94,6 +109,8 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
|
||||
accountNumber: accNum,
|
||||
sequence: accSeq,
|
||||
timeoutHeight: timeoutHeight,
|
||||
timeoutTimestamp: timeoutTimestamp,
|
||||
unordered: unordered,
|
||||
gasAdjustment: gasAdj,
|
||||
memo: memo,
|
||||
signMode: signMode,
|
||||
@@ -101,10 +118,10 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e
|
||||
feePayer: clientCtx.FeePayer,
|
||||
}
|
||||
|
||||
feesStr, _ := flagSet.GetString(flags.FlagFees)
|
||||
feesStr := clientCtx.Viper.GetString(flags.FlagFees)
|
||||
f = f.WithFees(feesStr)
|
||||
|
||||
gasPricesStr, _ := flagSet.GetString(flags.FlagGasPrices)
|
||||
gasPricesStr := clientCtx.Viper.GetString(flags.FlagGasPrices)
|
||||
f = f.WithGasPrices(gasPricesStr)
|
||||
|
||||
f = f.WithPreprocessTxHook(clientCtx.PreprocessTxHook)
|
||||
@@ -123,6 +140,8 @@ func (f Factory) Fees() sdk.Coins { return f.fees }
|
||||
func (f Factory) GasPrices() sdk.DecCoins { return f.gasPrices }
|
||||
func (f Factory) AccountRetriever() client.AccountRetriever { return f.accountRetriever }
|
||||
func (f Factory) TimeoutHeight() uint64 { return f.timeoutHeight }
|
||||
func (f Factory) TimeoutTimestamp() time.Time { return f.timeoutTimestamp }
|
||||
func (f Factory) Unordered() bool { return f.unordered }
|
||||
func (f Factory) FromName() string { return f.fromName }
|
||||
|
||||
// SimulateAndExecute returns the option to simulate and then execute the transaction
|
||||
@@ -236,6 +255,18 @@ func (f Factory) WithTimeoutHeight(height uint64) Factory {
|
||||
return f
|
||||
}
|
||||
|
||||
// WithTimeoutTimestamp returns a copy of the Factory with an updated timeout timestamp.
|
||||
func (f Factory) WithTimeoutTimestamp(timestamp time.Time) Factory {
|
||||
f.timeoutTimestamp = timestamp
|
||||
return f
|
||||
}
|
||||
|
||||
// WithUnordered returns a copy of the Factory with an updated unordered field.
|
||||
func (f Factory) WithUnordered(v bool) Factory {
|
||||
f.unordered = v
|
||||
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
|
||||
@@ -311,14 +342,16 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) {
|
||||
return nil, errors.New("cannot provide both fees and gas prices")
|
||||
}
|
||||
|
||||
glDec := math.LegacyNewDec(int64(f.gas))
|
||||
// f.gas is a uint64 and we should convert to LegacyDec
|
||||
// without the risk of under/overflow via uint64->int64.
|
||||
gasLimitDec := math.LegacyNewDecFromBigInt(new(big.Int).SetUint64(f.gas))
|
||||
|
||||
// Derive the fees based on the provided gas prices, where
|
||||
// fee = ceil(gasPrice * gasLimit).
|
||||
fees = make(sdk.Coins, len(f.gasPrices))
|
||||
|
||||
for i, gp := range f.gasPrices {
|
||||
fee := gp.Amount.Mul(glDec)
|
||||
fee := gp.Amount.Mul(gasLimitDec)
|
||||
fees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
|
||||
}
|
||||
}
|
||||
@@ -340,6 +373,8 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) {
|
||||
tx.SetFeeGranter(f.feeGranter)
|
||||
tx.SetFeePayer(f.feePayer)
|
||||
tx.SetTimeoutHeight(f.TimeoutHeight())
|
||||
tx.SetTimeoutTimestamp(f.TimeoutTimestamp())
|
||||
tx.SetUnordered(f.Unordered())
|
||||
|
||||
if etx, ok := tx.(client.ExtendedTxBuilder); ok {
|
||||
etx.SetExtensionOptions(f.extOptions...)
|
||||
@@ -482,7 +517,7 @@ func (f Factory) Prepare(clientCtx client.Context) (Factory, error) {
|
||||
}
|
||||
|
||||
fc := f
|
||||
from := clientCtx.GetFromAddress()
|
||||
from := clientCtx.FromAddress
|
||||
|
||||
if err := fc.accountRetriever.EnsureExists(clientCtx, from); err != nil {
|
||||
return fc, err
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
txsigning "cosmossdk.io/x/tx/signing"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
@@ -48,6 +50,8 @@ type (
|
||||
SetFeePayer(feePayer sdk.AccAddress)
|
||||
SetGasLimit(limit uint64)
|
||||
SetTimeoutHeight(height uint64)
|
||||
SetTimeoutTimestamp(timestamp time.Time)
|
||||
SetUnordered(v bool)
|
||||
SetFeeGranter(feeGranter sdk.AccAddress)
|
||||
AddAuxSignerData(tx.AuxSignerData) error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user