From 7c59eade9a934f1319694f27e7c4876d31531e92 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sun, 30 Apr 2023 21:01:13 +0800 Subject: [PATCH] feat: support extension options for build tx (#15992) --- CHANGELOG.md | 1 + client/tx/factory.go | 28 +++++++++++++++ client/tx/tx_test.go | 65 ++++++++++++++++------------------ client/tx_config.go | 7 ++++ types/module/testutil/codec.go | 29 +++++++++++++++ 5 files changed, 96 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f70e7231..96ff4790c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (telemetry) [#15657](https://github.com/cosmos/cosmos-sdk/pull/15657) Emit more data (go version, sdk version, upgrade height) in prom metrics * (modulemanager) [#15829](https://github.com/cosmos/cosmos-sdk/pull/15829) add new endblocker interface to handle valset updates * (core) [#14860](https://github.com/cosmos/cosmos-sdk/pull/14860) Add `Precommit` and `PrepareCheckState` AppModule callbacks. +* (tx) [#15992](https://github.com/cosmos/cosmos-sdk/pull/15992) Add `WithExtensionOptions` in tx Factory to allow `SetExtensionOptions` with given extension options. ### Improvements diff --git a/client/tx/factory.go b/client/tx/factory.go index 2586fcf5dc..db71f2a75f 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -41,6 +42,7 @@ type Factory struct { feeGranter sdk.AccAddress feePayer sdk.AccAddress gasPrices sdk.DecCoins + extOptions []*codectypes.Any signMode signing.SignMode simulateAndExecute bool preprocessTxHook client.PreprocessTxFn @@ -281,6 +283,28 @@ func (f Factory) PreprocessTx(keyname string, builder client.TxBuilder) error { return f.preprocessTxHook(f.chainID, key.GetType(), builder) } +// WithExtensionOptions returns a Factory with given extension options added to the existing options, +// Example to add dynamic fee extension options: +// +// extOpt := ethermint.ExtensionOptionDynamicFeeTx{ +// MaxPriorityPrice: sdk.NewInt(1000000), +// } +// +// extBytes, _ := extOpt.Marshal() +// +// extOpts := []*types.Any{ +// { +// TypeUrl: "/ethermint.types.v1.ExtensionOptionDynamicFeeTx", +// Value: extBytes, +// }, +// } +// +// txf.WithExtensionOptions(extOpts...) +func (f Factory) WithExtensionOptions(extOpts ...*codectypes.Any) Factory { + f.extOptions = extOpts + return f +} + // BuildUnsignedTx builds a transaction to be signed given a set of messages. // Once created, the fee, memo, and messages are set. func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) { @@ -329,6 +353,10 @@ func (f Factory) BuildUnsignedTx(msgs ...sdk.Msg) (client.TxBuilder, error) { tx.SetFeePayer(f.feePayer) tx.SetTimeoutHeight(f.TimeoutHeight()) + if etx, ok := tx.(client.ExtendedTxBuilder); ok { + etx.SetExtensionOptions(f.extOptions...) + } + return tx, nil } diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 20d0a642d8..386a2391e0 100644 --- a/client/tx/tx_test.go +++ b/client/tx/tx_test.go @@ -105,6 +105,16 @@ func TestCalculateGas(t *testing.T) { } } +func mockTxFactory(txCfg client.TxConfig) tx.Factory { + return tx.Factory{}. + WithTxConfig(txCfg). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain") +} + func TestBuildSimTx(t *testing.T) { txCfg, cdc := newTestTxConfig() defaultSignMode, err := signing.APISignModeToInternal(txCfg.SignModeHandler().DefaultMode()) @@ -117,16 +127,7 @@ func TestBuildSimTx(t *testing.T) { _, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) - txf := tx.Factory{}. - WithTxConfig(txCfg). - WithAccountNumber(50). - WithSequence(23). - WithFees("50stake"). - WithMemo("memo"). - WithChainID("test-chain"). - WithSignMode(defaultSignMode). - WithKeybase(kb) - + txf := mockTxFactory(txCfg).WithSignMode(defaultSignMode).WithKeybase(kb) msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) bz, err := txf.BuildSimTx(msg) require.NoError(t, err) @@ -142,16 +143,7 @@ func TestBuildUnsignedTx(t *testing.T) { _, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) - - txf := tx.Factory{}. - WithTxConfig(txConfig). - WithAccountNumber(50). - WithSequence(23). - WithFees("50stake"). - WithMemo("memo"). - WithChainID("test-chain"). - WithKeybase(kb) - + txf := mockTxFactory(txConfig).WithKeybase(kb) msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) tx, err := txf.BuildUnsignedTx(msg) require.NoError(t, err) @@ -162,6 +154,23 @@ func TestBuildUnsignedTx(t *testing.T) { require.Empty(t, sigs) } +func TestBuildUnsignedTxWithWithExtensionOptions(t *testing.T) { + txCfg := moduletestutil.MakeBuilderTestTxConfig() + extOpts := []*codectypes.Any{ + { + TypeUrl: "/test", + Value: []byte("test"), + }, + } + txf := mockTxFactory(txCfg).WithExtensionOptions(extOpts...) + msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) + tx, err := txf.BuildUnsignedTx(msg) + require.NoError(t, err) + require.NotNil(t, tx) + txb := tx.(*moduletestutil.TestTxBuilder) + require.Equal(t, extOpts, txb.ExtOptions) +} + func TestMnemonicInMemo(t *testing.T) { txConfig, cdc := newTestTxConfig() kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil, cdc) @@ -238,13 +247,7 @@ func TestSign(t *testing.T) { requireT.NotEqual(pubKey1.Bytes(), pubKey2.Bytes()) t.Log("Pub keys:", pubKey1, pubKey2) - txfNoKeybase := tx.Factory{}. - WithTxConfig(txConfig). - WithAccountNumber(50). - WithSequence(23). - WithFees("50stake"). - WithMemo("memo"). - WithChainID("test-chain") + txfNoKeybase := mockTxFactory(txConfig) txfDirect := txfNoKeybase. WithKeybase(kb). WithSignMode(signingtypes.SignMode_SIGN_MODE_DIRECT) @@ -405,13 +408,7 @@ func TestPreprocessHook(t *testing.T) { return nil }) - txfDirect := tx.Factory{}. - WithTxConfig(txConfig). - WithAccountNumber(50). - WithSequence(23). - WithFees("50stake"). - WithMemo("memo"). - WithChainID("test-chain"). + txfDirect := mockTxFactory(txConfig). WithKeybase(kb). WithSignMode(signingtypes.SignMode_SIGN_MODE_DIRECT). WithPreprocessTxHook(preprocessHook) diff --git a/client/tx_config.go b/client/tx_config.go index 2d56976a63..5b74c0d175 100644 --- a/client/tx_config.go +++ b/client/tx_config.go @@ -2,6 +2,7 @@ package client import ( txsigning "cosmossdk.io/x/tx/signing" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -49,4 +50,10 @@ type ( SetFeeGranter(feeGranter sdk.AccAddress) AddAuxSignerData(tx.AuxSignerData) error } + + // ExtendedTxBuilder extends the TxBuilder interface, + // which is used to set extension options to be included in a transaction. + ExtendedTxBuilder interface { + SetExtensionOptions(extOpts ...*codectypes.Any) + } ) diff --git a/types/module/testutil/codec.go b/types/module/testutil/codec.go index 8c1078b176..2a6959f02d 100644 --- a/types/module/testutil/codec.go +++ b/types/module/testutil/codec.go @@ -47,3 +47,32 @@ func MakeTestTxConfig() client.TxConfig { cdc := codec.NewProtoCodec(interfaceRegistry) return tx.NewTxConfig(cdc, tx.DefaultSignModes) } + +type TestBuilderTxConfig struct { + client.TxConfig + TxBuilder *TestTxBuilder +} + +func MakeBuilderTestTxConfig() TestBuilderTxConfig { + return TestBuilderTxConfig{ + TxConfig: MakeTestTxConfig(), + } +} + +func (cfg TestBuilderTxConfig) NewTxBuilder() client.TxBuilder { + if cfg.TxBuilder == nil { + cfg.TxBuilder = &TestTxBuilder{ + TxBuilder: cfg.TxConfig.NewTxBuilder(), + } + } + return cfg.TxBuilder +} + +type TestTxBuilder struct { + client.TxBuilder + ExtOptions []*types.Any +} + +func (b *TestTxBuilder) SetExtensionOptions(extOpts ...*types.Any) { + b.ExtOptions = extOpts +}