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:
Tyler
2025-02-27 11:01:50 -08:00
committed by GitHub
co-authored by Aleksandr Bezobchuk yihuang Facundo Facundo Medica Alex | Interchain Labs
parent ff779eca8d
commit 7f7c41e4aa
63 changed files with 3091 additions and 1295 deletions
+8 -1
View File
@@ -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)
}
+14 -3
View File
@@ -2,9 +2,11 @@ package tx
import (
"context"
"time"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
txsigning "cosmossdk.io/x/tx/signing"
@@ -58,6 +60,14 @@ func (b *AuxTxBuilder) SetTimeoutHeight(height uint64) {
b.auxSignerData.SignDoc.BodyBytes = nil
}
// SetTimeoutTimestamp sets a timeout timestamp in the tx.
func (b *AuxTxBuilder) SetTimeoutTimestamp(timestamp time.Time) {
b.checkEmptyFields()
b.body.TimeoutTimestamp = timestamppb.New(timestamp)
b.auxSignerData.SignDoc.BodyBytes = nil
}
// SetMsgs sets an array of Msgs in the tx.
func (b *AuxTxBuilder) SetMsgs(msgs ...sdk.Msg) error {
anys := make([]*anypb.Any, len(msgs))
@@ -209,9 +219,10 @@ func (b *AuxTxBuilder) GetSignBytes() ([]byte, error) {
})
auxBody := &txv1beta1.TxBody{
Messages: body.Messages,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
Messages: body.Messages,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
TimeoutTimestamp: body.TimeoutTimestamp,
// AuxTxBuilder has no concern with extension options, so we set them to nil.
// This preserves pre-PR#16025 behavior where extension options were ignored, this code path:
// https://github.com/cosmos/cosmos-sdk/blob/ac3c209326a26b46f65a6cc6f5b5ebf6beb79b38/client/tx/aux_builder.go#L193
+46 -11
View File
@@ -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
+4
View File
@@ -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
}