Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add signer extraction adapter to prio-nonce mempool #18991

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Every Module contains its own CHANGELOG.md. Please refer to the module you are i

### 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.
Expand Down
13 changes: 10 additions & 3 deletions types/mempool/priority_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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
Expand Down Expand Up @@ -117,7 +120,8 @@ func NewDefaultTxPriority() TxPriority[int64] {

func DefaultPriorityNonceMempoolConfig() PriorityNonceMempoolConfig[int64] {
return PriorityNonceMempoolConfig[int64]{
TxPriority: NewDefaultTxPriority(),
TxPriority: NewDefaultTxPriority(),
SignerExtractor: NewDefaultSignerExtractionAdapter(),
}
}

Expand Down Expand Up @@ -158,6 +162,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),
Expand Down Expand Up @@ -205,7 +212,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
}
Expand All @@ -214,7 +221,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}
Expand Down
17 changes: 11 additions & 6 deletions types/mempool/priority_nonce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ func (s *MempoolTestSuite) TestRandomGeneratedTxs() {
OnRead: func(tx sdk.Tx) {
s.iterations++
},
SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(),
},
)

Expand Down Expand Up @@ -697,8 +698,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 {
Expand All @@ -717,8 +719,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 {
Expand All @@ -736,8 +739,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 {
Expand Down Expand Up @@ -782,6 +786,7 @@ func TestNextSenderTx_TxReplacement(t *testing.T) {
threshold := int64(100 + feeBump)
return np >= op*threshold/100
},
SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(),
},
)

Expand Down
58 changes: 58 additions & 0 deletions types/mempool/signer_extraction_adapater_test.go
Original file line number Diff line number Diff line change
@@ -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())
})
}
}
68 changes: 68 additions & 0 deletions types/mempool/signer_extraction_adapter.go
Original file line number Diff line number Diff line change
@@ -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
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
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
}
Loading