dex: bound the GPU book indices, and let a market sell actually cross

The C matcher takes the book pointer and the indices but no book LENGTH, so it
cannot bounds-check, and the Go wrapper did not either. MatchOrderCPU guards the
same field because indices genuinely can be out of range — one field, two
readers, one guard. On a real Metal device that wrote past the end of a Go slice
and answered 420 where the oracle answered 490.

An empty book is the same hole from the other side: &book[0] panicked where the
CPU path returned cleanly, so one build was total and the other fatal on
identical input. Both cases hand off to the oracle rather than restate its bounds
rule here; a second copy of a bounds check is a second chance to get it wrong.

The shadow gate gave every market order the MAXIMAL price, "so it crosses every
resting level". That is true for a market BUY, which sits on the bid and crosses
any ask at or below it. A market SELL sits on the ask and crosses any bid at or
above it, and no bid is ever above +inf — so every market sell matched nothing,
compared empty against empty, and was counted VERIFIED. The mechanism the GPU
path is assured by was measuring nothing for an entire order type, while an
operator watched Verified climb and Divergences stay at zero.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-21 01:18:02 -07:00
co-authored by Hanzo Dev
parent 2e25239bd7
commit 247b110b9d
2 changed files with 37 additions and 1 deletions
+15 -1
View File
@@ -247,7 +247,21 @@ func orderToFlatTaker(order *Order) DEXOrder {
}
t.Remaining = t.Quantity
if order.Type == Market {
t.Price = DEXPrice{Integer: ^uint64(0), Fraction: ^uint64(0)}
// A market order crosses every resting level, and WHICH price does that
// depends on the side. A market BUY sits on the bid and crosses any ask at
// or below it, so the maximal price sweeps the book. A market SELL sits on
// the ask and crosses any bid at or above it — the maximal price crosses
// NOTHING, because no bid is ever above +inf.
//
// Given the maximum for both, every market sell matched nothing in the
// shadow, compared empty against empty, and was counted VERIFIED. An
// operator watching Verified climb with Divergences at zero could not see
// that an entire order type was being waved through unchecked.
if t.Side == sideToDEX(Sell) {
t.Price = DEXPrice{Integer: 0, Fraction: 0}
} else {
t.Price = DEXPrice{Integer: ^uint64(0), Fraction: ^uint64(0)}
}
} else {
t.Price = priceRowFor(orderPriceUnits(order))
}
+22
View File
@@ -112,6 +112,28 @@ func gpuMatchOrder(
if len(bookIndices) == 0 {
return nil, incoming.Quantity, nil
}
// The C matcher takes the indices and the book pointer but NO book length,
// so it cannot bounds-check and neither did this wrapper. MatchOrderCPU
// guards the same field (orderbook_match_cpu.go: `if int(idx) >= len(book)`)
// because indices genuinely can be out of range — one field, two readers, and
// only one of them checked. On a real device that wrote past the end of a Go
// slice and answered 420 where the CPU answered 490.
//
// An empty book is the same hole seen from the other side: &book[0] panics
// where the CPU path returns cleanly, so one build is total and the other is
// fatal on identical input. Hand both to the oracle rather than reproduce its
// guard here — a second copy of a bounds rule is a second chance to get it
// wrong.
if len(book) == 0 {
t, r := MatchOrderCPU(incoming, book, bookIndices, tradeIDBase, timestamp)
return t, r, nil
}
for _, idx := range bookIndices {
if uint64(idx) >= uint64(len(book)) {
t, r := MatchOrderCPU(incoming, book, bookIndices, tradeIDBase, timestamp)
return t, r, nil
}
}
tradesOut := make([]DEXTrade, len(bookIndices))
var (