Skip to content

Commit

Permalink
feat: Add signer extraction adapter to prio-nonce mempool (cosmos#18991)
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric-Warehime authored Jan 12, 2024
1 parent eaf92c2 commit 5452586
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 9 deletions.
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
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
}

0 comments on commit 5452586

Please sign in to comment.