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 1 commit
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
Next Next commit
Add signer extraction adapter to mempool
  • Loading branch information
Eric-Warehime committed Jan 9, 2024
commit e646d5ba0f6a9f79c866ed50e11bb96a1f54d371
10 changes: 7 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 @@ -205,7 +209,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 +218,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
}