feat(x402-e2e): concurrent commit-and-redeem scenario (20 parallel buyers)#82
Conversation
…l buyers) E0 — 20 fresh EOAs fire `fetch(/resource)` concurrently against an in-process resource server, each going through atomic commit-and-redeem. Surfaces concurrency regressions the existing single-buyer scenarios can't catch (relayer nonceManager, offer template signing, subgraph indexer under load). Three upstream tweaks were needed to land the test green: - example-resource-server: fold the per-request session id into `metadataUri` / `metadataHash` so concurrent buyers each get a distinct `predictedOfferId`. Without it, two requests within the same millisecond produce byte-identical `UnsignedFullOffer` structs and the second commit reverts `OfferSoldOut` on the single-quantity template. - example-resource-server: widen offer + voucher validity windows from 1 h to 30 d (with 1 d backdate) so they tolerate Hardhat's ~20x chain-time acceleration under interval mining. - x402-e2e/globalSetup: switch Hardhat to 50 ms interval mining right after `[stack] ready`. The default automine rejects concurrent meta-tx submissions from a single relayer with "transactions can't be queued when automining"; interval mining lets them queue and mine in batches. Also adds an `E2E_DOCKER_KEEP_STACK=1` escape hatch on globalSetup so individual scenario files can be re-run repeatedly against a stack the maintainer brought up via `pnpm stack:up` — useful for iterating without paying the full cold-start cost each run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds an x402-e2e concurrency scenario and supporting harness/example-server adjustments to make concurrent commit-and-redeem flows reliable against the local Docker stack, plus a dev “keep stack running” mode to speed up iteration.
Changes:
- Add
concurrent.test.tsscenario that runs 20 parallel buyers through the atomic commit-and-redeem (A2) path. - Update e2e
globalSetupto enable 50ms interval mining and to supportE2E_DOCKER_KEEP_STACK=1(skip pre-stop and post-teardown). - Update example resource server offer-building to salt offers with a per-request session id and widen validity windows to tolerate Hardhat time drift under interval mining.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| typescript/packages/x402-e2e/test/setup/globalSetup.ts | Adds KEEP_STACK mode and switches Hardhat from automine to interval mining to allow queued concurrent txs. |
| typescript/packages/x402-e2e/test/scenarios/concurrent.test.ts | New E0 scenario: 20 parallel buyers execute commit-and-redeem concurrently and assert on unique exchanges + REDEEMED state. |
| typescript/packages/x402-e2e/test/scenarios/_seed-wallets.ts | Adds a dedicated seed-wallet slot for the new concurrent scenario. |
| examples/resource-server/src/offer.ts | Salts metadata with sessionId to avoid identical offers under concurrency; widens offer/voucher windows. |
| examples/resource-server/src/app.ts | Plumbs sessionId into buildUnsignedOffer. |
| CLAUDE.md | Documents E2E_DOCKER_KEEP_STACK=1 workflow for iterative scenario runs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c2ef3c4
into
add-x402-e2e-post-commit-lifecycle
Summary
@p0scenario fileconcurrent.test.ts— 20 fresh EOAs firefetch(/resource)concurrently against an in-process resource server, each going through atomic commit-and-redeem (the A2 wire path). Surfaces concurrency regressions the existing single-buyer scenarios can't catch (relayernonceManager, offer-template signing under contention, subgraph indexer under load).E2E_DOCKER_KEEP_STACK=1escape hatch onglobalSetup— preserves a stack the maintainer brought up viapnpm stack:upso individual scenario files can be re-run repeatedly without paying the full cold-start cost.Upstream tweaks bundled with the test
Three concurrency limits had to be addressed for the test to land green. Each is documented inline:
example-resource-serveroffer salting —buildUnsignedOffernow folds the per-requestsessionIdintometadataUri/metadataHash. Without it, two concurrent requests within the same millisecond produce byte-identicalUnsignedFullOfferstructs and the second commit revertsOfferSoldOut()on the single-quantity template.example-resource-serverwidened validity windows — offer + voucher windows go from 1 h to 30 d (with 1 d backdate). Hardhat'sblock.timestampadvances 1 s per block; at 50 ms interval mining chain time runs ~20× wall-clock, so the prior 1 h window expired in ~3 min of suite wall-clock.x402-e2e/globalSetupinterval mining — switches Hardhat from automine to 50 ms interval mining right after[stack] ready. Default automine rejects concurrent meta-tx submissions from a single relayer with"transactions can't be queued when automining"; interval mining lets them queue and mine in batches.Run modes
Test plan
pnpm -r build— every workspace package builds cleanpnpm -r lint— zero warningspnpm format:check— cleanpnpm --filter @bosonprotocol/x402-example-resource-server test— 11/11 unit tests pass (cache + offer building still green after thesessionIdplumbing)A1,A2, newE0(concurrent),B1–B7, harness, stack — all green; stack tears down cleanly at the endE2E_DOCKER_KEEP_STACK=1, preserved stack): ran the concurrent test twice in a row against the same stack — both green, stack preserved between runsThis PR is stacked on #76 (
add-x402-e2e-post-commit-lifecycle); base auto-rebases tomainwhen that merges.🤖 Generated with Claude Code