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(adapters): Add SignerExtractionAdapter [ENG-1916] #114

Merged
merged 3 commits into from
Sep 27, 2023
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ TEST_INTEGRATION_TAGS = integration

test-integration: $(TEST_INTEGRATION_DEPS)
@ echo "Running integration tests..."
@go test ./tests/integration/block_sdk_integration_test.go -timeout 30m -race -v -tags='$(TEST_INTEGRATION_TAGS)'
@go test ./tests/integration/block_sdk_integration_test.go -timeout 30m -p 1 -race -v -tags='$(TEST_INTEGRATION_TAGS)'

test: use-main
@go test -v -race $(shell go list ./... | grep -v tests/)
Expand Down
47 changes: 26 additions & 21 deletions abci/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/suite"

"github.com/skip-mev/block-sdk/abci"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
"github.com/skip-mev/block-sdk/block/base"
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
Expand Down Expand Up @@ -735,52 +736,56 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool)

func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *defaultlane.DefaultLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
MaxBlockSpace: maxBlockSpace,
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
MaxBlockSpace: maxBlockSpace,
}

return defaultlane.NewDefaultLane(cfg)
}

func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
MaxBlockSpace: maxBlockSpace,
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxBlockSpace: maxBlockSpace,
}

return mev.NewMEVLane(cfg, mev.NewDefaultAuctionFactory(cfg.TxDecoder))
return mev.NewMEVLane(cfg, mev.NewDefaultAuctionFactory(cfg.TxDecoder, signer_extraction.NewDefaultAdapter()))
}

func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
MaxBlockSpace: maxBlockSpace,
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: s.setUpAnteHandler(expectedExecution),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxBlockSpace: maxBlockSpace,
}

return free.NewFreeLane(cfg, base.DefaultTxPriority(), free.DefaultMatchHandler())
}

func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *base.BaseLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
MaxBlockSpace: maxBlockSpace,
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxBlockSpace: maxBlockSpace,
}

lane := base.NewBaseLane(
cfg,
"panic",
base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, 0),
base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, signer_extraction.NewDefaultAdapter(), 0),
base.DefaultMatchHandler(),
)

Expand Down
51 changes: 51 additions & 0 deletions adapters/signer_extraction_adapter/signer_extraction_adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package signerextraction

import (
"fmt"

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

type SignerData struct {
Signer sdk.AccAddress
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious to hear what you think, but I wonder if this needs to be an sdk.AccAddress or just a string. Also, we should probably bring this up to the cosmos sdk team. Ideally, we upstream these changes for others to also have, but for time being this works.

Sequence uint64
}

// SignerExtractionAdapter is an interface used to determine how the signers of a transaction should be extracted
// from the transaction.
type Adapter interface {
GetSigners(sdk.Tx) ([]SignerData, error)
}

var _ Adapter = DefaultAdapter{}

// DefaultSignerExtractionAdapter is the default implementation of SignerExtractionAdapter. It extracts the signers
// from a cosmos-sdk tx via GetSignaturesV2.
type DefaultAdapter struct{}

func NewDefaultAdapter() DefaultAdapter {
return DefaultAdapter{}
}

func (DefaultAdapter) 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] = SignerData{
Signer: sig.PubKey.Address().Bytes(),
Sequence: sig.Sequence,
}
}

return signers, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package signerextraction_test

import (
"math/rand"
"testing"

"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
testutils "github.com/skip-mev/block-sdk/testutils"
"github.com/stretchr/testify/suite"
)

type SignerExtractionAdapterTestSuite struct {
suite.Suite
txConfig client.TxConfig
accts []testutils.Account
adapter signer_extraction.DefaultAdapter
}

func TestSignerExtractionAdapterTestSuite(t *testing.T) {
suite.Run(t, new(SignerExtractionAdapterTestSuite))
}

func (s *SignerExtractionAdapterTestSuite) SetupTest() {
encodingConfig := testutils.CreateTestEncodingConfig()
s.txConfig = encodingConfig.TxConfig

accts := testutils.RandomAccounts(rand.New(rand.NewSource(1)), 2)

s.accts = accts
}

func (s *SignerExtractionAdapterTestSuite) TestGetSigners() {
acct := s.accts[0]
tx, err := testutils.CreateTx(s.txConfig, acct, 1, 1, []sdk.Msg{
&banktypes.MsgSend{
FromAddress: acct.Address.String(),
ToAddress: acct.Address.String(),
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 1)),
},
}, sdk.NewCoins(sdk.NewCoin("test", math.NewInt(1)))...)
s.Require().NoError(err)

signers, err := s.adapter.GetSigners(tx)
s.Require().NoError(err)

s.Require().Len(signers, 1)
s.Require().Equal(acct.Address.String(), signers[0].Signer.String())
s.Require().Equal(uint64(1), signers[0].Sequence)
}
21 changes: 16 additions & 5 deletions block/base/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
)

Expand All @@ -17,6 +18,10 @@ type LaneConfig struct {
TxDecoder sdk.TxDecoder
AnteHandler sdk.AnteHandler

// SignerExtractor defines the interface used for extracting the expected signers of a transaction
// from the transaction.
SignerExtractor signer_extraction.Adapter

// MaxBlockSpace defines the relative percentage of block space that can be
// used by this lane. NOTE: If this is set to zero, then there is no limit
// on the number of transactions that can be included in the block for this
Expand Down Expand Up @@ -47,14 +52,16 @@ func NewLaneConfig(
txEncoder sdk.TxEncoder,
txDecoder sdk.TxDecoder,
anteHandler sdk.AnteHandler,
signerExtractor signer_extraction.Adapter,
maxBlockSpace math.LegacyDec,
) LaneConfig {
return LaneConfig{
Logger: logger,
TxEncoder: txEncoder,
TxDecoder: txDecoder,
AnteHandler: anteHandler,
MaxBlockSpace: maxBlockSpace,
Logger: logger,
TxEncoder: txEncoder,
TxDecoder: txDecoder,
AnteHandler: anteHandler,
MaxBlockSpace: maxBlockSpace,
SignerExtractor: signerExtractor,
}
}

Expand All @@ -72,6 +79,10 @@ func (c *LaneConfig) ValidateBasic() error {
return fmt.Errorf("tx decoder cannot be nil")
}

if c.SignerExtractor == nil {
return fmt.Errorf("signer extractor cannot be nil")
}

if c.MaxBlockSpace.IsNil() || c.MaxBlockSpace.IsNegative() || c.MaxBlockSpace.GT(math.LegacyOneDec()) {
return fmt.Errorf("max block space must be set to a value between 0 and 1")
}
Expand Down
4 changes: 3 additions & 1 deletion block/base/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"

signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block/utils"
)

Expand Down Expand Up @@ -81,13 +82,14 @@ func DefaultTxPriority() TxPriority[string] {
}

// NewMempool returns a new Mempool.
func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *Mempool[C] {
func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, extractor signer_extraction.Adapter, maxTx int) *Mempool[C] {
return &Mempool[C]{
index: NewPriorityMempool(
PriorityNonceMempoolConfig[C]{
TxPriority: txPriority,
MaxTx: maxTx,
},
extractor,
),
txPriority: txPriority,
txEncoder: txEncoder,
Expand Down
48 changes: 25 additions & 23 deletions block/base/priority_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
)

var (
Expand Down Expand Up @@ -62,11 +62,12 @@ type (
// priority to other sender txs and must be partially ordered by both sender-nonce
// and priority.
PriorityNonceMempool[C comparable] struct {
priorityIndex *skiplist.SkipList
priorityCounts map[C]int
senderIndices map[string]*skiplist.SkipList
scores map[txMeta[C]]txMeta[C]
cfg PriorityNonceMempoolConfig[C]
priorityIndex *skiplist.SkipList
priorityCounts map[C]int
senderIndices map[string]*skiplist.SkipList
scores map[txMeta[C]]txMeta[C]
cfg PriorityNonceMempoolConfig[C]
signerExtractor signer_extraction.Adapter
}

// PriorityNonceIterator defines an iterator that is used for mempool iteration
Expand Down Expand Up @@ -167,21 +168,22 @@ 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] {
func NewPriorityMempool[C comparable](cfg PriorityNonceMempoolConfig[C], extractor signer_extraction.Adapter) *PriorityNonceMempool[C] {
mp := &PriorityNonceMempool[C]{
priorityIndex: skiplist.New(skiplistComparable(cfg.TxPriority)),
priorityCounts: make(map[C]int),
senderIndices: make(map[string]*skiplist.SkipList),
scores: make(map[txMeta[C]]txMeta[C]),
cfg: cfg,
priorityIndex: skiplist.New(skiplistComparable(cfg.TxPriority)),
priorityCounts: make(map[C]int),
senderIndices: make(map[string]*skiplist.SkipList),
scores: make(map[txMeta[C]]txMeta[C]),
cfg: cfg,
signerExtractor: extractor,
}

return mp
}

// DefaultPriorityMempool returns a priorityNonceMempool with no options.
func DefaultPriorityMempool() *PriorityNonceMempool[int64] {
return NewPriorityMempool(DefaultPriorityNonceMempoolConfig())
func DefaultPriorityMempool(extractor signer_extraction.DefaultAdapter) *PriorityNonceMempool[int64] {
return NewPriorityMempool(DefaultPriorityNonceMempoolConfig(), extractor)
}

// NextSenderTx returns the next transaction for a given sender by nonce order,
Expand Down Expand Up @@ -213,18 +215,18 @@ func (mp *PriorityNonceMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error
return nil
}

sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
signers, err := mp.signerExtractor.GetSigners(tx)
if err != nil {
return err
}
if len(sigs) == 0 {
if len(signers) == 0 {
return fmt.Errorf("tx must have at least one signer")
}

sig := sigs[0]
sender := sdk.AccAddress(sig.PubKey.Address()).String()
signer := signers[0]
sender := signer.Signer.String()
priority := mp.cfg.TxPriority.GetTxPriority(ctx, tx)
nonce := sig.Sequence
nonce := signer.Sequence
key := txMeta[C]{nonce: nonce, priority: priority, sender: sender}

senderIndex, ok := mp.senderIndices[sender]
Expand Down Expand Up @@ -427,16 +429,16 @@ func (mp *PriorityNonceMempool[C]) CountTx() int {
// Remove removes a transaction from the mempool in O(log n) time, returning an
// error if unsuccessful.
func (mp *PriorityNonceMempool[C]) Remove(tx sdk.Tx) error {
sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
signers, err := mp.signerExtractor.GetSigners(tx)
if err != nil {
return err
}
if len(sigs) == 0 {
if len(signers) == 0 {
return fmt.Errorf("attempted to remove a tx with no signatures")
}

sig := sigs[0]
sender := sdk.AccAddress(sig.PubKey.Address()).String()
sig := signers[0]
sender := sig.Signer.String()
nonce := sig.Sequence

scoreKey := txMeta[C]{nonce: nonce, sender: sender}
Expand Down
Loading
Loading