feat: Add signer extraction adapter to prio-nonce mempool (backport #18991) (#19042)

Co-authored-by: Eric Warehime <eric.warehime@gmail.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2024-01-15 10:25:14 +01:00 committed by GitHub
parent 8ac0492142
commit 3e9a3e94bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 10 deletions

View File

@ -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

View File

@ -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}

View File

@ -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(),
},
)

View File

@ -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())
})
}
}

View File

@ -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
}