From 6694fbe342bafacef044ca1b96e847b08b7ecf77 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Fri, 28 Apr 2023 18:35:13 +0800 Subject: [PATCH 1/5] support extension options for build tx * include extension option used with ethermint dynamic fee tx * for more info, see https://github.com/evmos/ethermint/blob/main/proto/ethermint/types/v1/dynamic_fee.proto --- client/tx/factory.go | 12 ++++++++++++ client/tx_config.go | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/client/tx/factory.go b/client/tx/factory.go index 2586fcf5dc79..626ba28504ce 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,12 @@ 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. +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 +337,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_config.go b/client/tx_config.go index 2d56976a63b0..51190d41be00 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,8 @@ type ( SetFeeGranter(feeGranter sdk.AccAddress) AddAuxSignerData(tx.AuxSignerData) error } + + ExtendedTxBuilder interface { + SetExtensionOptions(extOpts ...*codectypes.Any) + } ) From e24161cd713f70e4ba8a3aa1c699b9e9ebc87739 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sat, 29 Apr 2023 20:39:01 +0800 Subject: [PATCH 2/5] add change doc --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f70e72315d..96ff4790c48b 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 From d4ddd9147ad7d621c90ef26e2f06f0a6e20cf2e5 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sat, 29 Apr 2023 21:54:26 +0800 Subject: [PATCH 3/5] add test --- client/tx/tx_test.go | 65 ++++++++++++++++------------------ types/module/testutil/codec.go | 29 +++++++++++++++ 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 20d0a642d860..386a2391e04c 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/types/module/testutil/codec.go b/types/module/testutil/codec.go index 8c1078b17675..2a6959f02dad 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 +} From a5fca5cc30fde5902c6f123fbbac9e264e736eb0 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sun, 30 Apr 2023 09:10:17 +0800 Subject: [PATCH 4/5] Apply suggestions from code review --- client/tx/factory.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/client/tx/factory.go b/client/tx/factory.go index 626ba28504ce..db71f2a75fb7 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -283,7 +283,23 @@ 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. +// 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 From 9562a14695c3eea3591e8345099e539631a2949c Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sun, 30 Apr 2023 16:55:56 +0800 Subject: [PATCH 5/5] Update client/tx_config.go --- client/tx_config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/tx_config.go b/client/tx_config.go index 51190d41be00..5b74c0d175cb 100644 --- a/client/tx_config.go +++ b/client/tx_config.go @@ -51,6 +51,8 @@ type ( 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) }