Skip to content

Commit

Permalink
feat: Add signer extraction adapter to prio-nonce mempool (backport #…
Browse files Browse the repository at this point in the history
…18991) (#19042)

Co-authored-by: Eric Warehime <eric.warehime@gmail.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
  • Loading branch information
3 people committed Jan 15, 2024
1 parent 8ac0492 commit 3e9a3e9
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 10 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

## [v0.50.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.3) - 2023-01-11
## [v0.50.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.3) - 2023-01-15

### Features

* (types) [#18991](https://github.com/cosmos/cosmos-sdk/pull/18991) Add SignerExtractionAdapter to PriorityNonceMempool/Config and provide Default implementation matching existing behavior.
* (gRPC) [#19043](https://github.com/cosmos/cosmos-sdk/pull/19043) Add `halt_height` to the gRPC `/cosmos/base/node/v1beta1/config` request.

### Improvements
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 @@ -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
Expand Down Expand Up @@ -116,7 +119,8 @@ func NewDefaultTxPriority() TxPriority[int64] {

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

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
}
Expand All @@ -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}
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 @@ -435,6 +435,7 @@ func (s *MempoolTestSuite) TestRandomGeneratedTxs() {
OnRead: func(tx sdk.Tx) {
s.iterations++
},
SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(),
},
)

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -783,6 +787,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())
})
}
}
67 changes: 67 additions & 0 deletions types/mempool/signer_extraction_adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package mempool

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
)

// 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 3e9a3e9

Please sign in to comment.