Files
zeekayandHanzo Dev 996677759a One request the edge permits was a thousand questions it did not
A price is a fan-out. The edge limits requests — thirty a second from one
address — and each may name fifty tokens, read at two sizes against two
numéraires across every arm. Nothing bounded what came out the other side, and
what comes out lands on public endpoints we hold no account with; being refused
there takes quoting down for everybody, on every chain.

So the budget is sixteen questions in flight against one chain for the whole
process, rather than eight per request multiplied by however many requests
arrive. It is taken around a single reading and not around a whole price,
because a price against the hub waits for the hub's own price, and a slot held
across that wait deadlocks the first time every slot is held by something
waiting on the hub.

Measured: one request of 50 cold Ethereum tokens went from 10.1s to 8.6s, and
four chains of 40 at once from 10.7s to 9.3s — and returned the same counts on
a repeat, where the same burst before this returned a different set of prices
each time.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-09 12:31:14 -07:00

2404 lines
96 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LX - Complete Architecture Documentation
## Executive Summary
LX is a planet-scale, fully on-chain decentralized exchange built on the Lux Network. It combines ultra-low latency matching (<1μs), quantum-resistant security, and a design target of supporting all global markets (784,000+ trading pairs — a coverage goal, not a measured benchmark) in a single unified system.
**Measured performance (Apple M1 Max, first-hand):**
- **Throughput**: 11.88M orders/sec (C++ engine, 10 threads); 2.2M orders/sec (pure Go `pkg/lx`)
- **Latency**: 169 ns avg match (p50 125 ns, p99 292 ns) on C++; 381 ns/order in pure Go; 49 ns cancel
- **Allocations**: 1–2 allocs/op on the Go matching path (not zero-alloc)
- **GPU**: the default (CGO-off) build matches on CPU; a GPU-native deterministic per-book matcher (byte-identical to the CPU oracle, parity-verified) is available with CGO_ENABLED=1 via the unified `lux-gpu` backend (runtime-select CUDA/HIP/Metal), sustaining up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10); kernels ship prebuilt from luxcpp/dex. GPU also drives FHE (Metal NTT, 23.6× at N=4096, batch=128)
- **CI/CD**: Fully automated with GitHub Actions
## Key Innovations
1. **Full On-Chain Architecture**: Unlike competitors (High-performance DEX, dYdX) that match off-chain, LX runs the entire orderbook and clearinghouse directly on-chain with 1ms block finality
2. **Planet-Scale Capacity** (design target): 5M markets simultaneously per node — a capacity goal, not a measured benchmark
3. **Quantum-Resistant**: QZMQ protocol with post-quantum cryptography for node communication
4. **Multi-Engine Performance**: pure Go 2.2M orders/sec (381 ns/order), C++ 11.88M orders/sec (10 threads, 169 ns avg match) — CPU default build. A GPU-native deterministic per-book matcher (parity-verified GPU==CPU, CGO_ENABLED=1, unified `lux-gpu` backend) sustains up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10). GPU also drives FHE (Metal NTT 23.6×)
5. **Universal Protocol Support**: JSON-RPC, gRPC, WebSocket, QZMQ, ZAP (HFT)
## On-Chain Settlement: D matches · C settles (0x9999)
The DEX matching engine runs on the **D-Chain** (`dexvm`) and does not move EVM
balances directly. Settlement uses a strict **D matches · C settles** split:
- **D-Chain** matches the order, emits a `DFillReceipt`, and a D-validator quorum
BLS-signs the receipt root.
- **C-Chain** precompile `0x9999` (Uniswap-V4 `PoolManager` ABI, receipt-settlement
only — **not** a matcher) verifies that certificate **inline** (BLS verify is
deterministic → fork-safe), debits/credits balances, and marks the receipt
consumed. Settlement is Block-STM-parallel with fine-grained keys (no global hot
slots). The verify path NEVER calls a live matcher.
- `certType` (BLS day-1 → Q / PQ / ZK) upgrades via an on-chain verifier registry
with no ABI change. `0x9010` is **deprecated** (alias/disabled, same impl +
`dex.precompile.v1.9999.*` namespace).
> Slogan: **D matches · C settles · BLS certifies day-1 · Q later · X finalizes.**
> Normative spec: **LP-9999** (`~/work/lux/lps/LPs/lp-9999-dex-v4-receipt-settlement-precompile.md`).
> This file summarizes; it does not duplicate the spec.
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────┐
│ LX ARCHITECTURE │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SDKs: TypeScript | Python | Go | Rust | Java │ │
│ │ Protocols: JSON-RPC | gRPC | WebSocket | REST │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ API Gateway Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Load Balancer | Rate Limiter | Auth | Caching │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Matching Engine Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Pure Go | Hybrid C++ | GPU/MLX | Auto-Selection │ │
│ │ OrderBook | Risk Engine | Settlement | Clearing │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Consensus Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ FPC Consensus | K=3 Validators | 1ms Finality │ │
│ │ QZMQ Network | Post-Quantum Crypto | DAG Backend │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Storage Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ BadgerDB | State Tree | Block Archive | Snapshots │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Core Components
### 1. Matching Engine (`pkg/lx/`)
**Two-tier matching architecture (CPU):**
- **Pure Go** (`pkg/lx`): 2.2M orders/sec, 381 ns/order, 1–2 allocs/op
- **C++/CGO**: 11.88M orders/sec (10 threads), 5.91M single-thread, 169 ns avg match (p50 125 ns, p99 292 ns), 49 ns cancel
**Key files:**
- `orderbook.go`: Core order book implementation
- `orderbook_advanced.go`: Advanced order types (iceberg, stop, bracket)
- `orderbook_cgo.go`: C++ bridge for performance
- `matching_engine.go`: Order matching logic
### 2. API Layer
#### JSON-RPC (`pkg/api/jsonrpc.go`)
Standard JSON-RPC 2.0 implementation for HTTP clients:
- Place/cancel orders
- Market data queries
- Account management
#### gRPC (`pkg/grpc/server.go`)
High-performance binary protocol:
- Streaming order books
- Real-time trades
- Bulk operations
#### WebSocket (`pkg/websocket/server.go`)
Real-time market data streaming:
- Order book updates
- Trade feed
- OHLCV candles
#### ZAP (`pkg/api/zap_server.go`) - NEW
Ultra-low-latency binary protocol for HFT:
- Zero-copy order placement/cancellation
- Binary wire format (64-byte fixed orders)
- Sub-100μs round-trip latency
- Low allocation on hot path (see benchmark below)
**Wire Protocol:**
```
Order (64 bytes):
[0:8] symbol (8 bytes, null-padded)
[8:16] order_id (uint64, big-endian)
[16:24] price (float64, IEEE 754)
[24:32] size (float64, IEEE 754)
[32] side (uint8: 0=buy, 1=sell)
[33] order_type (uint8: 0=limit, 1=market)
[34:36] flags (uint16: post-only, reduce-only, STP)
[36:44] timestamp (uint64, unix nanos)
[44:60] user_id (16 bytes)
[60:64] padding
Quote (24 bytes):
[0:8] price (float64)
[8:16] size (float64)
[16:20] count (uint32)
[20:24] padding
```
**Benchmark Results (Apple M1 Max):**
```
BenchmarkZAPOrderPlacement ~42μs/op 5.7KB/op 24 allocs
BenchmarkZAPBestBid ~48μs/op 5.7KB/op 18 allocs
BenchmarkZAPRoundTrip ~44μs/op 4.9KB/op 10 allocs
```
**Usage:**
```go
// Server
server := api.NewZAPServer(orderBook, ":9000", logger)
server.Start(ctx)
// Client
client, _ := rpc.Dial(ctx, "localhost:9000")
resp, _ := client.CallRaw(ctx, "place_order", orderPayload)
resp, _ := client.CallRaw(ctx, "best_bid", nil)
resp, _ := client.CallRaw(ctx, "orderbook", depthReq)
```
### 3. Consensus Integration
#### QZMQ (`pkg/qzmq/qzmq.go`)
Quantum-resistant ZeroMQ for node communication:
- Post-quantum key exchange
- Encrypted order propagation
- Consensus messaging
#### Integration (`cmd/luxd/qzmq_integration.go`)
- Order propagation between nodes
- Trade confirmation broadcasting
- State synchronization
- Block proposals
### 4. Market Data (`pkg/marketdata/`)
Real-time OHLCV aggregation:
- 19 time intervals (1s to 1M)
- Volume-weighted calculations
- Technical indicators (RSI, MA, VWAP)
### 5. SDKs
#### TypeScript (`sdk/typescript/`)
```typescript
const client = new LXDexClient();
const order = await client.placeOrder({
symbol: "BTC-USD",
type: OrderType.LIMIT,
side: OrderSide.BUY,
price: 50000,
size: 0.1
});
```
#### Python (`sdk/python/`)
```python
client = LXDexClient()
order = client.place_order(
symbol="BTC-USD",
order_type=OrderType.LIMIT,
side=OrderSide.BUY,
price=50000,
size=0.1
)
```
#### Go (`sdk/go/`)
```go
client := client.NewClient()
order, _ := client.PlaceOrder(ctx, &Order{
Symbol: "BTC-USD",
Type: OrderTypeLimit,
Side: OrderSideBuy,
Price: 50000,
Size: 0.1,
})
```
## Performance Characteristics
### Latency Benchmarks
- **Order Matching**: 169 ns avg (C++ engine), 381 ns (pure Go)
- **Consensus Round**: <1ms
- **Block Finality**: 1ms (1000 blocks/sec)
- **End-to-end Trade**: <5ms
### Throughput Capacity
- **Orders/sec**: 11.88M (C++, 10 threads); 2.2M (pure Go) — CPU default build. GPU-native per-book matcher (CGO_ENABLED=1, `lux-gpu`, parity-verified GPU==CPU): up to 12.76B (AMD 8060S) / 9.13B (GB10)
- **Trades/sec**: 10M+ (design target — no benchmark backing)
- **Markets**: 5M simultaneous (design target — no benchmark backing)
- **Connections**: 1M+ WebSocket (design target — no benchmark backing)
### Memory Requirements
- **Development**: 16GB (top 100 pairs)
- **Production**: 64GB (all crypto)
- **Global Scale**: 128-512GB (all markets)
## Deployment Options
### Single Node Development
```bash
./bin/luxd \
--enable-mlx \
--block-time 1ms \
--http-port 8080 \
--ws-port 8081
```
### 3-Node Cluster (K=3)
```bash
# Node 1
./bin/luxd --node-id 1 --enable-qzmq --qzmq-pub-port 5555
# Node 2
./bin/luxd --node-id 2 --enable-qzmq --qzmq-pub-port 5565
# Node 3
./bin/luxd --node-id 3 --enable-qzmq --qzmq-pub-port 5575
```
### Docker Deployment
```bash
make up # Starts complete stack with monitoring
```
## The trading gateway (`cmd/gateway`, `pkg/gateway`)
One image, two binaries. `dexd` is the D-Chain venue; `gateway` is the HTTP
trading surface. They share the venue types, which is why they ship together.
**Everything answers under `/v1/trade`, and `api/openapi.yaml` describes exactly
that.** `pkg/gateway.Server.routes()` is the surface written once;
`TestTheDocumentIsTheSurface` reads the document and holds the two to each
other, in both directions, so a route added in code and not written down fails
the build and so does a path promised in the document and not served.
Ten paths, and every one of them answers:
GET /v1/trade/venues what this deployment can price, per chain
GET /v1/trade/tokens what it can trade there, and their decimals
GET /v1/trade/price what those tokens are worth, in dollars
POST /v1/trade/quote the best price across the venues that hold the pair
POST /v1/trade/quotes every venue's price, best first
POST /v1/trade/swap the transaction that takes that price
POST /v1/trade/approval/check what the spender may already move
POST /v1/trade/approval/build an unsigned ERC-20 approve
POST /v1/trade/permit2/check whether a live Permit2 grant covers it
POST /v1/trade/permit2/build a Permit2 grant, as a tx or as typed data
That prefix replaced three names for one API: `/v1/*` here, `/trading/*` beside
it in Uniswap's shapes, and a published openapi.yaml describing a third set that
`pkg/trading` implemented and no binary ever mounted. `pkg/trading` was a second
copy of the venue interface, the constant-product math and the eth_call client;
it is gone, along with the `/trading/*` handlers and their types.
**Every chain is read from its own pools.** Ours through the V4 precompiles with
a V2 router beside them where one is deployed; every other chain through the V2
Router02 and V3 QuoterV2 Uniswap published there, over JSON-RPC, with no API key
and no upstream account. The hosted trade API answers ACCESS_DENIED without a
contract, so `uniswap.NewProvider` is registered ONLY when `UNISWAP_API_KEY` is
set — a provider that always refuses turns a pair with no pool into an error
about somebody else's quota.
Environment: `GATEWAY_ADDR` (:8080), `LUX_RPC`, `LUX_CHAIN_ID` (96369),
`LUX_V2_ROUTER`, `LUX_V3_QUOTER`, `LUX_V3_ROUTER`, `RPC_<chainid>` per chain,
`GATEWAY_CACHE_TTL` (seconds), `UNISWAP_API_KEY` (optional). Chains with venues: 1, 10, 56, 137,
8453, 42161, 96369.
`GET /v1/trade/venues` is the question a screen asks before it asks anything
else — which chains this deployment can price, and from what. It is grouped by
chain because a venue is a contract on a chain, and `native` marks the ones we
settle ourselves. Measured against 96369 with `LUX_V3_QUOTER` set:
96369 native [v4_native, uniswap_v3]
1 [uniswap_v2, uniswap_v3]
10, 56, 137, 8453, 42161 the same
⚠ **The `v4_native` arm on 96369 is configured and silent.** `0x9010` answers
`0x` there — measured with the venue's own quoter selector — so the arm quotes
nothing and every 96369 price today comes from `uniswap_v3`, reading the
QuoterV2 the venue deployment put on that chain. `native: true` says the arm is
attached, not that it answered; a quote is what tells you. It starts answering
the day the precompile does, with no configuration change.
What 96369 actually carries, from luxfi/standard `deployments/venue/96369.json`:
V3Factory 0x6E5884084814F4785e21f6f9C11D3C299Ef5d7f1
Quoter (V2) 0x6EA2F79f743C809D1152657Ef5d5907f47a34cfc LUX_V3_QUOTER
Router (02) 0xbbD8d9A1E6bf5627A2F6D2aF38664bbe1cEF63Bb LUX_V3_ROUTER
Positions 0xd5350c2f13772f126F942b152BaeFee943df4e5a
WLUX 0x66dbed9572eb9be45ef05526c65beddf8d60a672
Its `Router` key is the V3 SwapRouter, NOT a V2 Router02 — `factory()` answers
the V3 factory and `WETH()` reverts — so it belongs to `LUX_V3_ROUTER` and
never to `LUX_V2_ROUTER`. The 12 V2 pairs on that chain hold reserves and
become quotable when a Router02 is deployed, not before.
Prices measured through this surface, one whole unit in, on every configured
chain:
96369 WLUX -> LUSD 997001483075056712 uniswap_v3 tier 3000
1 WETH -> USDC 2517103282 uniswap_v3 tier 100
8453 WETH -> USDC 2519069935 uniswap_v3 tier 500
42161 WETH -> USDC 2519534429 uniswap_v3 tier 500
10 WETH -> USDC 2509863629 uniswap_v3 tier 500
137 WPOL -> USDC 99582 uniswap_v3 tier 500
56 WBNB -> USDT 755677742862096479703 uniswap_v3 tier 100
### What a chain lists, and what it is worth
A screen cannot draw a market until it knows a token's name, symbol and
decimals, and every chain but ours has no indexer behind it. So the list travels
in the binary: `pkg/gateway/tokens.json`, 935 tokens over the seven chains this
gateway prices, embedded and served with no upstream, no key and no round trip.
Identity only — address, chainId, symbol, name, decimals.
**It carries no logo, and the struct has nowhere to put one.** Public token
lists give logos as URLs on `assets.coingecko.com`, `coin-images.coingecko.com`,
`raw.githubusercontent.com` and `s2.coinmarketcap.com`: three companies fetched
by every viewer's browser on every market screen. Marks come from
`cdn.lux.cloud`, and a token with no mark there draws its own letters.
`TestTheEmbeddedListIsWellFormed` refuses any key that is not one of the five.
The file is the boundary, so it is checked at build time rather than per
request: valid addresses, decimals in range, no duplicates, no control
characters in a symbol that something is going to render.
`GET /v1/trade/price?chainId=&token=` prices one token or fifty, answered in the
order asked. There is no oracle behind it and no vendor — it is a reading of the
same pools `/v1/trade/quote` reads, and it is the same number:
spend some dollars of a numéraire → see what comes back → put the fee back
Dollars rather than one whole token, because one whole token is a fifty-cent
trade in one market and a hundred-thousand-dollar trade in the next, and the
second one moves the pool it is measuring — priced that way SHIB came out 40%
high. The fee correction matters as much: a pool hands back the mid price LESS
its tier, so an uncorrected reading is high by exactly that tier — $1.0031 for a
$1.00 token on a 0.3% pool, and $3018.51 for a $3000 one two hops deep.
Two numéraires per chain (`Numeraires` in `venue_chains.go`): the dollar
stablecoin, worth one by construction, and the wrapped native token, whose own
mark is read against the dollar. Most V3 liquidity is against the native token,
not the dollar, so a gateway that knew only the dollar could price the majors
and nothing behind them. Every route is read and the cheapest wins — a price is
what somebody actually receives, and the route handing over the most tokens is
nearest the middle of the market.
**Twice, ten times apart.** A pool with almost nothing left in it still answers,
and the implied price of a rounding error comes out in the millions. So the
pools are asked at $10 and at $100 and the two best readings are compared; more
than 2% between them and nothing is returned. Measured over the fifty tokens
Ethereum lists first:
one reading 50 asked, 44 priced, half of them >1% off, AUCTION at
$889,434,176 for a token worth $3.44
both readings 50 asked, 27 priced, 25 of 27 within 2%
The two that remain apart — ASM +621%, AGLD +122% — are real pools here that
sit off the wider market. That number is not an error: `/v1/trade/quote` for the
same size implies $0.036806 for ASM where `/v1/trade/price` says $0.036834, so
the screen and the swap panel agree, and agreeing with each other is what
matters. Gating each numéraire separately was tried and is wrong: it refuses the
cheapest route for being thin and then quotes a dearer one that was not, which
is a price nobody would have paid. The gate is on the best reading at each size.
Cross-chain, same minute, against an independent source:
1 WETH 2487.03 -0.09% 8453 cbETH 2831.86 -0.17%
1 WBTC 78414.92 -0.01% 42161 ARB 0.154 -0.16%
1 UNI 6.608 -0.22% 10 OP 0.103 -0.23%
1 AAVE 128.59 +0.05% 137 WMATIC 0.0965 -0.04%
1 PEPE 3.6e-6 -0.32% 56 WBNB 742.72 +0.46%
1 SHIB 5.37e-6 +0.07%
**A token no pool holds — or holds too thinly to price — comes back WITHOUT
`priceUSD`, never with a zero.** A screen can draw a dash; it cannot un-draw a
wrong number. `venue` and `via` go missing with it, because a number nobody can
trace is a number nobody can check.
**The bound is on what leaves, not on what arrives.** The edge limits requests —
thirty a second from one address — and each may name fifty tokens read at two
sizes against two numéraires across every arm, so one permitted request becomes
up to a thousand eth_calls landing on public endpoints we hold no account with.
`readingsInFlight` is sixteen questions in flight against ONE chain for the
whole process, not per request, so a second screen shares them instead of
doubling them.
Cost, measured: 50 cold Ethereum tokens in 8.6s, the same 50 warm in 36ms, and
160 tokens across four chains at once in 9.3s — and the same counts on a repeat
run, where before the bound a four-chain burst returned a different set of
prices each time. The two sizes are asked concurrently, so the second one costs
calls and not latency.
A reading stands for a minute (`markTTL`) and `asOf` says when it was taken; a
chain that could not be read is remembered for ten seconds, not a minute,
because the pools have not changed — our access to them has. Fifty tokens per
request and eight readings in flight, so one HTTP request cannot turn into five
hundred eth_calls against an endpoint we hold no account with. Thirty-two
readers arriving for the same cold price produce ONE fan-out, not thirty-two.
**One unit for a fee, everywhere.** Hundredths of a basis point — how V3 states
a tier, how `SwapOrder.Fee` is read, and how a price puts back what a pool took.
The V2 arm answered `30` where the V3 arm answered `3000` for the same three
tenths of a percent; `feeFromBPS` converts once, in `venue.go`, and nothing else
means anything else by that number.
**The native arm no longer hides a chain it cannot reach.** Every other arm
separates a reverting contract from an endpoint that never answered, and the
router reports a chain it could not read only when EVERY arm on it failed — so
one arm answering "no liquidity" to an unreachable endpoint was enough on its
own to turn an unreadable chain into a pair nobody holds.
### The venue that quoted is the venue that builds
`/v1/trade/swap` asked the hosted provider registry for a price — which this
deployment has none of, so it answered 500 while `/v1/trade/quote` priced the
same pair from the pools a line away — and then encoded whatever came back for
the PoolManager precompile at `0x9010`, regardless of where the price came
from. A router on Ethereum quoted and a precompile on our chain addressed, in
one response. `0x9010` answers `0x` on 96369, measured with the venue's own
quoter selector, so the calldata named a contract that is not there.
`Venue` has a `Swap` method now (`venue_swap.go`). Each venue builds against
the contract it read: `UniswapV3Venue` against the SwapRouter02 beside its
quoter, `UniswapV2Venue` against the same Router02 that answered
`getAmountsOut`. A venue that prices a market it cannot settle returns nil, and
the surface answers 404 naming it rather than calldata for something else.
**The selector was wrong, and it is the kind of wrong that spends money.**
`calldata_lxrouter.go` sent `04e45aaf` under a signature it called
`ExactInputSingle(address,address,uint256,uint256,uint160,bytes32)`. That
signature hashes to `f48f227e`. `04e45aaf` is SwapRouter02's
`exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))` —
a SEVEN-field tuple. A router handed the six-field form reads `amountIn` where
it expects the fee tier and `amountOutMinimum` where it expects the recipient.
Those builders were reachable only from their own tests; what survives is the
padding and decoding, in `abi.go`.
Proof, end to end, against the live chain — the gateway's own calldata replayed
through `eth_call` on 96369's real router from the account that holds the
allowance:
POST /v1/trade/swap 1 WLUX -> LUSD, slippage 0.5
to 0xbbD8d9A1E6bf5627A2F6D2aF38664bbe1cEF63Bb (SwapRouter02)
fee 3000 the tier the quote was read from
minOut 992016475589681300 99.5% of the quote
eth_call that calldata, from 0x9011E8…, latest
-> 997001483075056712
the gateway quoted 997001483075056712
**The floor is not optional.** A router handed the quoted amount as its
`amountOutMinimum` reverts on any movement at all, including the movement the
caller's own trade causes. `slippage` is a percentage; zero or nonsense means
half a percent.
It is computed in hundredths of a basis point — the unit a V3 fee tier is
already stated in. In whole basis points a tolerance finer than 0.01%
truncated to nothing, so `slippage: 0.005` produced a floor EQUAL to the quote:
a request for very tight protection answered with none at all, and a
transaction that reverts on the first wei of movement. Measured on 96369, 1
WLUX into LUSD quoting 997001483075056712:
slippage 0.005 -> 996951633000902959 99.9950%
slippage 0.5 -> 992016475659681428 99.5000%
**The tier travels with the quote.** A V3 quote asks all four tiers and keeps
the best, so it is a reading of ONE pool. The winning tier rides on
`route[0].fee` and the swap names the same one — without it the transaction
executes against a different pool at a different price.
**The deadline is honoured on every arm.** SwapRouter02 dropped the field from
`exactInputSingle`'s tuple and takes it through `multicall(uint256,bytes[])`
instead, which is how Uniswap's own interface sends one; the V2 router takes it
directly. Without the wrap a deadline was accepted and silently dropped on
every V3 route while V2 honoured it — one field meaning two things depending on
which pool won the quote. `amountOutMinimum` bounds the price, the deadline
bounds the time, and a signed swap that lands an hour late still passes its
floor at a price nobody would choose now.
Both directions measured against 96369's live router:
deadline ahead of block.timestamp -> 997001483075056712, the quoted amount
deadline behind it -> execution reverted: Transaction too old
⚠ **96369's clock is not wall clock.** It seals a block when there is a
transaction, so `block.timestamp` was 4709 seconds behind wall clock while this
was measured. A deadline computed from wall clock is generous here rather than
tight, which is the safe direction — but a test that expects `now - 60` to be
expired will not see it expire.
**A dead endpoint is not an empty pool.** Both used to come out as an empty
list, so WETH/USDC on Ethereum — one of the deepest pools in existence — was
reported as a pair nobody holds whenever its RPC was down, which sends a reader
looking for liquidity instead of at their endpoint. `EVMClient` marks the
answers it never received with `ErrUnreachable` (a connection failure, a 5xx, a
429, a body that is not JSON-RPC) and the venues pass that through, while a
contract answering by REVERTING stays what it always was: this pool does not
exist. Three of the four V3 tiers revert on most pairs; that is the ordinary
case and must not read as an outage.
`QueryAllVenues` returns the reason only when EVERY arm it asked failed — one
arm down while another answers is a quote. Measured, with chain 1's endpoint
pointed at a closed port:
chain 1 502 chain 1 could not be read: endpoint unreachable: dial tcp …
chain 96369 200 997001483075056712
a pair with no pool on 96369
404 no venue here holds this pair
/providers 200 one dark chain does not take a pod out of rotation
**A direction is not a missing market.** `getAmountsOut` and
`quoteExactInputSingle` price a known INPUT and have no other direction, so an
exact-output request gets nothing back however deep the pool is. Answering that
with "no venue here holds this pair" sends someone looking for liquidity that
is sitting right there; it now says which of the two it was, and what to ask
instead.
### The allowance is read, not assumed
`/v1/trade/approval/check` and `/v1/trade/permit2/check` return what the chain
says, over the same endpoint the pool is quoted from — `Server.read` in
`server_approval.go`. Both used to answer `allowance: "0", needsApproval: true`
on every request, under a comment claiming the gateway had no way to read a
chain; it has one per chain, one field away. The cost of that guess was not a
wrong number on a screen: a wallet told it needs an approval for a token it has
already approved sends an approve transaction, and the user pays gas for a state
change that changes nothing, before every swap, forever.
Permit2 answers `(amount, expiration, nonce)` and the expiration is half the
answer — a grant that has run out is not a grant. The unsigned tx and the
EIP-712 request ride along ONLY when one is needed; handed back beside "you
already approved" they are transactions somebody sends.
**Permit2 is at one address everywhere it exists, and it does not exist
everywhere.** `0x000000000022D473030F116dDEE9F6B43aC78BA3` holds 9152 bytes on
Ethereum and nothing on 96369. Both paths check before answering, so our own
chain gets `permit2 is not deployed on chain 96369 — approve the spender
directly` instead of calldata addressed to an empty account. A transaction that
costs gas to do nothing is the failure worse than an error.
### Three things to know before deploying it
**The image is amd64 and says so nowhere but the Dockerfile.** `GOARCH=amd64` is
written out in the build, and what is published is a single OCI manifest, not an
index — so a kubelet on an arm64 node pulls it, believes it, and the container
exits with `exec format error`. Where a cluster is mixed, the workload states
`nodeSelector: kubernetes.io/arch: amd64`. Where binfmt happens to be registered
the failure is worse than a crash: the container runs, `uname -m` inside it says
x86_64, and a matching engine executes under instruction emulation.
**`runAsNonRoot` needs the uid, not the name.** The image declares `USER dexd`;
a kubelet cannot check a string against runAsNonRoot and refuses the container
with `image has non-numeric user (dexd)`. State `runAsUser: 1000`.
**`/healthz` is not a readiness probe until it has a source.** It answers from
the venues and the hosted provider registry together; before that it read the
registry alone, so the ordinary configuration — no hosted provider, pools as the
source — answered 503 forever and any probe on it held every replica out of its
own Service. `/providers` is the narrower question (does the mux answer) and is
what the lux-mainnet deployment probes.
### What is deliberately not here
There is no `/v1/admin/*`. It paused the DEX, paused a pool and froze one on the
strength of `X-User-Role: admin` — a header the caller writes — and drove state
nothing read: `PauseState` had no caller outside its own file, per replica, lost
on restart. A switch wired to nothing is worse than no switch. The real gate is
`checkPauseState` in the pool manager, on chain, where a market is.
### What this surface is not, and where each of those lives instead
Five paths answered 500 `no providers available` in the deployment that
actually runs, and four more built transactions for contracts that are not
there. A path in a published contract that cannot work is worse than a missing
one: a client writes against it and finds out in production.
| gone | why | where it lives |
|---|---|---|
| `/v1/tokens` `/v1/pools` `/v1/pool/` `/v1/positions` `/v1/stats` `/v1/price` `/v1/prices` | enumerating what exists on a chain is an INDEX read; this reads a pool by address, right now, and cannot walk every pool on Ethereum | `api-explore.lux.cloud` — measured returning 16 pools, $39.9M TVL, real symbols and decimals for 96369 |
| `/v1/route` | the same read in a hat: needs a pool graph, and without one answered `{"routes":[]}` forever, which reads as "no path exists" rather than "I cannot answer" | same |
| `/v1/order` `/v1/order/` | an in-memory map behind two replicas: an order placed on one pod was a 404 on the other about half the time, and every order died on restart | the D-Chain venue IS the order book — `dex_place`, `dex_get_orders`, `dex_get_book` over ZAP, deployed as `dex.yaml` |
| `/v1/position` `/v1/position/{increase,decrease,claim}` | every builder addressed `0x9010`, which answers `0x`; liquidity on 96369 is managed through the position manager the venue deployment actually put there | not here |
`TestWhatThisSurfaceIsNot` keeps them gone and records the reason beside each.
**Nothing here invents a number.** `/v1/history/prices` and `/v1/history/tvl`
returned a random walk seeded from hardcoded base prices — LUX at 2.47, WBTC at
96420, 12.5M of TVL on 96369 — and both were routed to the public internet. A
screen drawing that chart shows a market that does not exist, and on the wire it
is indistinguishable from one that does. Price history belongs to the indexer,
which reads the chain: `api-explore.lux.cloud`. `/v1/tokens/search` answered 501
on a TODO, which is a route lying about the surface in the other direction. All
three are gone, and `TestNothingHereInventsANumber` keeps them gone.
## API Endpoints
### JSON-RPC Methods
- `lx_placeOrder`: Place new order
- `lx_cancelOrder`: Cancel existing order
- `lx_getOrderBook`: Get order book snapshot
- `lx_getTrades`: Get recent trades
- `lx_getOrder`: Get order details
- `lx_getBestBid`: Get best bid price
- `lx_getBestAsk`: Get best ask price
- `lx_getInfo`: Get node information
### WebSocket Channels
- `orderbook:{symbol}`: Order book updates
- `trades:{symbol}`: Trade feed
- `candles:{symbol}:{interval}`: OHLCV updates
### gRPC Services
- `PlaceOrder`: Unary order placement
- `CancelOrder`: Unary order cancellation
- `StreamOrderBook`: Server streaming order book
- `StreamTrades`: Server streaming trades
## Order Types
### Basic Orders
- **Limit**: Price-specific order
- **Market**: Immediate execution
- **Stop**: Trigger at price
- **Stop-Limit**: Stop that becomes limit
### Advanced Orders
- **Iceberg**: Hidden quantity
- **Peg**: Track best bid/ask
- **Bracket**: Entry with stop-loss/take-profit
- **Trailing Stop**: Dynamic stop price
### Time in Force
- **GTC**: Good Till Cancelled
- **IOC**: Immediate Or Cancel
- **FOK**: Fill Or Kill
- **DAY**: Day order
## Security Features
### Quantum Resistance
- Post-quantum key exchange (Kyber)
- Quantum-resistant signatures (Dilithium)
- SHA3 hashing
- QZMQ encrypted messaging
### Consensus Security
- BFT with K=3 validators
- Deterministic finality
- No rollbacks after 1ms
- Slashing for misbehavior
### Application Security
- Self-trade prevention
- Position limits
- Rate limiting
- API key authentication
## Monitoring & Observability
### Prometheus Metrics
- `lx_orders_processed_total`: Total orders
- `lx_trades_executed_total`: Total trades
- `lx_matching_latency_seconds`: Matching latency
- `lx_consensus_latency_seconds`: Consensus latency
- `lx_block_height`: Current block height
### Health Endpoints
- `/health`: Node health status
- `/metrics`: Prometheus metrics
- `/debug/pprof`: Go profiling
## Development Workflow
### Go toolchain policy
Every Dockerfile stage that compiles Go must (1) pin its base to the latest
stable patch — currently `1.26.5`, never below the go.mod `go` directive — and
(2) set `ENV GOTOOLCHAIN=auto`.
The official `golang` images ship `GOTOOLCHAIN=local`, which turns "base older
than the governing `go` directive" into a hard failure —
`go: go.mod requires go >= X (running go Y; GOTOOLCHAIN=local)` — instead of
fetching the needed toolchain. This is not hypothetical here:
`sdk/lx-trading-go/go.mod` declares `go 1.26.5`, above the root module's
`1.26.4`, so a submodule build under `GOTOOLCHAIN=local` dies at exactly that.
The root `go` directive stays at `1.26.4` — raising it pushes a hard
patch-level floor onto every consumer. Building an older directive with a newer
toolchain is always valid. `docs/Dockerfile` is exempt (no Go).
### Building
```bash
# Build all components
make build
# Build with GPU support
CGO_ENABLED=1 make build-gpu
# Run tests
make test
# Run benchmarks
make bench
```
### Testing
```bash
# Unit tests
go test ./pkg/...
# Integration tests
go test ./test/integration/...
# E2E tests with Docker
make docker-test
```
### Contributing
1. Fork repository
2. Create feature branch
3. Write tests
4. Implement feature
5. Run linters
6. Submit PR
## Configuration
### Environment Variables
```bash
LXD_DATA_DIR=~/.lxd
LXD_LOG_LEVEL=info
LXD_HTTP_PORT=8080
LXD_WS_PORT=8081
LXD_ENABLE_MLX=true
LXD_BLOCK_TIME=1ms
```
### Config File (`config.yaml`)
```yaml
node:
id: 1
data_dir: ~/.lxd
network:
http_port: 8080
ws_port: 8081
consensus:
block_time: 1ms
validators: 3
engine:
type: hybrid
max_batch: 10000
```
## Troubleshooting
### Common Issues
1. **Port already in use**
```bash
lsof -i :8080
kill -9 <PID>
```
2. **MLX not available**
- Ensure Apple Silicon Mac
- Update MLX framework
- Fallback to CPU mode
3. **QZMQ connection failed**
- Check firewall rules
- Verify ZeroMQ installation
- Check network connectivity
## Roadmap
### Q1 2025
- [ ] Production mainnet launch
- [ ] Cross-chain bridges
- [ ] Mobile SDKs
- [ ] Hardware wallet support
### Q2 2025
- [ ] Derivatives trading
- [ ] Options markets
- [ ] Lending/borrowing
- [ ] Staking rewards
### Q3 2025
- [ ] Institutional APIs
- [ ] Regulatory compliance
- [ ] Fiat on/off ramps
- [ ] Multi-region deployment
### Q4 2025
- [ ] AI-powered trading
- [ ] Social trading features
- [ ] Copy trading
- [ ] Strategy marketplace
## Performance Optimizations
### Memory Management
1. **Lock-Free Data Structures**
- Atomic operations for concurrent access
- RCU (Read-Copy-Update) patterns for orderbook updates
- Memory pools to eliminate GC pressure
2. **Low-Allocation Design**
- Pre-allocated order pools
- Reusable message buffers
- Fixed-point arithmetic (7 decimal precision)
- Measured: 1–2 allocs/op on the Go matching path (not zero)
3. **Buffer Management**
- Ring buffers for order queues
- Memory-mapped files for persistence
- NUMA-aware memory allocation
### Network Optimizations
1. **Kernel Bypass**
- DPDK/XDP support for packet processing
- Zero-copy networking with io_uring
- TCP_NODELAY for minimal latency
2. **Protocol Efficiency**
- Binary FIX: 60-byte fixed messages
- Custom binary formats for internal communication
- Multicast for market data distribution
3. **Connection Management**
- Connection pooling
- Persistent WebSocket connections
- Graceful degradation under load
### Algorithm Optimizations
1. **Order Matching**
- Red-black trees for price levels
- Heap-based priority queues for orders
- O(log n) insertion and deletion
2. **GPU Acceleration**
- Metal NTT kernel (FHE): 23.6× over CPU NTT (N=4096, batch=128)
- GPU-native deterministic per-book matcher (CGO_ENABLED=1, unified `lux-gpu` backend, runtime-select CUDA/HIP/Metal; byte-identical to the CPU oracle, parity-verified): up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10); kernels ship prebuilt from luxcpp/dex
- The default (CGO-off) build matches on CPU: a single hot-book 169 ns match is smaller than integrated-GPU dispatch overhead, so the CPU wins the single-book hot path; the GPU wins at planet scale (one thread per book across millions of books)
3. **Caching Strategy**
- L1: Hot orders in CPU cache
- L2: Active price levels in memory
- L3: Historical data in BadgerDB
### Benchmarking Results
| Engine | Operation | Throughput | Latency |
|--------|-----------|------------|---------|
| Pure Go (`pkg/lx`) | Match order | 2.2M orders/sec | 381 ns (1–2 allocs/op) |
| C++ (single thread) | Match order | 5.91M orders/sec | 169 ns avg (p50 125, p99 292) |
| C++ (10 threads) | Match order | 11.88M orders/sec | — |
| C++ | Cancel order | — | 49 ns |
| GPU per-book (`lux-gpu`, CGO=1) | Match order (parity-verified GPU==CPU) | 12.76B (AMD 8060S) / 9.13B (GB10) / 5.60B (M4 Max) / 2.80B (M1 Max); 21.9B two-node | — |
| GPU (Metal) | FHE NTT (N=4096, batch=128) | 23.6× vs CPU | — |
| Consensus | Block finality | 1000/sec | 1 ms |
### Production Deployment
1. **Hardware Requirements**
- Minimum: 8 cores, 32GB RAM, NVMe SSD
- Recommended: 32 cores, 128GB RAM, Optane SSD
- Optimal: Mac Studio M2 Ultra, 512GB RAM
2. **Scaling Strategy**
- Horizontal: Multiple nodes for different markets
- Vertical: more cores / NUMA-aware C++ engine for hot markets
- GPU: FHE (Metal NTT) plus the GPU-native per-book matcher (CGO_ENABLED=1, unified `lux-gpu` backend, parity-verified GPU==CPU) for planet-scale fan-out across millions of books
3. **Monitoring & Metrics**
- Prometheus for metrics collection
- Grafana for visualization
- Custom dashboards for order flow analysis
## Testing & Quality Assurance
### Test Coverage Status (January 22, 2025)
- **pkg/lx**: 39.1% coverage (all tests passing)
- **pkg/mlx**: 96.2% coverage (comprehensive MLX testing)
- **Integration Tests**: 100% passing
- **Performance Tests**: Exceeding all targets by 4.34x
- **Market Data Tests**: Alpaca and professional feeds tested
- **X-Chain Tests**: Settlement and clearing validated
### Test Categories
1. **Unit Tests**: Individual component validation
2. **Integration Tests**: Multi-component workflows
3. **Performance Tests**: Throughput and latency benchmarks
4. **Stress Tests**: High-load scenarios
5. **Chaos Tests**: Failure recovery validation
### CI/CD Pipeline
- GitHub Actions for automated testing
- Docker containerization for deployment
- Semantic versioning with automated releases
- Multi-platform binary builds (Linux, macOS, Windows)
## References
- [Lux Network Documentation](https://docs.lux.network)
- [FPC Consensus Paper](https://arxiv.org/fpc-consensus)
- [Post-Quantum Cryptography](https://csrc.nist.gov/pqc)
- [MLX Framework](https://github.com/ml-explore/mlx)
## Production Infrastructure
### Kubernetes Deployment
The LX includes comprehensive Kubernetes manifests for production deployment:
#### Production Environment (`k8s/production/`)
- **StatefulSet**: 3-node cluster with anti-affinity rules
- **Services**: LoadBalancer for HTTP/WS, ClusterIP for gRPC
- **Ingress**: NGINX with SSL termination and WebSocket support
- **Monitoring**: ServiceMonitor and PrometheusRule for alerts
- **Autoscaling**: HPA (3-9 replicas), VPA, and PodDisruptionBudget
- **Security**: NetworkPolicy, Secrets management, TLS certificates
#### Staging Environment (`k8s/staging/`)
- Simplified single-node deployment for testing
- LoadBalancer service for external access
- Debug logging enabled
### Helm Chart (`helm/lxdex/`)
Production-ready Helm chart with:
- Configurable replica count and resource limits
- External service dependencies (PostgreSQL, Redis, NATS)
- GPU support for MLX acceleration
- Prometheus and Grafana monitoring integration
- Multiple storage class support
Installation:
```bash
helm install lxdex ./helm/lxdex \
--namespace lxdex-production \
--values values.production.yaml
```
### CI/CD Pipeline
#### GitHub Actions Workflows
1. **CI Pipeline** (`.github/workflows/ci.yml`)
- Linting with golangci-lint
- Security scanning with gosec
- Multi-platform testing (Ubuntu, macOS)
- Code coverage reporting
- Benchmarking
2. **Deploy Pipeline** (`.github/workflows/deploy.yml`)
- Docker image building (multi-arch)
- Staging deployment on develop branch
- Production deployment on tags
- Automated rollback on failure
- Slack notifications
### Deployment Scripts
**Automated Deployment** (`scripts/deploy.sh`):
```bash
./deploy.sh staging deploy # Deploy to staging
./deploy.sh production deploy # Deploy to production
./deploy.sh production rollback # Rollback production
```
Features:
- Prerequisites checking
- Docker image building and pushing
- Kubectl or Helm deployment
- Health checks and integration tests
- Status monitoring and log viewing
### Production Checklist
Before deploying to production:
- [ ] All tests passing (unit, integration, E2E)
- [ ] Security scan completed
- [ ] Performance benchmarks met
- [ ] Docker images built and pushed
- [ ] Kubernetes cluster ready
- [ ] SSL certificates configured
- [ ] Monitoring dashboards set up
- [ ] Backup strategy in place
- [ ] Disaster recovery plan documented
- [ ] Load testing completed
### Infrastructure Requirements
#### Minimum Production Setup
- **Kubernetes**: v1.25+
- **Nodes**: 3x (32 CPU, 128GB RAM, 1TB NVMe)
- **GPU**: Optional (NVIDIA A100 or Apple M2 Ultra)
- **Network**: 100Gbps interconnect
- **Storage**: Fast SSD with 100K+ IOPS
#### Recommended Production Setup
- **Kubernetes**: Managed service (EKS, GKE, AKS)
- **Nodes**: 9x with GPU acceleration
- **Load Balancer**: Global with GeoDNS
- **CDN**: CloudFlare or Fastly
- **Monitoring**: Full observability stack
- **Backup**: Cross-region replication
## Latest Updates (January 19, 2025)
### Testing Status - 100% Passing ✅
- **Core DEX Tests**: All 144 tests passing
- **Test Coverage**: Improved from 22.4% to 39.1%
- **Performance**: 11.88M orders/sec (C++, 10 threads), 2.2M (pure Go) — CPU default build; GPU-native per-book matcher (CGO_ENABLED=1, `lux-gpu`, parity-verified GPU==CPU) up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10)
- **Integration**: All components fully integrated
### Professional Market Data Sources
Successfully integrated professional-grade market data feeds:
- **Alpaca Markets**: Real-time equities and crypto
- **NYSE Arca**: Direct exchange feeds
- **IEX Cloud**: Low-latency market data
- **Polygon.io**: Comprehensive aggregation
- **CME Group**: Futures and derivatives
- **Refinitiv**: Institutional data
- **ICE Data Services**: Fixed income
- **Bloomberg B-PIPE**: Enterprise data
- **NASDAQ TotalView**: Full depth
- **Coinbase Pro**: Cryptocurrency
### New Features Implemented
1. **Liquidation Engine**: Complete with insurance fund, ADL, socialized losses
2. **X-Chain Settlement**: On-chain clearing and settlement
3. **Advanced Orders**: Iceberg, bracket, hidden orders
4. **Risk Management**: Real-time margin monitoring
5. **Market Data Aggregation**: Multi-source with latency tracking
### SDK Updates
All three SDKs updated with new features:
**Go SDK** (`/sdk/go`):
- `market_data.go`: Market data client methods
- Liquidation monitoring
- Settlement tracking
- WebSocket subscriptions
**Python SDK** (`/sdk/python`):
- `market_data.py`: MarketDataClient class
- LiquidationMonitor for real-time events
- Type-safe dataclasses
- Async/await support
**TypeScript SDK** (`/sdk/typescript`):
- `marketData.ts`: Full TypeScript types
- MarketDataClient and LiquidationMonitor
- Promise-based operations
- WebSocket integration
## Price Feed Architecture (`pkg/price/`)
Multi-source price aggregation for sub-millisecond latency trading with quantum finality verification:
```
┌─────────────────────────────────────────────────────────────────────┐
│ LUX DEX PRICE ORACLE │
│ │
│ DEX/AMM Sources: Oracle Sources: │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ X-Chain │ Weight: 2.0 │ A-Chain │ Weight: 1.5 │
│ │ Native DEX │──┐ │ Validators │──┐ │
│ └──────────────┘ │ └──────────────┘ │ │
│ ┌──────────────┐ │ ┌──────────────┐ │ ┌────────────┐ │
│ │ C-Chain │ │ │ Pyth │ │ │ Oracle │ │
│ │ AMM Pools │──┤ Weight: │ Network │──┼──│ Aggregator │ │
│ │ (Liquid) │ │ 1.8 └──────────────┘ │ │ │──→
│ └──────────────┘ │ ┌──────────────┐ │ │ • Weighted │ │
│ ┌──────────────┐ │ │ Chainlink │ │ │ Median │ │
│ │ Zoo Chain │──┘ Weight: │ Oracle │──┘ │ • TWAP/VWAP│ │
│ │ EVM AMM │ 1.7 └──────────────┘ │ • Circuit │ │
│ │ (Zoo Labs) │ │ Breakers │ │
│ └──────────────┘ └────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Q-Chain Verifier (NOT a price source) │ │
│ │ Verifies quantum finality for prices from other sources: │ │
│ │ • Post-quantum signatures (dilithium, sphincs+) │ │
│ │ • Cross-chain order inclusion proofs │ │
│ │ • Validator quorum consensus │ │
│ └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
### Price Sources (Ordered by Trust Weight)
| Source | Weight | Type | Interval | Features |
|--------|--------|------|----------|----------|
| **X-Chain** | 2.0 | DEX | 50ms | Native DEX orderbook, highest trust |
| **C-Chain** | 1.8 | AMM | 100ms | AMM pools, liquid tokens (LZOO, LETH, etc.) |
| **Zoo Chain** | 1.7 | AMM | 100ms | Zoo Labs EVM DEX, ZOO token pairs |
| **A-Chain** | 1.5 | Oracle | 100ms | Validator-attested network oracle |
| **Pyth** | 1.2 | Oracle | Real-time | External oracle, WebSocket streaming |
| **Chainlink** | 1.0 | Oracle | 1s | External oracle, reference only |
**Weight Priority**: DEX (native orderbook) > AMM (on-chain pools) > Network Oracles > External Oracles
**Note**: Q-Chain is NOT a price source. It's a **finality verification layer** that verifies prices/orders from other sources have achieved quantum finality with post-quantum signatures.
**Latency-Adjusted Weighting** (Planned):
- Weights should be dynamically adjusted based on detected latency
- Lower latency sources get boosted weight for time-sensitive decisions
- High latency or stale feeds get weight penalties
- Formula: `effective_weight = base_weight * (1 / (1 + latency_ms/100))`
```go
// Planned: Adaptive weight calculation based on measured latency
func (s *Source) EffectiveWeight() float64 {
baseWeight := s.Weight()
latencyMs := s.MeasuredLatency().Milliseconds()
// Latency penalty: halve weight for every 100ms of latency
latencyFactor := 1.0 / (1.0 + float64(latencyMs)/100.0)
// Staleness penalty: reduce weight if data is old
age := time.Since(s.LastUpdate())
staleFactor := math.Max(0.1, 1.0 - float64(age.Seconds())/10.0)
return baseWeight * latencyFactor * staleFactor
}
```
**Current Static Weights** (to be replaced with adaptive):
- Native X-Chain (50ms) provides fastest updates for trading decisions
- AMMs (100ms) are reliable for larger trades and reference
- External oracles (1s+) are reference-only, not for time-sensitive trading
### Future External DEX Integrations (Omni-DEX)
Planned cross-chain DEX integrations for comprehensive price discovery:
| DEX | Chain | Type | Priority |
|-----|-------|------|----------|
| **Uniswap V3** | Ethereum | AMM | P1 |
| **SushiSwap** | Multi-chain | AMM | P1 |
| **Astar** | Polkadot | DEX | P2 |
| **dYdX** | Cosmos | Perps | P2 |
| **GMX** | Arbitrum | Perps | P2 |
| **Raydium** | Solana | AMM | P3 |
| **Orca** | Solana | AMM | P3 |
This enables true omni-DEX price aggregation across all major chains and protocols.
### Liquid Token Pairs (C-Chain AMM)
Real market data from Lux C-Chain AMMs:
| Symbol | Price (USD) | Description |
|--------|-------------|-------------|
| LUX | $0.00232 | Native Lux token |
| LZOO | $0.000107 | Liquid ZOO |
| LETH | $4,078.55 | Liquid ETH |
| LSOL | $325.69 | Liquid SOL |
| LUSD | $1.00 | Lux USD stablecoin |
| LBTC | $124,217.54 | Liquid BTC |
| LTON | $10.67 | Liquid TON |
| LAVAX | $72.26 | Liquid AVAX |
| LBNB | $1,065.75 | Liquid BNB |
| LBLAST | $0.0178 | Liquid BLAST |
| LPOL | $0.436 | Liquid POL |
### Zoo Chain Token Pairs
| Symbol | Description |
|--------|-------------|
| ZOO-USDC | Native ZOO token |
| ZOO-LUX | ZOO/LUX cross-chain |
| LZOO-ZOO | Liquid ZOO on Zoo Chain |
| ETH-ZOO | ETH/ZOO ratio |
| WBTC-ZOO | BTC/ZOO ratio |
### Q-Chain Quantum Finality
Post-quantum cryptographic verification for all price feeds:
```go
type QuantumFinality struct {
BlockHash string
BlockHeight uint64
StateRoot string
Timestamp time.Time
Signatures []QuantumSignature // dilithium3, dilithium5, sphincs+
Finalized bool
Latency time.Duration // ~50ms
}
type QValidator struct {
Address string
PublicKey []byte
Stake uint64
Active bool
Algorithm string // dilithium3, sphincs-sha2-256f, etc.
}
```
**Quantum Signature Algorithms**:
- **Dilithium3**: Primary lattice-based signatures
- **Dilithium5**: High-security variant
- **SPHINCS+**: Hash-based backup signatures
**Verification Functions**:
```go
// Verify order inclusion across chains
qchain.VerifyInclusion(orderID, chain, txHash) (*OrderInclusion, error)
// Verify price was finalized with quantum signatures
qchain.VerifyOrderPrice(symbol, price, chain) bool
// Cross-chain finality verification
qchain.CrossChainVerify([]string{"x-chain", "c-chain", "zoo-chain"}) (bool, map[string]*QuantumFinality)
```
### Symbol Normalization
Unified symbol format across disparate sources:
```go
// All variants normalize to canonical form
"LUX/USD" → "LUX-USD"
"LUXUSD" → "LUX-USD"
"LUX-USDC" → "LUX-USD"
"LUX/USDT" → "LUX-USD"
// SymbolMap provides bidirectional mapping
symbolMap := price.NewSymbolMap()
normalized := symbolMap.Map("LUX/USDC") // "LUX-USD"
variants := symbolMap.Reverse("LUX-USD") // ["LUX-USD", "LUX-USDC", "LUX/USD", ...]
```
### Usage
```go
oracle := price.NewOracle()
// Native Lux chain sources
oracle.AddSource("x-chain", price.NewXChainSource(rpcURL, wsURL))
oracle.AddSource("a-chain", price.NewAChainSource(rpcURL, wsURL))
oracle.AddSource("c-chain", price.NewCChainSource(rpcURL, wsURL))
oracle.AddSource("q-chain", price.NewQChainSource(rpcURL, wsURL))
// Zoo Labs network
oracle.AddSource("zoo-chain", price.NewZooChainSource(rpcURL, wsURL))
// External oracles
oracle.AddSource("pyth", price.NewPythSource(wsURL, apiURL))
oracle.AddSource("chainlink", price.NewChainlinkSource())
// Watch symbols
oracle.Watch("LUX-USD")
oracle.Watch("LZOO-USDC")
oracle.Watch("ZOO-USDC")
oracle.Start()
// Get aggregated price
data, _ := oracle.Data("LUX-USD")
fmt.Printf("Price: $%.6f, Source: %s, Confidence: %.2f\n",
data.Price, data.Source, data.Confidence)
// Get TWAP/VWAP
twap := oracle.TWAP("LUX-USD")
vwap := oracle.VWAP("LUX-USD")
// Verify quantum finality
qchain := oracle.Source("q-chain").(*price.QChainSource)
allFinalized, finality := qchain.CrossChainVerify([]string{"x-chain", "c-chain", "zoo-chain"})
if allFinalized {
fmt.Println("QUANTUM finality achieved across all chains")
}
```
### Price Feed CLI
```bash
# Run with all sources
go run ./cmd/price-feed
# Specific sources only
go run ./cmd/price-feed -sources xchain,cchain,zoo
# With quantum finality verification
go run ./cmd/price-feed -verify
# Custom symbols and interval
go run ./cmd/price-feed -symbols "LUX-USDC,LZOO-USDC,ZOO-USDC" -interval 100ms -verify
```
**Output with -verify flag**:
```
SYMBOL PRICE TWAP VWAP CONF SOURCE FINALITY AGE
----------------------------------------------------------------------------------------------------
LUX-USDC $0.002320 $0.002320 $0.000000 0.70 aggregated QUANTUM 0ms
LZOO-USDC $0.000107 $0.000107 $0.000000 0.70 aggregated QUANTUM 0ms
LETH-USDC $4078.550000 $4078.550000 $0.000000 0.70 aggregated QUANTUM 0ms
ZOO-USDC $0.000150 $0.000150 $0.000000 0.70 aggregated QUANTUM 0ms
Quantum finality latency: 50ms
```
### Features
- **Weighted median aggregation** - Outlier resistant with source weights
- **Circuit breakers** - Prevent erroneous prices (max change threshold)
- **TWAP/VWAP** - 5-minute time and volume weighted averages
- **Health monitoring** - Auto-failover on source failure
- **Alert system** - Real-time anomaly detection
- **Symbol normalization** - Unified format across all sources
- **Quantum finality** - Post-quantum signature verification
- **Cross-chain verification** - Verify finality across x-chain, c-chain, zoo-chain
### Remaining Work
- [x] Add X-Chain native DEX source
- [x] Add A-Chain attestation source
- [x] Add C-Chain AMM with liquid tokens
- [x] Add Zoo Chain EVM AMM source
- [x] Add Q-Chain quantum finality verification
- [x] Symbol normalization layer
- [x] Adjust weights: DEX > AMM > Attestations > Oracles
- [ ] Implement adaptive latency-based weighting
- [ ] Add Uniswap/SushiSwap/perps-DEX sources (omni-DEX)
- [ ] Add REST API endpoints for price feeds
- [ ] Complete WebSocket real-time streaming
- [ ] Add Prometheus metrics endpoints
- [ ] Create Grafana dashboards
## Contact
- GitHub: https://github.com/luxfi/dex
- Discord: https://discord.gg/luxnetwork
- Email: [email protected]
---
*Last Updated: December 11, 2025*
*Version: 1.1.0*
*Performance (Apple M1 Max): 11.88M orders/sec C++ (10 threads), 2.2M Go — CPU default build; GPU-native per-book matcher (CGO_ENABLED=1, `lux-gpu`, parity-verified GPU==CPU) up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10) / 5.60B (M4 Max) / 2.80B (M1 Max)*
*Test Status: 100% passing*
*Production Ready: Full Kubernetes + Helm deployment*
*Price Feeds: 7 sources with quantum finality verification*
---
## COMPREHENSIVE CODE REVIEW: DEX Consensus and Network Layers
**Review Date**: 2025-12-11
**Scope**: Consensus (/pkg/consensus/), Network (/pkg/network/), DPDK (/pkg/dpdk/), FPGA (/pkg/fpga/)
**Reviewer**: AI Architect/CTO Analysis
### Executive Summary
**Overall Assessment**: Architecture shows ambitious design with multiple acceleration technologies (GPU, FPGA, DPDK) but has **significant implementation gaps** between documented capabilities and actual code.
**Risk Level**: MEDIUM-HIGH
**Recommendation**: Requires completion of core components before production deployment
---
## 1. CONSENSUS LAYER ANALYSIS
### 1.1 DAG Consensus Architecture (`/pkg/consensus/dag.go`)
#### Implemented Components ✅
**LuxDAGOrderBook** (Lux Consensus Implementation):
```go
type LuxDAGOrderBook struct {
*DAGOrderBook
luxConfig LuxConsensusConfig
blsKey *SecretKey
corona *CoronaEngine // Post-quantum signatures
quasar *Quasar // Dual-certificate protocol
votes map[ID]*VoteState
certificates map[ID]*QuantumCertificate
voteThreshold float64 // Adaptive 0.55-0.65
finalityCount atomic.Uint64
}
```
**Key Features**:
- ✅ DAG vertex structure with parent references
- ✅ Adaptive vote thresholds (θ_min=0.55, θ_max=0.65)
- ✅ Quantum-resistant certificates (BLS + Corona hybrid)
- ✅ Vote state tracking with confidence metrics
- ✅ FPC (Fast Probabilistic Consensus) rounds
#### Consensus Protocol Flow
**50ms Finality Claim Analysis**:
```go
func (lux *LuxDAGOrderBook) RunLuxConsensus() error {
ticker := time.NewTicker(50 * time.Millisecond) // 20 rounds/second
defer ticker.Stop()
for {
select {
case <-ticker.C:
lux.round++
_ = lux.runFPCRound(lux.round)
lux.checkQuantumFinality()
case <-lux.shutdown:
return nil
}
}
}
```
**Verdict**: ✅ **50ms finality is FEASIBLE** but depends on:
1. Network round-trip time < 25ms (half the ticker interval)
2. Vote collection from K=3 validators in parallel
3. Quorum threshold met (typically 2/3 = 67%)
4. No Byzantine behavior detected
**Adaptive Threshold Mechanism**:
```go
func (lux *LuxDAGOrderBook) runFPCRound(round int) error {
progress := float64(round) / 10.0
if progress > 1.0 {
progress = 1.0
}
lux.voteThreshold = lux.luxConfig.ThetaMin +
(lux.luxConfig.ThetaMax - lux.luxConfig.ThetaMin) * progress
// Starts at 0.55, converges to 0.65 over 10 rounds (500ms)
}
```
#### Critical Issues Found
**Issue 1: Mock Implementations** ⚠️
```go
// Many cryptographic operations are mocked
func (r *CoronaEngine) Sign(msg []byte, sk []byte) ([]byte, error) {
sig := make([]byte, 64) // Returns empty signature!
return sig, nil
}
func (r *CoronaEngine) Verify(msg []byte, sig []byte, pk []byte) bool {
if len(sig) != 64 || string(msg) == "Wrong message" {
return false
}
return true // Always returns true except for literal "Wrong message"
}
```
**Impact**: Security NOT production-ready. Needs integration with the actual Corona threshold implementation (`github.com/luxfi/corona`).
**Issue 2: Missing Network Layer Integration** ❌
```go
// No actual P2P message passing implemented
func (dob *DAGOrderBook) AddOrder(order *lx.Order) (*OrderVertex, error) {
// Vertex created but NOT broadcast to network
vertex := &OrderVertex{...}
dob.vertices[vertex.ID] = vertex
// MISSING: Broadcast vertex to peers
// MISSING: Receive votes from validators
return vertex, nil
}
```
**Issue 3: Vote Collection Not Implemented** ❌
- No code for querying K validators
- No vote aggregation mechanism
- No timeout handling for slow validators
#### Multi-Node Testing
**File**: `/pkg/consensus/multinode_test.go` (692 lines)
**Test Coverage**:
- ✅ 3-node consensus with parallel block production
- ✅ 5-node fault tolerance (node 2 failure simulation)
- ✅ High throughput stress test (1000+ blocks/sec target)
- ✅ Byzantine fault tolerance (4 nodes, 1 Byzantine)
- ✅ Network partition and recovery
- ✅ Variable block sizes (1KB - 100KB)
**Key Test Results**:
```go
func TestThreeNodeConsensus(t *testing.T) {
// Creates 3 nodes, each submits 100 blocks
// Expects finalized heights within 5 blocks of each other
// Expects at least 10 blocks finalized
assert.LessOrEqual(t, maxHeight-minHeight, uint64(5),
"Nodes should have similar finalized heights")
}
```
**Verdict**: ✅ Test harness is comprehensive, but tests use **mock DAGConsensus** not production consensus.
---
## 2. NETWORK LAYER ANALYSIS
### 2.1 Network Package Status
**Finding**: `/pkg/network/` directory **DOES NOT EXIST** ❌
**Searched locations**:
```bash
find /Users/z/work/lux/dex -name "network" -type d
# Returns: (empty)
```
**Impact**: Critical gap in architecture. No P2P networking implementation found.
### 2.2 Expected Network Protocol Specification
Based on architecture diagrams and whitepaper claims, the network layer should implement:
#### Message Types
```go
// Expected (NOT FOUND in codebase)
type NetworkMessage struct {
Type MessageType // VERTEX_PROPOSAL, VOTE_REQUEST, VOTE_RESPONSE
Sender NodeID // 32-byte node identifier
Timestamp uint64 // Unix nanoseconds
Payload []byte // Serialized message
Signature []byte // BLS or Corona signature
}
type MessageType uint8
const (
MSG_VERTEX_PROPOSAL MessageType = 0x01
MSG_VOTE_REQUEST MessageType = 0x02
MSG_VOTE_RESPONSE MessageType = 0x03
MSG_CERTIFICATE MessageType = 0x04
)
```
#### State Machine (NOT IMPLEMENTED)
```
┌─────────────────────────────────────────────────────────┐
│ CONSENSUS STATE MACHINE │
├─────────────────────────────────────────────────────────┤
│ │
│ [VERTEX_CREATED] │
│ │ │
│ ├──> Broadcast to K validators │
│ │ │
│ ▼ │
│ [WAITING_VOTES] │
│ │ │
│ ├──> Collect votes (timeout: 25ms) │
│ │ │
│ ▼ │
│ [QUORUM_REACHED] (2/3 threshold) │
│ │ │
│ ├──> Generate quantum certificate │
│ │ │
│ ▼ │
│ [FINALIZED] │
│ │
└─────────────────────────────────────────────────────────┘
```
**Recommendation**: Implement network layer as separate package with clear message protocol.
---
## 3. DPDK KERNEL BYPASS ANALYSIS
### 3.1 Implementation Status (`/pkg/dpdk/kernel_bypass.go`)
#### Architecture
```go
type KernelBypassEngine struct {
mode string // "dpdk", "xdp", "afpacket", "raw", "bpf"
socket int // Raw socket file descriptor
bufferSize int // 1MB default
ringBuffer *RingBuffer
stats *Stats
orderProcessor OrderProcessor
}
```
#### Platform Detection ✅
```go
func NewKernelBypassEngine(processor OrderProcessor) (*KernelBypassEngine, error) {
switch runtime.GOOS {
case "linux":
// Try AF_PACKET (zero-copy) → raw sockets
case "darwin":
// Try BPF → raw sockets
default:
// Optimized UDP sockets
}
}
```
**Modes Supported**:
- Linux: AF_PACKET (PACKET_MMAP), raw sockets
- macOS: BPF (not implemented), raw sockets
- Fallback: Optimized UDP with large buffers
#### Performance Claims
**From comments** (lines 210-216):
```go
// Benchmark results with FPGA acceleration:
// - Order matching latency: 100-500 nanoseconds (10-100x faster than CPU)
// - Network latency: <1 microsecond with kernel bypass
// - End-to-end latency: 1-5 microseconds
// - Throughput: 500M+ orders/second per FPGA
```
> No benchmark backs the "500M+/FPGA" or "100–500 ns" figures — these are
> aspirational source comments, not measured (see the DPDK/FPGA verdict below).
#### Critical Issues
**Issue 1: Binary Protocol Parsing** ⚠️
```go
func (e *KernelBypassEngine) parseOrder(data []byte) *lx.Order {
if len(data) < 32 {
return nil
}
// Fixed 32-byte format:
// [8:orderID][8:price][8:size][1:side][7:padding]
order := &lx.Order{
ID: *(*uint64)(unsafe.Pointer(&data[0])),
Price: *(*float64)(unsafe.Pointer(&data[8])),
Size: *(*float64)(unsafe.Pointer(&data[16])),
Side: lx.Side(data[24]),
Type: lx.Limit,
}
return order
}
```
**Concern**: Only 32 bytes per order. Missing fields:
- User ID (authentication)
- Symbol/market ID
- Order flags (post-only, reduce-only, STP)
- Timestamp (replay protection)
- Signature (integrity)
**Issue 2: No DPDK Integration** ❌
```go
func initDPDK() error {
// Initialize DPDK for kernel bypass
// This would call into DPDK C libraries
return nil // NOT IMPLEMENTED
}
```
**Verdict**: DPDK claims are **ASPIRATIONAL**, not implemented.
---
## 4. FPGA ACCELERATION ANALYSIS
### 4.1 FPGA Interface (`/pkg/fpga/fpga_interface.go`)
#### Device Types Supported
```go
type FPGAType int
const (
FPGATypeNone FPGAType = iota
FPGATypeAMDVersal // AMD Versal VCK190 (AI engine)
FPGATypeAWSF2 // AWS EC2 F1.2xlarge
FPGATypeIntelStratix // Intel Stratix 10 GX
FPGATypeXilinxAlveo // Xilinx Alveo U250
)
```
#### Wire Protocol Specification ✅
**Fixed-size binary messages** (lines 56-63):
```go
const (
OrderWireSize = 48 // 48 bytes per order
AckWireSize = 32 // 32 bytes per acknowledgment
CancelWireSize = 24 // 24 bytes per cancel
TradeWireSize = 64 // 64 bytes per trade
)
```
**Order Format** (48 bytes):
```go
type FPGAOrder struct {
Symbol [8]byte // Fixed-length symbol
OrderID uint64 // 8 bytes
Price uint64 // 8 bytes - fixed-point (8 decimals)
Quantity uint64 // 8 bytes
Side uint8 // 1 byte (0=buy, 1=sell)
OrderType uint8 // 1 byte (0=limit, 1=market, 2=stop)
Flags uint16 // 2 bytes
Timestamp uint64 // 8 bytes - Unix nanoseconds
UserID uint32 // 4 bytes
// Total: 48 bytes (cache-line aligned)
}
```
**Verdict**: ✅ Wire protocol is **well-designed** for FPGA processing (fixed-size, aligned).
#### FPGA Manager Implementation
**File**: `/pkg/fpga/fpga_interface.go` (259 lines)
**Key Features**:
- ✅ Device multiplexing (multiple FPGAs)
- ✅ Round-robin load balancing
- ✅ Batch processing support
- ✅ Health monitoring
- ✅ Temperature/power tracking
**Manager Operations**:
```go
func (m *FPGAManagerImpl) ProcessOrder(order *FPGAOrder) (*FPGAResult, error) {
// Round-robin load balancing
selectedID := deviceIDs[m.roundRobin % len(deviceIDs)]
device := m.devices[selectedID]
start := time.Now()
result, err := device.ProcessOrder(order)
latency := time.Since(start).Nanoseconds()
// Update statistics
stats.AverageLatencyNs = (stats.AverageLatencyNs + uint64(latency)) / 2
return result, err
}
```
#### Critical Issues
**Issue 1: Stub Implementations** ❌
**All device types return errors**:
```go
// In fpga_engine.go:
func programVersalFPGA(device *FPGADevice) error {
// Program AMD Versal FPGA
return nil // NOT IMPLEMENTED
}
func programAWSF2FPGA(device *FPGADevice) error {
// Program AWS F2 instance FPGA
return nil // NOT IMPLEMENTED
}
```
**Issue 2: DMA Transfers Not Implemented** ❌
```go
type FPGAAccelerator interface {
AllocateDMABuffer(size int) (uintptr, error)
FreeDMABuffer(addr uintptr) error
DMATransfer(src, dst uintptr, size int, direction DMADirection) error
}
// No implementations found
```
**Verdict**: FPGA interface is **specification-only**, no hardware integration.
---
## 5. LP-9003 COMPLIANCE CHECK
### 5.1 LP-9003 Requirements
**Lux Proposal 9003**: High-Performance DEX with MEV Protection
**Required Features**:
1. ✅ GPU-accelerated matching — GPU-native deterministic per-book matcher (`pkg/lx/orderbook_gpu.go`, CGO_ENABLED=1, unified `lux-gpu` backend, runtime-select CUDA/HIP/Metal; byte-identical to `MatchOrderCPU`, parity-verified); the default CGO-off build matches on CPU
2. ❌ Commit-reveal MEV protection
3. ⚠️ Cross-chain settlement
### 5.2 GPU Acceleration Status
**File**: `/pkg/mlx/mlx.go` (does not exist — see Verdict below)
**MLX Engine** (Apple Silicon GPU):
```go
type MLXEngine struct {
device mlx.Device
orderBuffer mlx.Buffer // GPU memory
resultBuffer mlx.Buffer
kernel mlx.Kernel // GPU matching kernel
}
func (e *MLXEngine) ProcessOrdersGPU(orders []*Order) ([]*Trade, error) {
// Copy orders to GPU memory
e.orderBuffer.CopyFromHost(unsafe.Pointer(&orders[0]), len(orders))
// Launch GPU kernel (matching happens in parallel on GPU cores)
e.kernel.Launch(len(orders))
// Copy results back
e.resultBuffer.CopyToHost(unsafe.Pointer(&trades[0]), maxTrades)
return trades, nil
}
```
**Performance** (measured, Apple M1 Max):
- CPU (Go, `pkg/lx`): 2.2M orders/sec, 381 ns/order, 1–2 allocs/op
- CPU (C++): 11.88M orders/sec (10 threads), 5.91M single-thread, 169 ns avg match
- GPU (MLX) matching: the prior "434M orders/sec" was a fabrication — it came from a
pure-Go *simulation* (`archive/_lp108-2026-05-04/mlx_engine.go`, hardcoded
`OrdersPerSecond: 1000000.0`), not a Metal kernel; there is no `pkg/mlx`.
- GPU-native per-book matcher (real): `pkg/lx/orderbook_gpu.go` (+`pkg/lxgpu/`),
CGO_ENABLED=1, unified `lux-gpu` backend (runtime-select CUDA > HIP > Metal > CPU),
byte-identical to `MatchOrderCPU` and parity-verified
(`pkg/lxgpu/orderbook_parity_test.go`, `three_mode_parity_test.go`) — up to
12.76B ord/s (AMD 8060S) / 9.13B (GB10) / 5.60B (M4 Max) / 2.80B (M1 Max);
21.9B on a two-node fabric. Kernels ship prebuilt from luxcpp/dex.
**Verdict**: There is no MLX/`pkg/mlx` matcher and the "434M orders/sec" figure
is debunked. A separate, real GPU-native deterministic per-book matcher DOES
exist (`pkg/lx/orderbook_gpu.go` via the unified `lux-gpu` backend,
parity-verified GPU==CPU), available with CGO_ENABLED=1; the default CGO-off
build matches on CPU. The GPU also drives FHE (Metal NTT, 23.6× at N=4096,
batch=128).
### 5.3 MEV Protection Analysis
**Expected**: Commit-reveal scheme to prevent front-running
**Searched codebase**:
```bash
grep -r "commit.*reveal\|MEV\|front.*run" /Users/z/work/lux/dex/pkg
# No matches found
```
**Finding**: ❌ **NO MEV PROTECTION** implemented.
**Recommendation**: Implement 2-phase commit:
```go
// Phase 1: Commit (hash of order)
type OrderCommitment struct {
CommitHash [32]byte // SHA256(orderID || price || size || nonce || signature)
Timestamp uint64
}
// Phase 2: Reveal (actual order after N blocks)
type OrderReveal struct {
Order *Order
Nonce uint64
Signature []byte
}
```
### 5.4 Cross-Chain Settlement
**File**: `/pkg/lx/bridge.go` (exists but not reviewed in detail)
**Status**: ⚠️ Bridge implementation exists but needs separate review.
---
## 6. IETF-STYLE PROTOCOL DOCUMENTATION
### 6.1 Message Formats
#### FPGA Wire Protocol (Binary)
**Order Message** (48 bytes):
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Symbol (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Order ID (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Price (8 bytes) |
| Fixed-point, 8 decimals |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Quantity (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|S|T| Flags | Timestamp (8 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User ID (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Where:
S = Side (1 bit): 0=Buy, 1=Sell
T = Type (1 bit): 0=Limit, 1=Market
Flags = 14 bits for order flags
```
**Acknowledgment Message** (32 bytes):
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Order ID (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Status |RejectCode| Reserved (6 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number (8 bytes) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Status: 0=Accepted, 1=Rejected, 2=Cancelled
RejectCode: Reason if status=Rejected
```
### 6.2 State Machines
#### Order Lifecycle State Machine
```
┌──────────────┐
│ CREATED │
└──────┬───────┘
│
▼
┌──────────────┴──────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ PENDING │ │ REJECTED │
└──────┬───────┘ └──────────────┘
│
├──> OPEN (in order book)
│
├──> PARTIAL_FILL
│ │
│ └──> FILLED
│
└──> CANCELLED
Transitions:
- CREATED → PENDING: Order submitted to network
- PENDING → REJECTED: Validation failure
- PENDING → OPEN: Passed validation, added to book
- OPEN → PARTIAL_FILL: Matched partially
- PARTIAL_FILL → FILLED: Fully matched
- OPEN/PARTIAL_FILL → CANCELLED: User cancellation
```
#### Consensus State Machine
```
┌────────────────────────────────────────────────────────────────┐
│ CONSENSUS PROTOCOL │
├────────────────────────────────────────────────────────────────┤
│ │
│ [VERTEX_PROPOSED] │
│ │ │
│ ├──> Broadcast to K validators │
│ │ (Multicast via QZMQ) │
│ │ │
│ ▼ │
│ [COLLECTING_VOTES] │
│ │ │
│ ├──> Wait for 2/3 votes (timeout: 25ms) │
│ │ │
│ ├──> If timeout: retry or skip │
│ │ │
│ ▼ │
│ [QUORUM_REACHED] │
│ │ │
│ ├──> Generate BLS aggregate signature │
│ ├──> Generate Corona quantum signature │
│ ├──> Create QuantumCertificate │
│ │ │
│ ▼ │
│ [CERTIFIED] │
│ │ │
│ ├──> Broadcast certificate to network │
│ │ │
│ ▼ │
│ [FINALIZED] │
│ │ │
│ └──> Update finalized height │
│ │
└────────────────────────────────────────────────────────────────┘
```
### 6.3 Timing Requirements
**Performance Targets** (from whitepaper):
| Operation | Target | Achieved | Status |
|-----------|--------|----------|--------|
| Network round-trip | <1μs | TBD | ⚠️ |
| Consensus finality | 50ms | TBD | ⚠️ |
| Block propagation | <25ms | TBD | ⚠️ |
**Critical Timing Constraints**:
```
Total Finality Time = 50ms
├─ Round-trip to K validators: 25ms (50%)
├─ Vote collection: 10ms (20%)
├─ Certificate generation: 10ms (20%)
└─ Block propagation: 5ms (10%)
```
### 6.4 Security Considerations
#### Threat Model
**Byzantine Validators**:
- Assumption: Up to f < N/3 validators are Byzantine
- Protection: 2/3 quorum threshold (67%)
- Detection: Conflicting votes, invalid signatures
**Network Attacks**:
- DDoS: Rate limiting at gateway layer
- Eclipse: Minimum peer connections (3-5)
- Sybil: Stake-based admission (1M LUX minimum)
**MEV Attacks**:
- Front-running: ❌ NOT PROTECTED (needs commit-reveal)
- Sandwich attacks: ❌ NOT PROTECTED
- Time-bandit attacks: ⚠️ Mitigated by fast finality (50ms)
**Quantum Threats**:
- Key compromise: ✅ Protected by Corona (post-quantum)
- Signature forgery: ✅ Protected by lattice-based crypto
- Future attacks: ✅ Hybrid BLS+Corona provides transition path
---
## 7. CRITICAL FINDINGS SUMMARY
### 7.1 Architecture Strengths ✅
1. **Multi-engine design** allows performance optimization per deployment
2. **GPU acceleration**: FHE (Metal NTT, 23.6× at N=4096, batch=128) plus a real GPU-native deterministic per-book matcher (CGO_ENABLED=1, unified `lux-gpu` backend, parity-verified GPU==CPU) up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10); the default CGO-off build matches on CPU
3. **FPGA wire protocol** is well-designed for hardware acceleration
4. **Test coverage** is comprehensive (multi-node, Byzantine, partition tests)
5. **Quantum-resistant crypto** architecture is sound (BLS+Corona hybrid)
### 7.2 Critical Gaps ❌
1. **No P2P network layer** - `/pkg/network/` does not exist
2. **Mock cryptographic implementations** - Corona not integrated
3. **No vote collection mechanism** - Consensus cannot finalize
4. **DPDK not implemented** - Kernel bypass claims are aspirational
5. **FPGA integration missing** - All device drivers are stubs
6. **MEV protection absent** - No commit-reveal scheme
7. **No message protocol specification** - Vertex/vote wire formats undefined
### 7.3 Implementation Priority List
**P0 (Blocking)**:
1. Implement P2P network layer with QZMQ/ZeroMQ
2. Integrate real Corona signatures from threshold package
3. Implement vote collection and aggregation
4. Define and implement network message protocol
**P1 (Required for Production)**:
5. Add MEV protection (commit-reveal)
6. Complete DPDK kernel bypass (Linux only acceptable)
7. Add comprehensive network fault injection tests
8. Implement Byzantine behavior detection
**P2 (Performance Optimization)**:
9. FPGA device drivers (AMD Versal, AWS F2)
10. RDMA integration for state replication
11. Advanced order types (iceberg, pegged, TWA P)
12. Cross-chain bridge security audit
### 7.4 50ms Finality Feasibility Assessment
**Theoretical Analysis**:
- Single consensus round: 50ms
- Network latency budget: 25ms (50%)
- Vote processing: 10ms (20%)
- Certificate generation: 10ms (20%)
- Propagation overhead: 5ms (10%)
**Network Assumptions**:
- Validators in same datacenter: RTT <1ms ✅
- Validators in same region: RTT <5ms ✅
- Cross-continental: RTT 100-200ms ❌
**Verdict**: ✅ **50ms finality is ACHIEVABLE** in single-region deployment with optimized network stack.
**Realistic Targets**:
- Same datacenter: 10-20ms finality
- Same region: 50-100ms finality
- Global: 200-500ms finality
---
## 8. RECOMMENDATIONS
### 8.1 Immediate Actions
1. **Create `/pkg/network/` package** with:
- QZMQ transport layer
- Message serialization (protobuf or custom binary)
- Peer management and health checking
- Retransmission and timeout logic
2. **Integrate Corona crypto** from `github.com/luxfi/corona`:
- Replace mock Sign() and Verify() implementations
- Test quantum certificate generation end-to-end
- Benchmark signature performance (target: <1ms)
3. **Implement vote collection**:
- Parallel vote requests to K validators
- Timeout handling (25ms)
- Vote verification and aggregation
- Quorum detection (2/3 threshold)
### 8.2 Code Quality Improvements
**Add comprehensive error handling**:
```go
// Current (bad):
func (e *Engine) Process() {
result := doSomething()
// No error check!
}
// Recommended:
func (e *Engine) Process() error {
result, err := doSomething()
if err != nil {
return fmt.Errorf("process failed: %w", err)
}
return nil
}
```
**Add metric instrumentation**:
```go
import "github.com/luxfi/metric"
var (
consensusLatency = metric.NewHistogram(...)
voteCount = metric.NewCounter(...)
byzantineDetected = metric.NewCounter(...)
)
```
### 8.3 Documentation Improvements
1. Add IETF-style RFC for network protocol
2. Document all message formats with packet diagrams
3. Add sequence diagrams for consensus protocol
4. Document Byzantine behavior detection logic
5. Add runbook for operators (monitoring, troubleshooting)
---
## 9. CONCLUSION
The LX architecture shows **ambitious vision** with cutting-edge technologies (DAG consensus, quantum resistance, GPU/FPGA acceleration), but has **significant implementation gaps** that prevent production deployment.
**Core Strength**: matching engine — C++ at 11.88M orders/sec (10 threads, 169 ns avg match) and pure Go at 2.2M orders/sec on the default CPU build, plus a GPU-native deterministic per-book matcher (CGO_ENABLED=1, unified `lux-gpu` backend, parity-verified GPU==CPU) up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10). GPU also drives FHE (Metal NTT, 23.6×).
**Critical Blocker**: Network layer and consensus protocol are incomplete. The 50ms finality claim is **architecturally sound** but requires network implementation to validate.
**Estimated Completion Time**:
- P0 tasks (network layer, crypto integration): 4-6 weeks
- P1 tasks (MEV protection, testing): 2-3 weeks
- P2 tasks (FPGA, RDMA): 8-12 weeks
**Overall Risk**: MEDIUM-HIGH until network layer is complete.
---
**End of Code Review**
**Next Steps**: Prioritize P0 tasks, create network package, integrate Corona crypto.
---
## Code Review Summary (2025-12-12)
A comprehensive code review was conducted focusing on compound word usage, code duplication, error handling, API surface area, and test coverage. Full details in CODE_REVIEW_2025-12-12.md.
### Key Findings
**Critical Issues:**
1. **Compound Word Overuse** - 30+ compound identifiers found (OrderBook, TradingEngine, ClearingHouse, etc.). Recommend decomposition to simpler names following Go conventions.
2. **Error Handling Inconsistency** - Mix of sentinel errors and string-based errors. Need standardization across 218+ error sites.
**Major Concerns:**
3. **SDK Duplication** - Identical logic implemented 3x (Go/TS/Python). Recommend gRPC service to eliminate duplication.
4. **API Surface Area** - 24+ public methods on OrderBook with redundancies. Recommend reduction to minimal interface.
**Test Coverage:** 75.3% (good baseline, needs edge case improvement)
**Overall Assessment:** Production-ready with Medium risk level. Approve with recommended changes.
### Priority Actions
1. Begin compound word deprecation cycle (type aliases → migration → removal)
2. Standardize error handling with sentinel errors
3. Extract SDK business logic to shared gRPC service
4. Reduce OrderBook API surface area
**Estimated Effort:** 2-3 sprints for high priority fixes, 1-2 quarters for complete refactoring.
See CODE_REVIEW_2025-12-12.md for detailed recommendations with file:line references.
---
## `pkg/trading` is gone — the surface it described lives in `pkg/gateway`
There were two implementations of one trading API in this repo, and neither one
was mounted by the binary that shipped.
`pkg/trading` carried its own `Venue` interface, its own `V4Venue`,
`NativeDEXVenue`, `UniswapV2Venue`, `UniswapV3Venue` and broker venues, its own
constant-product math and its own `eth_call` client — all of it a copy of what
`pkg/gateway` already had. Its `RegisterRoutes` was called by its own tests and
by nothing else: `grep -rn 'luxfi/dex/pkg/trading' --include='*.go' .` outside
that directory returned empty. `api/openapi.yaml` documented it anyway, so the
one published contract in the repo described the one package no binary served.
The surface it described — quote, swap, order, approval, venues — is what
`pkg/gateway` serves under `/v1/trade`, and `api/openapi.yaml` now describes
that. The old package is at `~/work/_archive/dex-duplicate-surfaces/`, with the
`/trading/*` compat handlers and their Uniswap-shaped types.
**Read this before adding a venue.** One `Venue` interface, in
`pkg/gateway/venue.go`. One place that says which contracts a chain is read
from, `DefaultChainVenues` in `venue_chains.go`. One route table,
`Server.routes()`. A second copy of any of them is how this happened.
## The C↔D seam: how a cross-chain swap becomes a trade
The shared-memory object between the C-Chain (0x9999 precompile) and this
D-Chain has **two canonical widths, and width is the discriminator**:
| Width | What it is | Who writes it | Decoder |
|-------|-----------|---------------|---------|
| 69 B | a **value** object — `rail|owner|asset|amount|spent` | D→C settlements, C→D LP commits | `decodeSeamObject` |
| 118 B | a C→D **swap intent** — value + `market|side|limitPrice|size` | 0x9999 `SubmitSwapIntent` | `decodeSeamIntentObject` |
A value says what moves; only an intent says what to *do* with it. Before the
operation segment existed, `executeImport` credited the taker and stopped —
nothing placed an order, so nothing crossed, so nothing was ever exported back to
C. Anything that is neither width is refused, never reinterpreted.
Cross-repo pins (the two repos cannot import each other):
`precompile/dex/native_seam_parity_test.go` and
`dex/pkg/dchain/seam_reproducible_test.go` hold the **same golden hex** for the
118-byte object, and both pin `SeamPendingTrait` =
`sha256("lux.dex.native.intent.pending.v2")`.
### The three legs
```
BuildBlock enumerates shared memory by SeamPendingTrait
├─ TxImport(intentID, object[118]) consume + credit seamAccount(intentID)
├─ TxIntentSubmit(intentID) derive the zapwire Submit from the escrow,
│ run the SAME custody→match→settle path a
│ signed dex_submit takes
└─ (next block) TxExport × n proceeds + leftover back to C
```
`TxIntentSubmit` is the **object-authorized sibling of `TxSubmit`**, exactly as
`TxImport` is of `TxDeposit`. It cannot be a plain `TxSubmit`: that type's
`requiresAuth()` is true and weakening it would let any unsigned submit move any
account's funds.
### Two rules that are easy to get wrong
1. **Never `sm.Get` inside `execute()`.** The object's bytes ride in the
`TxImport` body; `Block.verifySeamImports` (gated on `vm.normalOp`)
authenticates them against shared memory at Verify. Reading shared memory
during execution makes the block unreplayable — the consuming Remove lands at
Accept, so the next node to sync finds the object gone and dies on
`execution root mismatch`.
2. **The seam runs in `seamAccount(intentID)`, never the taker's own account.**
The export drive settles an account's whole balance back to C; sharing the
account with the taker's native D balance means exporting value the seam never
backed, which C credits out of the shared seam reserve — a raid on other
takers' pooled input.
`seamIntentRecord.Status` (`open → submitted → reclaimed`) is the state machine.
The export drive gates on `submitted`, a **recorded fact**, not on "does the
account hold some other asset?" — that inference could never export a zero-fill
swap, which stranded the principal on D while C's deadline reclaim paid the taker
a second time.
### Flag day
The intent object (69→118) and the `TxImport` body (32→150) both changed width.
An old node's `bodySize(TxImport)` is 32, so a new seam block fails
`parseTxFrame` → `decodeTxList` → `ParseBlock`: it is rejected as **unparseable**,
so an old node never computes a state root for it and cannot compute a different
one. Mixed fleets stall; they cannot fork. No historical block anywhere contains
a `TxImport`, so there is no retroactive effect.
**Upgrade every D validator before the first v2 intent is minted on C.**
### D-Chain HTTP surface — and how to read a 404
`CreateHandlers` mounts `/v1/chain/D/dex/<method>` (`ingest.go`). The method names
were **`clob_*` up to dex v1.5.x** and `dex_*` after (commit `28970d8`). So on a
fleet running an older plugin:
```
/v1/chain/d/dex/dex_get_markets -> 404 (new names, not deployed)
/v1/chain/d/dex/clob_get_markets -> 200 (old names, live)
```
**A 404 on `dex_*` means "old method names", not "no surface" and not "dead
chain".** Probe `clob_*` before concluding anything, and check what the deployed
plugin actually vendors rather than trusting `DEX_REF` in `luxfi/node`'s
Dockerfile — the running image can predate the pin:
```
kubectl exec <pod> -c luxd -- sh -c \
"grep -a -oE 'luxfi/dex.v[0-9]+[.][0-9]+[.][0-9]+' /data/plugins/mDVT5EW..."
```
A D-Chain on **dex < v1.14** has no `atomic.go` and no `drive.go` at all: it
cannot import a C->D intent or export a D->C fill under any configuration. That
is a structurally absent seam, not a misconfiguration.
### D-Chain genesis changed between dex v1.5.x and v1.14.x
**A fleet carrying a v1.5 D-Chain store cannot be joined by a v1.14 node.** The two
lineages derive different genesis blocks, so they disagree at height 0 and no
amount of syncing fixes it:
```
dex v1.5.15 genesis MuetnVSbs1UnPUDTH5q4kgbKcfRx97Djmz... root f8e013a4...
dex v1.14.x genesis mpxP6xF3ahpjdwP6YgjtagSWQCZSR6pziE... root 92ae9d5c...
```
Nodes that keep their old store carry the v1.5 genesis and keep working; a node
whose store is wiped (or any newly added validator) adopts the v1.14 genesis and
is stranded at height 0 forever, `isBootstrapped:false`, with no error that names
the cause.
So the v1.5 -> v1.14 bump is a **D-Chain re-genesis**, not an upgrade. Roll it as
one: wipe `/data/chainData/network-<id>/<D-chain-id>` on every validator as part
of the rollout, one node at a time. Do it while D is at height 0 and there is
nothing to lose. Verify afterwards that all validators report the *same*
`lastAccepted` — a matching height is not enough, since every node reports
height 0 either way.
### Getting liquidity onto D
A seam order is IOC — it never rests — so a cross-chain swap can only trade
against a **signed maker** order. Funding one needs either:
- `TxDeposit`, which requires `depositAuthority` in the D chain-config
(`/data/configs/chains/D/config.json`, schema in `zapingest.go`). It is a
**consensus parameter**: every validator must carry the byte-identical value
or they fork on deposit authorization. Empty (the default) rejects every
deposit, fail-closed.
- or a C->D atomic import — which funds a per-intent `seamAccount` that no
signature can authorize, so it cannot place a resting order.
So a devnet trade proof needs the deposit authority configured first.