diff --git a/CHANGELOG.md b/CHANGELOG.md index 721dc731d61f..3b2a8bc74488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,20 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [v0.50.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.3) - 2023-01-11 +<<<<<<< HEAD +======= +### Features + +* (types) [#18991](https://github.com/cosmos/cosmos-sdk/pull/18991) Add SignerExtractionAdapter to PriorityNonceMempool/Config and provide Default implementation matching existing behavior. +* (client) [#18557](https://github.com/cosmos/cosmos-sdk/pull/18557) Add `--qrcode` flag to `keys show` command to support displaying keys address QR code. +* (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. +* (tests) [#17868](https://github.com/cosmos/cosmos-sdk/pull/17868) Added helper method `SubmitTestTx` in testutil to broadcast test txns to test e2e tests. +* (client) [#17513](https://github.com/cosmos/cosmos-sdk/pull/17513) Allow overwriting `client.toml`. Use `client.CreateClientConfig` in place of `client.ReadFromClientConfig` and provide a custom template and a custom config. +* (runtime) [#18475](https://github.com/cosmos/cosmos-sdk/pull/18475) Adds an implementation for core.branch.Service. +* (baseapp) [#18499](https://github.com/cosmos/cosmos-sdk/pull/18499) Add `MsgRouter` response type from message name function. +* (types) [#18768](https://github.com/cosmos/cosmos-sdk/pull/18768) Add MustValAddressFromBech32 function. + +>>>>>>> 545258668 (feat: Add signer extraction adapter to prio-nonce mempool (#18991)) ### Improvements * (x/bank) [#18956](https://github.com/cosmos/cosmos-sdk/pull/18956) Introduced a new `DenomOwnersByQuery` query method for `DenomOwners`, which accepts the denom value as a query string parameter, resolving issues with denoms containing slashes. diff --git a/types/mempool/priority_nonce.go b/types/mempool/priority_nonce.go index 6f344a1c8326..bf996b9a6878 100644 --- a/types/mempool/priority_nonce.go +++ b/types/mempool/priority_nonce.go @@ -40,6 +40,9 @@ type ( // (sequence number) when evicting transactions. // - if MaxTx < 0, `Insert` is a no-op. MaxTx int + + // SignerExtractor is an implementation which retrieves signer data from a sdk.Tx + SignerExtractor SignerExtractionAdapter } // PriorityNonceMempool is a mempool implementation that stores txs @@ -116,7 +119,8 @@ func NewDefaultTxPriority() TxPriority[int64] { func DefaultPriorityNonceMempoolConfig() PriorityNonceMempoolConfig[int64] { return PriorityNonceMempoolConfig[int64]{ - TxPriority: NewDefaultTxPriority(), + TxPriority: NewDefaultTxPriority(), + SignerExtractor: NewDefaultSignerExtractionAdapter(), } } @@ -157,6 +161,9 @@ func skiplistComparable[C comparable](txPriority TxPriority[C]) skiplist.Compara // NewPriorityMempool returns the SDK's default mempool implementation which // returns txs in a partial order by 2 dimensions; priority, and sender-nonce. func NewPriorityMempool[C comparable](cfg PriorityNonceMempoolConfig[C]) *PriorityNonceMempool[C] { + if cfg.SignerExtractor == nil { + cfg.SignerExtractor = NewDefaultSignerExtractionAdapter() + } mp := &PriorityNonceMempool[C]{ priorityIndex: skiplist.New(skiplistComparable(cfg.TxPriority)), priorityCounts: make(map[C]int), @@ -204,7 +211,7 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error return nil } - sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2() + sigs, err := mp.cfg.SignerExtractor.GetSigners(tx) if err != nil { return err } @@ -213,7 +220,7 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error } sig := sigs[0] - sender := sdk.AccAddress(sig.PubKey.Address()).String() + sender := sig.Signer.String() priority := mp.cfg.TxPriority.GetTxPriority(ctx, tx) nonce := sig.Sequence key := txMeta[C]{nonce: nonce, priority: priority, sender: sender} diff --git a/types/mempool/priority_nonce_test.go b/types/mempool/priority_nonce_test.go index e51b7271c62d..462b213201cc 100644 --- a/types/mempool/priority_nonce_test.go +++ b/types/mempool/priority_nonce_test.go @@ -435,6 +435,7 @@ func (s *MempoolTestSuite) TestRandomGeneratedTxs() { OnRead: func(tx sdk.Tx) { s.iterations++ }, + SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(), }, ) @@ -698,8 +699,9 @@ func TestNextSenderTx_TxLimit(t *testing.T) { // unlimited mp := mempool.NewPriorityMempool( mempool.PriorityNonceMempoolConfig[int64]{ - TxPriority: mempool.NewDefaultTxPriority(), - MaxTx: 0, + TxPriority: mempool.NewDefaultTxPriority(), + MaxTx: 0, + SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(), }, ) for i, tx := range txs { @@ -718,8 +720,9 @@ func TestNextSenderTx_TxLimit(t *testing.T) { // limit: 3 mp = mempool.NewPriorityMempool( mempool.PriorityNonceMempoolConfig[int64]{ - TxPriority: mempool.NewDefaultTxPriority(), - MaxTx: 3, + TxPriority: mempool.NewDefaultTxPriority(), + MaxTx: 3, + SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(), }, ) for i, tx := range txs { @@ -737,8 +740,9 @@ func TestNextSenderTx_TxLimit(t *testing.T) { // disabled mp = mempool.NewPriorityMempool( mempool.PriorityNonceMempoolConfig[int64]{ - TxPriority: mempool.NewDefaultTxPriority(), - MaxTx: -1, + TxPriority: mempool.NewDefaultTxPriority(), + MaxTx: -1, + SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(), }, ) for _, tx := range txs { @@ -783,6 +787,7 @@ func TestNextSenderTx_TxReplacement(t *testing.T) { threshold := int64(100 + feeBump) return np >= op*threshold/100 }, + SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(), }, ) diff --git a/types/mempool/signer_extraction_adapater_test.go b/types/mempool/signer_extraction_adapater_test.go new file mode 100644 index 000000000000..b2218dc8e511 --- /dev/null +++ b/types/mempool/signer_extraction_adapater_test.go @@ -0,0 +1,58 @@ +package mempool_test + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/mempool" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +type nonVerifiableTx struct{} + +func (n nonVerifiableTx) GetMsgs() []sdk.Msg { + panic("not implemented") +} + +func (n nonVerifiableTx) GetMsgsV2() ([]proto.Message, error) { + panic("not implemented") +} + +func TestDefaultSignerExtractor(t *testing.T) { + accounts := simtypes.RandomAccounts(rand.New(rand.NewSource(0)), 1) + sa := accounts[0].Address + ext := mempool.NewDefaultSignerExtractionAdapter() + goodTx := testTx{id: 0, priority: 0, nonce: 0, address: sa} + badTx := &sigErrTx{getSigs: func() ([]txsigning.SignatureV2, error) { + return nil, fmt.Errorf("error") + }} + nonSigVerify := nonVerifiableTx{} + + tests := []struct { + name string + tx sdk.Tx + sea mempool.SignerExtractionAdapter + err error + }{ + {name: "valid tx extracts sigs", tx: goodTx, sea: ext, err: nil}, + {name: "invalid tx fails on sig", tx: badTx, sea: ext, err: fmt.Errorf("err")}, + {name: "non-verifiable tx fails on conversion", tx: nonSigVerify, sea: ext, err: fmt.Errorf("tx of type %T does not implement SigVerifiableTx", nonSigVerify)}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + sigs, err := test.sea.GetSigners(test.tx) + if test.err != nil { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, sigs[0].String(), mempool.SignerData{Signer: sa, Sequence: 0}.String()) + }) + } +} diff --git a/types/mempool/signer_extraction_adapter.go b/types/mempool/signer_extraction_adapter.go new file mode 100644 index 000000000000..f79d6c0693d5 --- /dev/null +++ b/types/mempool/signer_extraction_adapter.go @@ -0,0 +1,68 @@ +package mempool + +import ( + "fmt" + + "cosmossdk.io/x/auth/signing" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SignerData contains canonical useful information about the signer of a transaction +type SignerData struct { + Signer sdk.AccAddress + Sequence uint64 +} + +// NewSignerData returns a new SignerData instance. +func NewSignerData(signer sdk.AccAddress, sequence uint64) SignerData { + return SignerData{ + Signer: signer, + Sequence: sequence, + } +} + +// String implements the fmt.Stringer interface. +func (s SignerData) String() string { + return fmt.Sprintf("SignerData{Signer: %s, Sequence: %d}", s.Signer, s.Sequence) +} + +// SignerExtractionAdapter is an interface used to determine how the signers of a transaction should be extracted +// from the transaction. +type SignerExtractionAdapter interface { + GetSigners(sdk.Tx) ([]SignerData, error) +} + +var _ SignerExtractionAdapter = DefaultSignerExtractionAdapter{} + +// DefaultSignerExtractionAdapter is the default implementation of SignerExtractionAdapter. It extracts the signers +// from a cosmos-sdk tx via GetSignaturesV2. +type DefaultSignerExtractionAdapter struct{} + +// NewDefaultSignerExtractionAdapter constructs a new DefaultSignerExtractionAdapter instance +func NewDefaultSignerExtractionAdapter() DefaultSignerExtractionAdapter { + return DefaultSignerExtractionAdapter{} +} + +// GetSigners implements the Adapter interface +func (DefaultSignerExtractionAdapter) GetSigners(tx sdk.Tx) ([]SignerData, error) { + sigTx, ok := tx.(signing.SigVerifiableTx) + if !ok { + return nil, fmt.Errorf("tx of type %T does not implement SigVerifiableTx", tx) + } + + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return nil, err + } + + signers := make([]SignerData, len(sigs)) + for i, sig := range sigs { + signers[i] = NewSignerData( + sig.PubKey.Address().Bytes(), + sig.Sequence, + ) + } + + return signers, nil +}