Skip to content

Commit 4c19301

Browse files
authored
feat(fix-nonce-gap): add support to pending nonce (#728)
* fix-nonce-gap: add support to pending nonce * small fixes * move mitm script to script folder
1 parent 1d8aaa7 commit 4c19301

File tree

2 files changed

+68
-7
lines changed

2 files changed

+68
-7
lines changed

cmd/fixnoncegap/fixnoncegap.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,12 @@ func fixNonceGap(cmd *cobra.Command, args []string) error {
115115
var maxNonce uint64
116116
if inputFixNonceGapArgs.maxNonce != 0 {
117117
maxNonce = inputFixNonceGapArgs.maxNonce
118+
log.Info().Uint64("maxNonce", maxNonce).Msg("maxNonce loaded from --max-nonce flag")
118119
} else {
119-
maxNonce, err = getMaxNonceFromTxPool(addr)
120+
log.Info().Msg("--max-nonce flag not set")
121+
maxNonce, err = getMaxNonce(addr)
120122
if err != nil {
121-
if strings.Contains(err.Error(), "the method txpool_content does not exist/is not available") {
122-
log.Error().Err(err).Msg("The RPC doesn't provide access to txpool_content, please check --help for more information about --max-nonce")
123-
return nil
124-
}
125-
log.Error().Err(err).Msg("Unable to get max nonce from txpool")
123+
log.Error().Err(err).Msg("Unable to get max nonce, please check --help for more information about --max-nonce")
126124
return err
127125
}
128126
}
@@ -132,7 +130,11 @@ func fixNonceGap(cmd *cobra.Command, args []string) error {
132130
log.Info().Stringer("addr", addr).Msg("There is no nonce gap.")
133131
return nil
134132
}
135-
log.Info().Stringer("addr", addr).Msgf("Nonce gap found. Max nonce: %d", maxNonce)
133+
log.Info().
134+
Stringer("addr", addr).
135+
Uint64("currentNonce", currentNonce).
136+
Uint64("maxNonce", maxNonce).
137+
Msg("Nonce gap found")
136138

137139
gasPrice, err := rpcClient.SuggestGasPrice(cmd.Context())
138140
if err != nil {
@@ -273,6 +275,26 @@ func WaitMineTransaction(ctx context.Context, client *ethclient.Client, tx *type
273275
}
274276
}
275277

278+
func getMaxNonce(addr common.Address) (uint64, error) {
279+
log.Info().Msg("getting max nonce from txpool_content")
280+
maxNonce, err := getMaxNonceFromTxPool(addr)
281+
if err == nil {
282+
log.Info().Uint64("maxNonce", maxNonce).Msg("maxNonce loaded from txpool_content")
283+
return maxNonce, nil
284+
}
285+
log.Warn().Err(err).Msg("unable to get max nonce from txpool_content")
286+
287+
log.Info().Msg("getting max nonce from pending nonce")
288+
// if the error is not about txpool_content, falls back to PendingNonceAt
289+
maxNonce, err = rpcClient.PendingNonceAt(context.Background(), addr)
290+
if err != nil {
291+
return 0, err
292+
}
293+
294+
log.Info().Uint64("maxNonce", maxNonce).Msg("maxNonce loaded from pending nonce")
295+
return maxNonce, nil
296+
}
297+
276298
func getMaxNonceFromTxPool(addr common.Address) (uint64, error) {
277299
var result PoolContent
278300
err := rpcClient.Client().Call(&result, "txpool_content")
@@ -299,6 +321,7 @@ func getMaxNonceFromTxPool(addr common.Address) (uint64, error) {
299321
nonceInt, ok := new(big.Int).SetString(nonce, 10)
300322
if !ok {
301323
err = fmt.Errorf("invalid nonce found: %s", nonce)
324+
log.Warn().Err(err).Msg("unable to get txpool_content")
302325
return 0, err
303326
}
304327

scripts/mitm/fake_mitm_override.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# fake_mitm_override_fixed.py
2+
# this is a mitm proxy script to be used to fake certain JSON-RPC responses from an Ethereum node
3+
import json, time
4+
from mitmproxy import http
5+
6+
def _serialize_params(p):
7+
if p is None:
8+
return None
9+
return json.dumps(p, separators=(",", ":"), sort_keys=True)
10+
11+
# keys: (method, params_json_or_None)
12+
OVERRIDES = {
13+
("txpool_content", None): {"error": {"code": -32601, "message": "Method not found"}},
14+
# ("txpool_content", None): {"jsonrpc": "2.0","id": 1,"result": {"pending": {"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {"40": {}}}},
15+
# ("eth_getTransactionCount", _serialize_params(["0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "latest"])): {"jsonrpc": "2.0", "id": 1, "result": "0xa"},
16+
("eth_getTransactionCount", _serialize_params(["0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "pending"])): {"jsonrpc": "2.0", "id": 1, "result": "0x100"},
17+
}
18+
19+
def request(flow: http.HTTPFlow):
20+
body_text = flow.request.get_text()
21+
try:
22+
j = json.loads(body_text)
23+
method = j.get("method")
24+
params = j.get("params")
25+
req_id = j.get("id")
26+
print(f"[{time.time():.3f}] REQ method={method} id={req_id} params={_serialize_params(params)}")
27+
except Exception:
28+
# malformed JSON: log truncated raw body and let it pass
29+
print(f"[{time.time():.3f}] REQ malformed body: {body_text[:200]!r}")
30+
return
31+
32+
key_exact = (method, _serialize_params(params))
33+
key_any = (method, None)
34+
override = OVERRIDES.get(key_exact) or OVERRIDES.get(key_any)
35+
if override is not None:
36+
resp = {"jsonrpc": "2.0", "id": req_id}
37+
resp.update(override)
38+
flow.response = http.Response.make(200, json.dumps(resp), {"Content-Type": "application/json"})

0 commit comments

Comments
 (0)