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: sender mempool impl #13888

Merged
merged 60 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
0cb8ad1
draft sender mempool impl
JeancarloBarrios Nov 16, 2022
b967d56
select
JeancarloBarrios Nov 16, 2022
353378c
nit
JeancarloBarrios Nov 16, 2022
20fbacb
random sender update
JeancarloBarrios Nov 16, 2022
c8ea169
nit
JeancarloBarrios Nov 16, 2022
1a89940
prevent memory leak
JeancarloBarrios Nov 16, 2022
28c6cfc
fix nil return
JeancarloBarrios Nov 17, 2022
faecdda
small fixes
JeancarloBarrios Nov 17, 2022
164bded
added tests
JeancarloBarrios Nov 17, 2022
87c221f
change count
JeancarloBarrios Nov 17, 2022
94334f5
finish tx order test removed the three address test due to make the t…
JeancarloBarrios Nov 17, 2022
24ebefe
remove unsued variable
JeancarloBarrios Nov 17, 2022
584bc51
nit
JeancarloBarrios Nov 17, 2022
fe3fbf8
fix
JeancarloBarrios Nov 17, 2022
2a18e81
temoral commit braking
JeancarloBarrios Nov 18, 2022
7784594
nit most
JeancarloBarrios Nov 18, 2022
d34cd88
nit most
JeancarloBarrios Nov 18, 2022
5a385cb
final
JeancarloBarrios Nov 18, 2022
9e02590
comments
JeancarloBarrios Nov 18, 2022
11ceaa7
t
JeancarloBarrios Nov 18, 2022
6703390
comments
JeancarloBarrios Nov 18, 2022
36b42cf
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 18, 2022
7556b9f
test
JeancarloBarrios Nov 20, 2022
6f1a08b
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
kocubinski Nov 21, 2022
d388bc4
add nolint
kocubinski Nov 21, 2022
9ff7528
Fix comment
kocubinski Nov 21, 2022
ec50ee6
golint comment
kocubinski Nov 21, 2022
bc851a9
golint
kocubinski Nov 21, 2022
1c938bf
improve format?
kocubinski Nov 21, 2022
8800e71
more gosec disable
kocubinski Nov 21, 2022
ed8a268
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
kocubinski Nov 21, 2022
beee298
Fix ctr usage
kocubinski Nov 21, 2022
1dcc89c
use #nosec
kocubinski Nov 21, 2022
99aff01
Update types/mempool/sender_nonce.go
kocubinski Nov 21, 2022
b5960c5
Kocubinski/random sender nonce (#13956)
kocubinski Nov 21, 2022
a3bdcb9
import fixes
kocubinski Nov 21, 2022
da63465
derive order randomness from seed only
kocubinski Nov 21, 2022
ea54e18
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
kocubinski Nov 21, 2022
c306fca
gosec fix
kocubinski Nov 21, 2022
a93c950
ignore gosec again
kocubinski Nov 21, 2022
578046a
comments
kocubinski Nov 21, 2022
695ed48
property based
JeancarloBarrios Nov 21, 2022
99b6536
minor fixes
JeancarloBarrios Nov 21, 2022
bf64e22
added property test
JeancarloBarrios Nov 21, 2022
5785fe7
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 21, 2022
ca1c17f
comment
JeancarloBarrios Nov 21, 2022
09cac5d
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 22, 2022
745d50d
fix imports
JeancarloBarrios Nov 22, 2022
c841351
Merge branch 'JeancarloBarrios/random-sender-nonce-ordering' of githu…
JeancarloBarrios Nov 22, 2022
77ea978
comment
JeancarloBarrios Nov 22, 2022
3078d65
Update types/mempool/sender_nonce_property_test.go
JeancarloBarrios Nov 22, 2022
e7c2e2f
remove unesessary loop
JeancarloBarrios Nov 22, 2022
d056a8a
improve function name
JeancarloBarrios Nov 22, 2022
9073aaf
Update types/mempool/sender_nonce.go
JeancarloBarrios Nov 22, 2022
69f3bc5
change import name
JeancarloBarrios Nov 22, 2022
73453ea
change validation to be preemvtive
JeancarloBarrios Nov 22, 2022
847bcf5
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 22, 2022
278adb0
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 23, 2022
560e747
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 23, 2022
cb6121e
Merge branch 'main' into JeancarloBarrios/random-sender-nonce-ordering
JeancarloBarrios Nov 23, 2022
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 baseapp/abci_v1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestABCIv1TestSuite(t *testing.T) {
func (s *ABCIv1TestSuite) SetupTest() {
t := s.T()
anteKey := []byte("ante-key")
pool := mempool.NewNonceMempool()
pool := mempool.NewSenderNonceMempool()
anteOpt := func(bapp *baseapp.BaseApp) {
bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey))
}
Expand Down
2 changes: 1 addition & 1 deletion baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func NewBaseApp(
}

if app.mempool == nil {
app.SetMempool(mempool.NewNonceMempool())
app.SetMempool(mempool.NewSenderNonceMempool())
}

if app.processProposal == nil {
Expand Down
2 changes: 1 addition & 1 deletion baseapp/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func makeTestConfig() depinject.Config {
}

func makeMinimalConfig() depinject.Config {
var mempoolOpt runtime.BaseAppOption = baseapp.SetMempool(mempool.NewNonceMempool())
var mempoolOpt runtime.BaseAppOption = baseapp.SetMempool(mempool.NewSenderNonceMempool())
return depinject.Configs(
depinject.Supply(mempoolOpt),
appconfig.Compose(&appv1alpha1.Config{
Expand Down
3 changes: 2 additions & 1 deletion types/mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func (s *MempoolTestSuite) TestDefaultMempool() {
for i := 0; i < txCount; i++ {
acc := accounts[i%len(accounts)]
tx := testTx{
nonce: 0,
address: acc.Address,
priority: rand.Int63(),
}
Expand Down Expand Up @@ -201,7 +202,7 @@ type MempoolTestSuite struct {

func (s *MempoolTestSuite) resetMempool() {
s.iterations = 0
s.mempool = mempool.NewNonceMempool()
s.mempool = mempool.NewSenderNonceMempool()
}

func (s *MempoolTestSuite) SetupTest() {
Expand Down
125 changes: 0 additions & 125 deletions types/mempool/nonce.go

This file was deleted.

207 changes: 207 additions & 0 deletions types/mempool/sender_nonce.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package mempool

import (
crand "crypto/rand" // #nosec // crypto/rand is used for seed generation
"encoding/binary"
"fmt"
"math/rand" // #nosec // math/rand is used for random selection and seeded from crypto/rand

huandu "github.com/huandu/skiplist"
JeancarloBarrios marked this conversation as resolved.
Show resolved Hide resolved

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

var (
_ Mempool = (*senderNonceMempool)(nil)
_ Iterator = (*senderNonceMepoolIterator)(nil)
)

// senderNonceMempool is a mempool that prioritizes transactions within a sender by nonce, the lowest first,
// but selects a random sender on each iteration. The mempool is iterated by:
//
// 1) Maintaining a separate list of nonce ordered txs per sender
// 2) For each select iteration, randomly choose a sender and pick the next nonce ordered tx from their list
// 3) Repeat 1,2 until the mempool is exhausted
//
// Note that PrepareProposal could choose to stop iteration before reaching the end if maxBytes is reached.
type senderNonceMempool struct {
senders map[string]*huandu.SkipList
Copy link
Member

Choose a reason for hiding this comment

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

is the mempool size here unbounded?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes it is unbounded but this make sense to e gonna start working on a solution

Copy link
Member

Choose a reason for hiding this comment

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

We can merge this and open another pr for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok

rnd *rand.Rand
}

// NewSenderNonceMempool creates a new mempool that prioritizes transactions by nonce, the lowest first.
func NewSenderNonceMempool() Mempool {
senderMap := make(map[string]*huandu.SkipList)
snp := &senderNonceMempool{
senders: senderMap,
}

var seed int64
err := binary.Read(crand.Reader, binary.BigEndian, &seed)
if err != nil {
panic(err)
}
snp.setSeed(seed)

return snp
}

// NewSenderNonceMempoolWithSeed creates a new mempool that prioritizes transactions by nonce, the lowest first and sets the random seed.
func NewSenderNonceMempoolWithSeed(seed int64) Mempool {
senderMap := make(map[string]*huandu.SkipList)
snp := &senderNonceMempool{
senders: senderMap,
}
snp.setSeed(seed)
return snp
}

func (snm *senderNonceMempool) setSeed(seed int64) {
s1 := rand.NewSource(seed)
snm.rnd = rand.New(s1) //#nosec // math/rand is seeded from crypto/rand by default
}

// Insert adds a tx to the mempool. It returns an error if the tx does not have at least one signer.
// priority is ignored.
func (snm *senderNonceMempool) Insert(_ sdk.Context, tx sdk.Tx) error {
sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
if err != nil {
return err
}
if len(sigs) == 0 {
return fmt.Errorf("tx must have at least one signer")
}

sig := sigs[0]
sender := sig.PubKey.Address().String()
nonce := sig.Sequence
senderTxs, found := snm.senders[sender]
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
if !found {
senderTxs = huandu.New(huandu.Uint64)
snm.senders[sender] = senderTxs
}
senderTxs.Set(nonce, tx)

return nil
}

// Select returns an iterator ordering transactions the mempool with the lowest nonce of a random selected sender first.
func (snm *senderNonceMempool) Select(_ sdk.Context, _ [][]byte) Iterator {
var senders []string
senderCursors := make(map[string]*huandu.Element)

orderedSenders := huandu.New(huandu.String)

// #nosec
for s := range snm.senders {
orderedSenders.Set(s, s)
}
Fixed Show fixed Hide fixed
Comment on lines +97 to +99

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism

s := orderedSenders.Front()
for s != nil {
sender := s.Value.(string)
senders = append(senders, sender)
senderCursors[sender] = snm.senders[sender].Front()
s = s.Next()
}

iter := &senderNonceMepoolIterator{
senders: senders,
rnd: snm.rnd,
senderCursors: senderCursors,
}
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved

newIter := iter.Next()
if newIter == nil {
return nil
}
return newIter
JeancarloBarrios marked this conversation as resolved.
Show resolved Hide resolved
}

// CountTx returns the total count of txs in the mempool.
func (snm *senderNonceMempool) CountTx() int {
count := 0

// Disable gosec here since we need neither strong randomness nor deterministic iteration.
// #nosec
for _, value := range snm.senders {
count += value.Len()
}
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
return count
}

// Remove removes a tx from the mempool. It returns an error if the tx does not have at least one signer or the tx
// was not found in the pool.
func (snm *senderNonceMempool) Remove(tx sdk.Tx) error {
sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
if err != nil {
return err
}
if len(sigs) == 0 {
return fmt.Errorf("tx must have at least one signer")
}

sig := sigs[0]
sender := sig.PubKey.Address().String()
nonce := sig.Sequence
senderTxs, found := snm.senders[sender]
if !found {
return ErrTxNotFound
}

res := senderTxs.Remove(nonce)
if res == nil {
return ErrTxNotFound
}

if senderTxs.Len() == 0 {
delete(snm.senders, sender)
}
return nil
}

type senderNonceMepoolIterator struct {
rnd *rand.Rand
currentTx *huandu.Element
senders []string
senderCursors map[string]*huandu.Element
}

// Next returns the next iterator state which will contain a tx with the next smallest nonce of a randomly
// selected sender.
func (i *senderNonceMepoolIterator) Next() Iterator {
for len(i.senders) > 0 {
senderIndex := i.rnd.Intn(len(i.senders))
sender := i.senders[senderIndex]
senderCursor, found := i.senderCursors[sender]
if !found {
i.senders = removeAtIndex(i.senders, senderIndex)
continue
}

if senderCursor == nil {
i.senders = removeAtIndex(i.senders, senderIndex)
continue
}
kocubinski marked this conversation as resolved.
Show resolved Hide resolved

i.senderCursors[sender] = senderCursor.Next()

return &senderNonceMepoolIterator{
senders: i.senders,
currentTx: senderCursor,
rnd: i.rnd,
senderCursors: i.senderCursors,
}
}

return nil
}

func (i *senderNonceMepoolIterator) Tx() sdk.Tx {
return i.currentTx.Value.(sdk.Tx)
}

func removeAtIndex[T any](slice []T, index int) []T {
return append(slice[:index], slice[index+1:]...)
}
Loading