Skip to content

Add Docker USDT payment and bridge fixtures - #25

Merged
ovitrif merged 7 commits into
mainfrom
feat/usdt-e2e-fixture
Oct 12, 2026
Merged

ovitrif merged 7 commits into
mainfrom
feat/usdt-e2e-fixture

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Run USDT payment and bridge tests through an opt-in usdt Docker Compose profile. Separate containers run a pinned Arbitrum fork, Alto, a local paymaster signer, bridge-provider fixtures and the real Bitkit USDT gateway. A containerized Core wallet checks execution and proofs. Existing Bitcoin, Lightning, staging Pubky, VSS and rate services retain their configuration.

./usdt-fixture build/up/check/smoke/bridge-smoke/down manages an isolated project. Source revisions, images and lockfiles are pinned; no host Node or Rust is required. The gateway uses service main commit 3d8c0b625b02f0731ff0a19910752961ada3e6f2, which includes merged synonymdev/bitkit-usdt-service#4. Only the gateway and controls are exposed on host loopback. The upstream RPC credential is a read-only secret, and its proxy blocks public-chain writes.

The normal fixture needs only archive-state reads available on Alchemy Free. Anvil paces and caches upstream reads and retries throttling; pre-fork mainnet logs are excluded. Locally mined payment history and recovery remain real. The monthly provider quota still applies; no paid-mode toggle is needed.

Controls cover test funding, held inclusion, authorization failures, bridge quotes, deposits, delivery/refund states and outages. Incoming Arbitrum credits and outgoing refunds execute real token transfers. Orchestra orders require source funding; LayerZero status uses an actual OFTSent receipt. The optional Shop overlay reaches the same fork through a private network. It pairs the denomination-aware seller setup in pubky/pubky-marketplace#167 with the per-rail verifier in pubky/locks#75. App startup preserves the RPC sidecar namespace, serializes heavy builds and bounds the Shop Node heap.

README and docs/usdt-fixture.md contain setup and scenario commands. Companion PR: synonymdev/bitkit-e2e-tests#276. Most added lines are generated npm/Cargo lockfiles.

Validation:

  • Built the pinned service and fixture images on Linux ARM64; fixture lint and all eight protocol/state tests passed. The wallet passed fmt/build/clippy. Nine warnings come from byte-identical pinned Core dependency sources; none are introduced by this PR update.
  • Direct-payment smoke passed first-use EIP-7702 execution without ETH, subsequent payment, exact token/fee deltas, pending inclusion across wallet reopen, incoming history, same-seed recovery into an empty database, proof verification and invalid authorization checks.
  • Bridge smoke passed incoming credit, requested external refund, Orchestra funding/completion, Arbitrum refund and USDT0 source execution with LayerZero attention/delivery, including duplicate settlement checks.
  • Compose configuration and shell syntax passed. Lifecycle checks cover repeated startup, reset, private secret permissions, read-only proof inputs and Shop RPC connectivity/write rejection.
  • Cold startup, direct/history/recovery and bridge smoke suites passed behind a guard enforcing 300 CU/s, a 10-block log limit and rejection of paid-only APIs. Forced and throughput HTTP 429 responses recovered. No upstream log or paid-only calls occurred. Validation used the existing endpoint with enforced limits, not an actual Free account.
  • Container recreation reused the fork cache: warm startup needed 15 upstream requests and no contract-code/storage reads. The E2E launcher and funding/mining helpers passed against this revision.
  • Shop seller setup and authenticated Locks readiness passed; the helper created a BTC-priced content lock and the Android buyer received 20 local test USDT. The paid checkout/order/unlock test was not completed: the required listing image upload was skipped.
  • Full Shop rebuild remains limited by the current 8 GB Docker VM. An isolated 3 GB-heap Shop build passed, but the full stack exceeded memory during TypeScript checking; smaller heaps failed compilation. A full wrapper rerun needs more Docker memory. The existing stack serves readiness and read-only Arbitrum RPC; no full restart pass is claimed.
  • Android was exercised locally for direct receive/send/details, restart and incoming Polygon bridge states. Full automated mobile, VSS restore and paid Shop journeys remain for Piotr to add.

Anvil executes deployed contracts with local balances and authorization. Pimlico's hosted pricing/signing, external deposit observation, operator delivery/refunds and LayerZero relaying are simulated. Nitro finality, production bundler policy and live liquidity require separate acceptance. CI needs Docker; AMD64 is configured but runtime validation here used ARM64.

@ben-kaufman ben-kaufman changed the title Add local USDT execution infrastructure for E2E tests Add Docker USDT payment and bridge fixtures Oct 11, 2026
@ben-kaufman
ben-kaufman marked this pull request as ready for review October 11, 2026 05:19

@talosmachina talosmachina left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No findings. Adds an opt-in usdt Compose profile: an Arbitrum fork with Alto, a local paymaster signer, Orchestra and LayerZero provider fixtures, the pinned USDT gateway and a containerized Core wallet. It also adds a Shop overlay that points Paykit Server's verifier at the same fork, and moves the shop-order pins to the denomination-aware Locks and Shop heads. Reviewed 13dab48, full tier.

What I checked, and 7 candidates I ruled out

Read in full: usdt-fixture, usdt/*.cjs, usdt/Dockerfile, usdt/shop-compose.yml, usdt/wallet/src/main.rs, the usdt and shop-order services in docker-compose.yml, shop-order, marketplace/shop-order/lock.py. Lockfiles not reviewed.
Cross-repo contracts checked: Paykit Server 662dca0 takes [usdt] rpc_url with deny_unknown_fields and allows plain HTTP only on localhost, so the sidecar on 127.0.0.1:23450 inside its namespace fits. Its verifier calls only eth_chainId, eth_blockNumber, eth_getBlockByNumber and eth_getTransactionReceipt, all on the read proxy's allowlist. The Shop at af2866f and lock.py both call setup-status?asset=BTC, which is the pairing with pubky/locks#75. The Shop Dockerfile declares NODE_MAX_OLD_SPACE_SIZE, so the new build arg takes effect.
Ran here: npm ci, biome check (clean) and node --test (7/7 pass) in usdt/, with a stripped environment.
Not run: the Compose stack, smoke and bridge-smoke. They need an archive Arbitrum RPC that this reviewer does not have.
CI: no checks reported on this branch.

Ruled out

  • Concurrent fund calls corrupt the donor balance: seed overwrites with read-plus-amount, so one top-up can be lost. The donor starts at 1,000,000 USDT, so neither transfer can underflow.
  • Two concurrent funded() lookups create two orders: the second if (record.order) after the receipt await returns the first order.
  • A failed settlement poisons the record: the rejected record.effect is kept on purpose, and the docs say so.
  • The control server binds 0.0.0.0: this is needed for the published port, which is loopback-only on the host, and the docs treat containers on the fixture network as trusted.
  • --no-deps on the app start skips dependencies: postgres, Paykit and the tunnels are already up from the earlier up steps.
  • A missing USDT_TEST_ENV_FILE breaks smoke/wallet: the secret falls back to /dev/null, and usdt-tools neither mounts it nor recreates the fixture.
  • The new shop-order defaults are open-PR heads: the earlier pin was an open PR head too, and the docs say to keep the pair together.

Merge confidence: 4/5, the Compose stack and both smoke suites need an archive RPC and were not run here, and the paid Shop checkout on the overlay is untested.

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the fixture setup and pushed a fix in e233cbe: the shared :local image tags could let another checkout overwrite the images used by an existing stack, even with a different USDT_PROJECT. The launcher now tags images using a fingerprint of the build inputs.

Fixture unit tests and lint passed. Full stack and mobile testing are still pending. From my review, it looks like running the local fork requires an Arbitrum archive RPC provider—which one did you use for testing?

@ben-kaufman

Copy link
Copy Markdown
Contributor Author

Thanks Piotr, both fixes make sense. We used Alchemy’s Arbitrum archive RPC, configured through USDT_TEST_ENV_FILE. It supplies the fork’s chain data; payments execute locally with test funds. I checked the endpoint against both apps and reran the peer tests successfully.

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA review

Re-reviewed changes since 13dab48, including affected code paths and the previous image-tag finding, at 9e32a22. Unchanged infrastructure coverage is retained from the earlier full review.

No new actionable code findings in the follow-up. The previous image-tag issue is fixed.

Validation: reran container lint and all eight fixture tests successfully, plus shell syntax and diff checks. Piotr completed local startup, payment smoke and bridge smoke using an actual Alchemy endpoint; the payment smoke exited successfully and the bridge result reported passed. The history smoke checks incoming transfers and same-seed recovery into an empty database.

The fixture intentionally excludes pre-fork mainnet history. Mobile, VSS restore and paid Shop acceptance remain separate; full Shop rebuild and paid checkout are not validated here. No GitHub checks are reported at this revision.

Approved as local USDT testing infrastructure.

@ovitrif
ovitrif enabled auto-merge (squash) October 12, 2026 15:52
@ovitrif
ovitrif disabled auto-merge October 12, 2026 15:53
@ovitrif
ovitrif merged commit b36f8ac into main Oct 12, 2026
@ovitrif
ovitrif deleted the feat/usdt-e2e-fixture branch October 12, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants