Skip to content
This repository was archived by the owner on Oct 20, 2024. It is now read-only.

Commit 336d8ee

Browse files
authored
Update Mempool to support multiple ops per Sender (#72)
1 parent 941d00a commit 336d8ee

File tree

14 files changed

+316
-102
lines changed

14 files changed

+316
-102
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/go-logr/logr v1.2.3
1212
github.com/go-logr/zerologr v1.2.2
1313
github.com/go-playground/validator/v10 v10.11.1
14+
github.com/google/go-cmp v0.5.9
1415
github.com/mitchellh/mapstructure v1.5.0
1516
github.com/rs/zerolog v1.28.0
1617
github.com/spf13/cobra v1.6.0

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
186186
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
187187
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
188188
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
189-
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
189+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
190+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
190191
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
191192
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
192193
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=

internal/testutils/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import (
44
"math/big"
55
"time"
66

7+
"github.com/ethereum/go-ethereum/common"
78
"github.com/stackup-wallet/stackup-bundler/pkg/entrypoint"
89
)
910

1011
var (
1112
OneETH = big.NewInt(1000000000000000000)
1213
DefaultUnstakeDelaySec = uint32(86400)
14+
ValidAddress = common.HexToAddress("0x7357b8a705328FC283dF72D7Ac546895B596DC12")
1315
StakedDepositInfo = &entrypoint.IStakeManagerDepositInfo{
1416
Deposit: big.NewInt(OneETH.Int64()),
1517
Staked: true,

internal/testutils/dbmock.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package testutils
2+
3+
import (
4+
"log"
5+
6+
badger "github.com/dgraph-io/badger/v3"
7+
)
8+
9+
func DBMock() *badger.DB {
10+
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true).WithLoggingLevel(badger.ERROR))
11+
if err != nil {
12+
log.Fatal(err)
13+
}
14+
15+
return db
16+
}

internal/testutils/opmock.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package testutils
22

33
import (
4+
"math/big"
5+
46
"github.com/ethereum/go-ethereum/common"
7+
"github.com/google/go-cmp/cmp"
58
"github.com/stackup-wallet/stackup-bundler/pkg/userop"
69
)
710

@@ -27,3 +30,19 @@ func MockValidInitUserOp() *userop.UserOperation {
2730
op, _ := userop.New(MockUserOpData)
2831
return op
2932
}
33+
34+
func IsOpsEqual(op1 *userop.UserOperation, op2 *userop.UserOperation) bool {
35+
return cmp.Equal(
36+
op1,
37+
op2,
38+
cmp.Comparer(func(a *big.Int, b *big.Int) bool { return a.Cmp(b) == 0 }),
39+
)
40+
}
41+
42+
func GetOpsDiff(op1 *userop.UserOperation, op2 *userop.UserOperation) string {
43+
return cmp.Diff(
44+
op1,
45+
op2,
46+
cmp.Comparer(func(a *big.Int, b *big.Int) bool { return a.Cmp(b) == 0 }),
47+
)
48+
}

pkg/bundler/bundler.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/stackup-wallet/stackup-bundler/pkg/mempool"
1212
"github.com/stackup-wallet/stackup-bundler/pkg/modules"
1313
"github.com/stackup-wallet/stackup-bundler/pkg/modules/noop"
14+
"github.com/stackup-wallet/stackup-bundler/pkg/userop"
1415
)
1516

1617
// Bundler controls the end to end process of creating a batch of UserOperations from the mempool and sending
@@ -72,8 +73,9 @@ func (i *Bundler) Run() error {
7273
continue
7374
}
7475

75-
senders := append(getSenders(ctx.Batch), getSenders(ctx.PendingRemoval)...)
76-
if err := i.mempool.RemoveOps(ep, senders...); err != nil {
76+
rmOps := append([]*userop.UserOperation{}, ctx.Batch...)
77+
rmOps = append(rmOps, ctx.PendingRemoval...)
78+
if err := i.mempool.RemoveOps(ep, rmOps...); err != nil {
7779
l.Error(err, "bundler run error")
7880
continue
7981
}

pkg/bundler/senders.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

pkg/client/client.go

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -85,33 +85,33 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error)
8585
// 1. the nonce remains the same
8686
// 2. the new maxPriorityFeePerGas is higher
8787
// 3. the new maxFeePerGas is increased equally
88-
memOp, err := i.mempool.GetOp(epAddr, userOp.Sender)
89-
if err != nil {
90-
l.Error(err, "eth_sendUserOperation error")
91-
return "", err
92-
}
93-
if memOp != nil {
94-
if memOp.Nonce.Cmp(memOp.Nonce) != 0 {
95-
err := errors.New("sender: Has userOp in mempool with a different nonce")
96-
l.Error(err, "eth_sendUserOperation error")
97-
return "", err
98-
}
99-
100-
if memOp.MaxPriorityFeePerGas.Cmp(memOp.MaxPriorityFeePerGas) <= 0 {
101-
err := errors.New("sender: Has userOp in mempool with same or higher priority fee")
102-
l.Error(err, "eth_sendUserOperation error")
103-
return "", err
104-
}
105-
106-
diff := big.NewInt(0)
107-
mf := big.NewInt(0)
108-
diff.Sub(memOp.MaxPriorityFeePerGas, memOp.MaxPriorityFeePerGas)
109-
if memOp.MaxFeePerGas.Cmp(mf.Add(memOp.MaxFeePerGas, diff)) != 0 {
110-
err := errors.New("sender: Replaced userOp must have an equally higher max fee")
111-
l.Error(err, "eth_sendUserOperation error")
112-
return "", err
113-
}
114-
}
88+
// memOp, err := i.mempool.GetOp(epAddr, userOp.Sender)
89+
// if err != nil {
90+
// l.Error(err, "eth_sendUserOperation error")
91+
// return "", err
92+
// }
93+
// if memOp != nil {
94+
// if memOp.Nonce.Cmp(memOp.Nonce) != 0 {
95+
// err := errors.New("sender: Has userOp in mempool with a different nonce")
96+
// l.Error(err, "eth_sendUserOperation error")
97+
// return "", err
98+
// }
99+
100+
// if memOp.MaxPriorityFeePerGas.Cmp(memOp.MaxPriorityFeePerGas) <= 0 {
101+
// err := errors.New("sender: Has userOp in mempool with same or higher priority fee")
102+
// l.Error(err, "eth_sendUserOperation error")
103+
// return "", err
104+
// }
105+
106+
// diff := big.NewInt(0)
107+
// mf := big.NewInt(0)
108+
// diff.Sub(memOp.MaxPriorityFeePerGas, memOp.MaxPriorityFeePerGas)
109+
// if memOp.MaxFeePerGas.Cmp(mf.Add(memOp.MaxFeePerGas, diff)) != 0 {
110+
// err := errors.New("sender: Replaced userOp must have an equally higher max fee")
111+
// l.Error(err, "eth_sendUserOperation error")
112+
// return "", err
113+
// }
114+
// }
115115

116116
// Run through client module stack.
117117
ctx := modules.NewUserOpHandlerContext(userOp, epAddr, i.chainID)

pkg/mempool/db.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package mempool
22

33
import (
44
"encoding/json"
5+
"math/big"
56
"strings"
67

78
badger "github.com/dgraph-io/badger/v3"
@@ -12,16 +13,15 @@ import (
1213
const keySeparator = ":"
1314
const keyPrefix = "mempool"
1415

15-
func getDBKey(entryPoint common.Address, sender common.Address) []byte {
16-
return []byte(keyPrefix + keySeparator + entryPoint.String() + keySeparator + sender.String())
16+
func getUniqueKey(entryPoint common.Address, sender common.Address, nonce *big.Int) []byte {
17+
return []byte(
18+
keyPrefix + keySeparator + entryPoint.String() + keySeparator + sender.String() + keySeparator + nonce.String(),
19+
)
1720
}
1821

19-
func getEntryPointAndSenderFromDBKey(key []byte) (common.Address, common.Address) {
22+
func getEntryPointFromDBKey(key []byte) common.Address {
2023
slc := strings.Split(string(key), keySeparator)
21-
ep := common.HexToAddress(slc[1])
22-
sender := common.HexToAddress(slc[2])
23-
24-
return ep, sender
24+
return common.HexToAddress(slc[1])
2525
}
2626

2727
func getUserOpFromDBValue(value []byte) (*userop.UserOperation, error) {
@@ -48,7 +48,7 @@ func loadFromDisk(db *badger.DB, q *userOpQueues) error {
4848

4949
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
5050
item := it.Item()
51-
ep, _ := getEntryPointAndSenderFromDBKey(item.Key())
51+
ep := getEntryPointFromDBKey(item.Key())
5252

5353
err := item.Value(func(v []byte) error {
5454
op, err := getUserOpFromDBValue(v)

pkg/mempool/instance.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,22 @@ func New(db *badger.DB) (*Mempool, error) {
2727
return &Mempool{db, queue}, nil
2828
}
2929

30-
// GetOp checks if a UserOperation is in the mempool and returns it.
31-
func (m *Mempool) GetOp(entryPoint common.Address, sender common.Address) (*userop.UserOperation, error) {
32-
op := m.queue.GetOp(entryPoint, sender)
33-
return op, nil
30+
// GetOps returns all the UserOperations associated with an EntryPoint and Sender address.
31+
func (m *Mempool) GetOps(entryPoint common.Address, sender common.Address) ([]*userop.UserOperation, error) {
32+
ops := m.queue.GetOps(entryPoint, sender)
33+
return ops, nil
3434
}
3535

36-
// AddOp adds a UserOperation to the mempool.
36+
// AddOp adds a UserOperation to the mempool or replace an existing one with the same EntryPoint, Sender, and
37+
// Nonce values.
3738
func (m *Mempool) AddOp(entryPoint common.Address, op *userop.UserOperation) error {
3839
data, err := op.MarshalJSON()
3940
if err != nil {
4041
return err
4142
}
4243

4344
err = m.db.Update(func(txn *badger.Txn) error {
44-
return txn.Set(getDBKey(entryPoint, op.Sender), data)
45+
return txn.Set(getUniqueKey(entryPoint, op.Sender, op.Nonce), data)
4546
})
4647
if err != nil {
4748
return err
@@ -51,16 +52,16 @@ func (m *Mempool) AddOp(entryPoint common.Address, op *userop.UserOperation) err
5152
return nil
5253
}
5354

54-
// BundleOps builds a bundle of ops from the mempool to be sent to the EntryPoint.
55+
// BundleOps builds a bundle of UserOperations from the mempool to be sent to the EntryPoint.
5556
func (m *Mempool) BundleOps(entryPoint common.Address) ([]*userop.UserOperation, error) {
5657
return m.queue.Next(entryPoint), nil
5758
}
5859

59-
// RemoveOps removes a list of UserOperations from the mempool by sender address.
60-
func (m *Mempool) RemoveOps(entryPoint common.Address, senders ...common.Address) error {
60+
// RemoveOps removes a list of UserOperations from the mempool by EntryPoint, Sender, and Nonce values.
61+
func (m *Mempool) RemoveOps(entryPoint common.Address, ops ...*userop.UserOperation) error {
6162
err := m.db.Update(func(txn *badger.Txn) error {
62-
for _, s := range senders {
63-
err := txn.Delete(getDBKey(entryPoint, s))
63+
for _, op := range ops {
64+
err := txn.Delete(getUniqueKey(entryPoint, op.Sender, op.Nonce))
6465
if err != nil {
6566
return err
6667
}
@@ -72,6 +73,6 @@ func (m *Mempool) RemoveOps(entryPoint common.Address, senders ...common.Address
7273
return err
7374
}
7475

75-
m.queue.RemoveOps(entryPoint, senders)
76+
m.queue.RemoveOps(entryPoint, ops...)
7677
return nil
7778
}

0 commit comments

Comments
 (0)