market: emit FIFA 17's own forSale itemState, not the oracle's listFS
Single-field protocol-correctness fix, deployed as a candidate for a live A/B. `itemData.itemState: "listFS"` on the seller's own auction rows is not a FIFA 17 token at all: zero occurrences in `CardsDLL_Win64_retail.dll` (md5 4de3493131d7d2ff7f8b360c5ac9b655), zero in 4.26 GiB of live client memory, and it decodes to -1 through `FUN_180166660` — so the client was handed an unrecognised `CARD_OFFERSTATE`. FIFA 17's value for an item offered for sale is `forSale` (5), from the 12-row table at 0x180229cc0. Changed only where the invalid token was emitted: `handle_market_query` (GET …/tradePile) and `handle_market_status` (GET …/trade/status). The market search path already emitted `forSale` and is untouched — which is also why the risk here was lower than it looked: the client has been decoding `forSale` on a live route all along, and only the seller's own pile carried the bad value. Wire A/B on the same expired row: EXACTLY one field differs. tradeId, tradeState, expires, startingBid, buyNowPrice, currentBid, bidState, sellerName, sellerEstablished, watched, coinsProcessed, the twelve-atom count and the whole itemData card are byte-identical; coins unchanged at 29,843,976; Fix A's zero `inactive` rows intact. The differential asserted PARITY on this field and therefore passed while BOTH sides were wrong — the exact mechanism by which the defect survived every run. `market query tradePile` is now DIFFERENT-BY-DESIGN, pinning oracle == "listFS" and rust == "forSale" so the divergence cannot silently close again. Where the FIFA 17 binary contradicts the Python oracle, the binary wins. Gates: 126 host tests, 214 adapter tests, fmt clean, clippy clean. NOT claimed: that this preserves the list -> expire -> Return-to-Club lifecycle. That needs an operator FIFA 17 session and has NOT been observed yet. Also not claimed: anything about the Flash action gate — `CARD_OFFERSTATE` is one of three still-confounded candidates and this change does not test it. Revert is one line if the live test fails.
This commit is contained in:
@@ -51,7 +51,12 @@
|
||||
//! | market list POST /ah | PARITY | 200; `{"id":<tradeId>}` (a positive listing id). Id SPACES |
|
||||
//! | | | differ (Rust MarketStore vs oracle 900500000+seq) — a |
|
||||
//! | | | wire-insignificant server-private handle. |
|
||||
//! | market query tradePile | PARITY | after list -> `auctionInfo` len 1, `tradeState:"active"`. |
|
||||
//! | market query tradePile | DIFFERENT-BY-DESIGN| after list -> `auctionInfo` len 1, `tradeState:"active"`. |
|
||||
//! | | | ONE field diverges deliberately: `itemData.itemState`. The |
|
||||
//! | | | oracle emits `listFS`, which does not exist in FIFA 17 (0 in |
|
||||
//! | | | CardsDLL, 0 in 4.26 GiB of client memory) and decodes to -1; |
|
||||
//! | | | Rust emits `forSale` (5), the client's own token. Parity here |
|
||||
//! | | | passed while BOTH were wrong, which is why it survived. |
|
||||
//! | market buy POST /trade | DIFFERENT-BY-DESIGN| first buy debits exactly `buyNowPrice` & closes on BOTH, but |
|
||||
//! | | | Rust's MarketStore is STATEFUL single-debit (a second buy of |
|
||||
//! | | | a sold listing is a no-op: 0 delta, empty `auctionInfo`) |
|
||||
@@ -91,8 +96,8 @@
|
||||
|
||||
use openfut_adapter_fifa17::fut::catalog::Fifa17CardCatalog;
|
||||
use openfut_adapter_fifa17::fut::entities::Fifa17Entities;
|
||||
use openfut_adapter_fifa17::fut::store_session::{SessionStore, StoreMode, SENTINEL_PACK_ID};
|
||||
use openfut_adapter_fifa17::fut::non_economy::PERSONA_DISPLAY_NAME;
|
||||
use openfut_adapter_fifa17::fut::store_session::{SessionStore, StoreMode, SENTINEL_PACK_ID};
|
||||
use openfut_identity::JsonIdentityStore;
|
||||
use openfut_utas_host::async_bridge::AsyncBridge;
|
||||
use openfut_utas_host::market_store::MarketStore;
|
||||
@@ -942,15 +947,32 @@ fn run_differential(core_base: &str, oracle: &Oracle, dir: &std::path::Path) {
|
||||
"an active auction has positive seconds remaining"
|
||||
);
|
||||
// itemData must be the full shaped card on both sides; a stub cannot render.
|
||||
//
|
||||
// DELIBERATE DIVERGENCE — the one field on this route where the oracle is
|
||||
// WRONG. It stamps `listFS`, which is not a FIFA 17 token at all: zero
|
||||
// occurrences in `CardsDLL_Win64_retail.dll` (md5
|
||||
// 4de3493131d7d2ff7f8b360c5ac9b655), zero in 4.26 GiB of live client memory,
|
||||
// and it decodes to -1 through the itemState table walk, so the client is
|
||||
// handed an unrecognised `CARD_OFFERSTATE`. FIFA 17's value for an item
|
||||
// offered for sale is `forSale` (5), from the 12-row table at 0x180229cc0.
|
||||
//
|
||||
// This assertion used to demand parity, and passed while BOTH sides were
|
||||
// wrong — the reason the defect survived every differential run. Oracle parity
|
||||
// is necessary but not sufficient; where the binary contradicts the oracle,
|
||||
// the binary wins.
|
||||
assert_eq!(
|
||||
o_rec["itemData"]["itemState"], r_rec["itemData"]["itemState"],
|
||||
"own-pile itemState parity (listFS)"
|
||||
o_rec["itemData"]["itemState"], "listFS",
|
||||
"pins what the oracle actually emits, so this divergence stays visible"
|
||||
);
|
||||
assert_eq!(
|
||||
r_rec["itemData"]["itemState"], "forSale",
|
||||
"Rust emits FIFA 17's own token, not the oracle's non-existent one"
|
||||
);
|
||||
assert!(
|
||||
r_rec["itemData"]["rating"].is_i64() && r_rec["itemData"]["attributeList"].is_array(),
|
||||
"rust tradePile itemData is the full card, not a stub"
|
||||
);
|
||||
matrix.push(("market query tradePile", "PARITY"));
|
||||
matrix.push(("market query tradePile", "DIFFERENT-BY-DESIGN"));
|
||||
|
||||
// ── OP 13: market buy (POST /trade/<id>) — DIFFERENT-BY-DESIGN ─────────
|
||||
// Shared invariant: the first buy debits exactly buyNowPrice and closes the
|
||||
|
||||
Reference in New Issue
Block a user