Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,34 @@ describe("useCreateMarket fresh batched registration", () => {
// keeper-register now writes the markets row, so THE REGISTRATION CALL is
// what must come after M3a. It used to be created before M2, which was safe
// only while it wrote nothing but the keeper's blob.
//
// #2464 TIGHTENED THIS. The call used to be STARTED here and awaited later,
// so it was in flight — and could have completed — while the insurance check
// was still deciding whether the launch had failed. It is now a thunk
// (`startKeeperRegister`) INVOKED after that check, so assert on the
// invocation, which is the moment the request actually leaves.
const m3a = freshBatchSource.indexOf("const m3aSig = await broadcastTailTx(2)");
const register = freshBatchSource.indexOf("const keeperRegisterPromise");
const register = freshBatchSource.indexOf("await startKeeperRegister()");
expect(m3a).toBeGreaterThanOrEqual(0);
expect(register).toBeGreaterThan(m3a);

// And the point of #2464: it must also come after the insurance verification,
// so a launch that throws there publishes nothing.
const insuranceGate = freshBatchSource.indexOf("Insurance fund was not seeded");
expect(insuranceGate).toBeGreaterThanOrEqual(0);
expect(register).toBeGreaterThan(insuranceGate);
});

it("keeps keeper-registration before M4, where marketauth still works", () => {
// Deliberately NOT deferred like the DB registration: StakeInitPool (in M4)
// rotates marketauth away from the deployer, and keeper-register's H1 check
// requires marketauth to still equal the deployer. Pinning the order stops
// someone "fixing" the asymmetry and silently breaking keeper registration.
const keeper = freshBatchSource.indexOf("const keeperRegisterPromise");
//
// #2464 narrowed the window from "after M3a" to "after the insurance check",
// but this upper bound is unchanged and is the reason it could not move any
// later. Asserting on the INVOCATION, not the thunk's declaration.
const keeper = freshBatchSource.indexOf("await startKeeperRegister()");
const m4 = freshBatchSource.indexOf("const m4Sig = await broadcastTailTx(4)");
expect(keeper).toBeGreaterThanOrEqual(0);
expect(m4).toBeGreaterThan(keeper);
Expand Down
24 changes: 22 additions & 2 deletions app/hooks/useCreateMarket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1434,7 +1434,24 @@ async function attemptFreshBatchedLaunch(ctx: FreshBatchContext): Promise<FreshB
// from the deployer, and keeper-register's H1 check requires marketauth to
// still equal the deployer. After M3a and before M4 is the only window that
// satisfies both constraints.
const keeperRegisterPromise: Promise<KeeperRegisterOutcome> =
// #2464: this used to be STARTED here and awaited after the insurance check,
// so the registration request was already in flight — and could already have
// completed — while that check was still deciding whether the launch had
// failed. A launch that threw on an unseeded insurance fund could therefore
// leave the market listed and keeper-registered anyway.
//
// It is now a THUNK, invoked below only once insurance has verified. The
// window it must run in is unchanged and still satisfied: after M3a (so a
// market is only listed once it holds collateral) and before M4 (whose
// StakeInitPool rotates marketauth away from the deployer, which
// keeper-register's H1 check requires).
//
// The cost is latency: the round-trip no longer overlaps M3b's broadcast, so
// the tail sits a little longer against the shared blockhash. That is the
// right trade — the blockhash-expiry path already REBUILDS a not-yet-landed
// tail tx (see TailTxDescriptor), whereas an incorrectly published market has
// no equivalent undo.
const startKeeperRegister = (): Promise<KeeperRegisterOutcome> =>
(isKeeperOracle && params.dexPoolAddress && keeperProofSignature)
? registerMarketWithKeeper(
{ publicKey: walletPk, signMessage: wallet.signMessage },
Expand Down Expand Up @@ -1504,7 +1521,10 @@ async function attemptFreshBatchedLaunch(ctx: FreshBatchContext): Promise<FreshB
}
updateInFlightStep(slabPk.toBase58(), 4);

const keeperOutcome = await keeperRegisterPromise;
// #2464: started HERE, not above — everything that can fail the launch has
// now run, so no externally visible registration happens for a launch that is
// about to be reported as failed.
const keeperOutcome = await startKeeperRegister();

const m4Sig = await broadcastTailTx(4);
advanceLanding(m4Sig);
Expand Down
Loading