3
3
package relay
4
4
5
5
import (
6
+ "fmt"
6
7
"math/big"
7
8
"net/http"
8
9
"time"
@@ -35,13 +36,14 @@ import (
35
36
// This will only work in the case of a private mempool and will not work in the P2P case where ops are
36
37
// propagated through the network and it is impossible to trust a sender's identifier.
37
38
type Relayer struct {
38
- db * badger.DB
39
- eoa * signer.EOA
40
- eth * ethclient.Client
41
- chainID * big.Int
42
- beneficiary common.Address
43
- logger logr.Logger
44
- bannedThreshold int
39
+ db * badger.DB
40
+ eoa * signer.EOA
41
+ eth * ethclient.Client
42
+ chainID * big.Int
43
+ beneficiary common.Address
44
+ logger logr.Logger
45
+ bannedThreshold int
46
+ bannedTimeWindow time.Duration
45
47
}
46
48
47
49
// New initializes a new EOA relayer for sending batches to the EntryPoint with IP throttling protection.
@@ -54,13 +56,14 @@ func New(
54
56
l logr.Logger ,
55
57
) * Relayer {
56
58
return & Relayer {
57
- db : db ,
58
- eoa : eoa ,
59
- eth : eth ,
60
- chainID : chainID ,
61
- beneficiary : beneficiary ,
62
- logger : l .WithName ("relayer" ),
63
- bannedThreshold : DefaultBanThreshold ,
59
+ db : db ,
60
+ eoa : eoa ,
61
+ eth : eth ,
62
+ chainID : chainID ,
63
+ beneficiary : beneficiary ,
64
+ logger : l .WithName ("relayer" ),
65
+ bannedThreshold : DefaultBanThreshold ,
66
+ bannedTimeWindow : DefaultBanTimeWindow ,
64
67
}
65
68
}
66
69
@@ -71,6 +74,12 @@ func (r *Relayer) SetBannedThreshold(limit int) {
71
74
r .bannedThreshold = limit
72
75
}
73
76
77
+ // SetBannedTimeWindow sets the limit for how long a banned client will be rejected for. The default value is
78
+ // 24 hours.
79
+ func (r * Relayer ) SetBannedTimeWindow (limit time.Duration ) {
80
+ r .bannedTimeWindow = limit
81
+ }
82
+
74
83
// FilterByClientID is a custom Gin middleware used to prevent requests from banned clients from adding their
75
84
// userOps to the mempool. Identifiers are prioritized by the following values:
76
85
// 1. X-Forwarded-By header: The first IP address in the array which is assumed to be the client
@@ -82,6 +91,7 @@ func (r *Relayer) FilterByClientID() gin.HandlerFunc {
82
91
l := r .logger .WithName ("filter_by_client" )
83
92
84
93
isBanned := false
94
+ var os , oi int
85
95
cid := ginutils .GetClientIPFromXFF (c )
86
96
err := r .db .View (func (txn * badger.Txn ) error {
87
97
opsSeen , opsIncluded , err := getOpsCountByClientID (txn , cid )
@@ -99,6 +109,8 @@ func (r *Relayer) FilterByClientID() gin.HandlerFunc {
99
109
}
100
110
101
111
isBanned = true
112
+ os = opsSeen
113
+ oi = opsIncluded
102
114
return nil
103
115
})
104
116
if err != nil {
@@ -110,6 +122,18 @@ func (r *Relayer) FilterByClientID() gin.HandlerFunc {
110
122
if isBanned {
111
123
l .Info ("client banned" )
112
124
c .Status (http .StatusForbidden )
125
+ c .JSON (
126
+ http .StatusForbidden ,
127
+ gin.H {
128
+ "error" : fmt .Sprintf (
129
+ "opsSeen (%d) exceeds opsIncluded (%d) by allowed threshold (%d). Wait %s to retry." ,
130
+ os ,
131
+ oi ,
132
+ r .bannedThreshold ,
133
+ r .bannedTimeWindow ,
134
+ ),
135
+ },
136
+ )
113
137
c .Abort ()
114
138
} else {
115
139
l .Info ("client ok" )
@@ -150,7 +174,7 @@ func (r *Relayer) MapUserOpHashToClientID() gin.HandlerFunc {
150
174
return err
151
175
}
152
176
153
- return incrementOpsSeenByClientID (txn , cid )
177
+ return incrementOpsSeenByClientID (txn , cid , r . bannedTimeWindow )
154
178
})
155
179
if err != nil {
156
180
l .Error (err , "map_userop_hash_to_client_id failed" )
@@ -240,7 +264,7 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc {
240
264
241
265
hashes := getUserOpHashesFromOps (ctx .EntryPoint , ctx .ChainID , ctx .Batch ... )
242
266
del = append ([]string {}, hashes ... )
243
- return incrementOpsIncludedByUserOpHashes (txn , hashes ... )
267
+ return incrementOpsIncludedByUserOpHashes (txn , r . bannedTimeWindow , hashes ... )
244
268
})
245
269
if err != nil {
246
270
return err
0 commit comments