-
Notifications
You must be signed in to change notification settings - Fork 886
Read the EVM account nonce before taking the producer mempool lock #4181
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
Changes from all commits
01aa7ba
1517047
92c6959
41f18b3
6359821
4a98fe7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -370,6 +370,83 @@ func TestMempool_BadNonce(t *testing.T) { | |
| require.NoError(t, err) | ||
| } | ||
|
|
||
| func TestInsertTx_NewSenderUsesAppNonce(t *testing.T) { | ||
| ctx := t.Context() | ||
| rng := utils.TestRng() | ||
| app := newTestApp() | ||
| env := newTestEnv(rng, app.Cfg(), app.Proxy()) | ||
| env.alignLocalMempool() | ||
| addr, nonce := app.NewAccount(rng) | ||
|
|
||
| for _, txNonce := range []uint64{nonce, nonce + 1} { | ||
| _, err := env.state.InsertTx(ctx, env.genTx(rng, addr, txNonce).encode()) | ||
| require.NoError(t, err) | ||
| } | ||
| require.Equal(t, nonce+2, env.state.EvmNextPendingNonce(addr)) | ||
| } | ||
|
|
||
| func TestInsertTx_ConcurrentSequentialNonces(t *testing.T) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] The new tests cover the two branches that were already reachable (fresh sender uses the pre-read, tracked entry wins) but not the branch this change actually adds: the Two additions would pin the new behavior: force a prune between the pre-read and the lock (a test app whose Also note |
||
| ctx := t.Context() | ||
| rng := utils.TestRng() | ||
| app := newTestApp() | ||
| env := newTestEnv(rng, app.Cfg(), app.Proxy()) | ||
| env.alignLocalMempool() | ||
|
|
||
| const ( | ||
| accountCount = 5 | ||
| txCount = 20 | ||
| ) | ||
| type account struct { | ||
| addr common.Address | ||
| start uint64 | ||
| rng utils.Rng | ||
| } | ||
| accounts := make([]account, accountCount) | ||
| for i := range accounts { | ||
| accounts[i] = account{rng: rng.Split()} | ||
| accounts[i].addr, accounts[i].start = app.NewAccount(rng) | ||
| } | ||
|
|
||
| require.NoError(t, scope.Run(ctx, func(ctx context.Context, s scope.Scope) error { | ||
| for _, account := range accounts { | ||
| s.Spawn(func() error { | ||
| for nonce := account.start; nonce < account.start+txCount; nonce++ { | ||
| if _, err := env.state.InsertTx(ctx, env.genTx(account.rng, account.addr, nonce).encode()); err != nil { | ||
| return fmt.Errorf("InsertTx(): %w", err) | ||
| } | ||
| } | ||
| return nil | ||
| }) | ||
| } | ||
| return nil | ||
| })) | ||
|
|
||
| for _, account := range accounts { | ||
| require.Equal(t, account.start+txCount, env.state.EvmNextPendingNonce(account.addr)) | ||
| } | ||
| } | ||
|
|
||
| func TestInsertTx_BadNonceRejected(t *testing.T) { | ||
| ctx := t.Context() | ||
| rng := utils.TestRng() | ||
| app := newTestApp() | ||
| env := newTestEnv(rng, app.Cfg(), app.Proxy()) | ||
| env.alignLocalMempool() | ||
| addr, nonce := app.NewAccount(rng) | ||
|
|
||
| for _, txNonce := range []uint64{nonce - 1, nonce + 1} { | ||
| _, err := env.state.InsertTx(ctx, env.genTx(rng, addr, txNonce).encode()) | ||
| require.ErrorIs(t, err, errBadNonce) | ||
| } | ||
| _, err := env.state.InsertTx(ctx, env.genTx(rng, addr, nonce).encode()) | ||
| require.NoError(t, err) | ||
|
|
||
| for _, txNonce := range []uint64{nonce, nonce + 2} { | ||
| _, err := env.state.InsertTx(ctx, env.genTx(rng, addr, txNonce).encode()) | ||
| require.ErrorIs(t, err, errBadNonce) | ||
| } | ||
| } | ||
|
|
||
| type blockStats struct { | ||
| count uint64 | ||
| sizeBytes uint64 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] The pre-read now runs before the mempool-full check, so
TryInsertTx(waitIfFull == false) pays for an app state view on every tx it is about to reject witherrMempoolFull. Previouslyif m.IsFull() && !waitIfFull(line 352) short-circuited before anyEvmNoncecall, so a saturated mempool rejected cheaply — which is exactly the state the node is in under the load this PR targets.preReadEvmNoncealready holds the lock and can seem.IsFull(), so it could returnerrMempoolFullfor the non-blocking caller and skip the read entirely. That stays correct becauseIsFullis re-checked under the main lock anyway, so the early answer is only a fast path for a condition that is already best-effort.