The v4_native arm is configured on 96369 and silent: 0x9010 answers 0x there, measured with the venue's own quoter selector, so every price on that chain today comes from uniswap_v3 reading the QuoterV2 the venue deployment put there. `native: true` says the arm is attached, not that it answered. Also the addresses from luxfi/standard deployments/venue/96369.json, the reason its `Router` key belongs to LUX_V3_ROUTER and never LUX_V2_ROUTER, and one measured price per configured chain so a later reader can tell a broken arm from a moved market. Co-authored-by: Hanzo Dev <[email protected]>
95 KiB
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-gpubackend (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
- 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
- Planet-Scale Capacity (design target): 5M markets simultaneously per node — a capacity goal, not a measured benchmark
- Quantum-Resistant: QZMQ protocol with post-quantum cryptography for node communication
- 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-gpubackend) sustains up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10). GPU also drives FHE (Metal NTT 23.6×) - 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-V4PoolManagerABI, 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.0x9010is 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 implementationorderbook_advanced.go: Advanced order types (iceberg, stop, bracket)orderbook_cgo.go: C++ bridge for performancematching_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:
// 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/)
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/)
client = LXDexClient()
order = client.place_order(
symbol="BTC-USD",
order_type=OrderType.LIMIT,
side=OrderSide.BUY,
price=50000,
size=0.1
)
Go (sdk/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
./bin/luxd \
--enable-mlx \
--block-time 1ms \
--http-port 8080 \
--ws-port 8081
3-Node Cluster (K=3)
# 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
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.
Eight paths, and every one of them answers:
GET /v1/trade/venues what this deployment can price, per chain
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
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 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 orderlx_cancelOrder: Cancel existing orderlx_getOrderBook: Get order book snapshotlx_getTrades: Get recent tradeslx_getOrder: Get order detailslx_getBestBid: Get best bid pricelx_getBestAsk: Get best ask pricelx_getInfo: Get node information
WebSocket Channels
orderbook:{symbol}: Order book updatestrades:{symbol}: Trade feedcandles:{symbol}:{interval}: OHLCV updates
gRPC Services
PlaceOrder: Unary order placementCancelOrder: Unary order cancellationStreamOrderBook: Server streaming order bookStreamTrades: 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 orderslx_trades_executed_total: Total tradeslx_matching_latency_seconds: Matching latencylx_consensus_latency_seconds: Consensus latencylx_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
# Build all components
make build
# Build with GPU support
CGO_ENABLED=1 make build-gpu
# Run tests
make test
# Run benchmarks
make bench
Testing
# Unit tests
go test ./pkg/...
# Integration tests
go test ./test/integration/...
# E2E tests with Docker
make docker-test
Contributing
- Fork repository
- Create feature branch
- Write tests
- Implement feature
- Run linters
- Submit PR
Configuration
Environment Variables
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)
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
-
Port already in use
lsof -i :8080 kill -9 <PID> -
MLX not available
- Ensure Apple Silicon Mac
- Update MLX framework
- Fallback to CPU mode
-
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
-
Lock-Free Data Structures
- Atomic operations for concurrent access
- RCU (Read-Copy-Update) patterns for orderbook updates
- Memory pools to eliminate GC pressure
-
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)
-
Buffer Management
- Ring buffers for order queues
- Memory-mapped files for persistence
- NUMA-aware memory allocation
Network Optimizations
-
Kernel Bypass
- DPDK/XDP support for packet processing
- Zero-copy networking with io_uring
- TCP_NODELAY for minimal latency
-
Protocol Efficiency
- Binary FIX: 60-byte fixed messages
- Custom binary formats for internal communication
- Multicast for market data distribution
-
Connection Management
- Connection pooling
- Persistent WebSocket connections
- Graceful degradation under load
Algorithm Optimizations
-
Order Matching
- Red-black trees for price levels
- Heap-based priority queues for orders
- O(log n) insertion and deletion
-
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-gpubackend, 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)
-
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
-
Hardware Requirements
- Minimum: 8 cores, 32GB RAM, NVMe SSD
- Recommended: 32 cores, 128GB RAM, Optane SSD
- Optimal: Mac Studio M2 Ultra, 512GB RAM
-
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-gpubackend, parity-verified GPU==CPU) for planet-scale fan-out across millions of books
-
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
- Unit Tests: Individual component validation
- Integration Tests: Multi-component workflows
- Performance Tests: Throughput and latency benchmarks
- Stress Tests: High-load scenarios
- 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
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:
helm install lxdex ./helm/lxdex \
--namespace lxdex-production \
--values values.production.yaml
CI/CD Pipeline
GitHub Actions Workflows
-
CI Pipeline (
.github/workflows/ci.yml)- Linting with golangci-lint
- Security scanning with gosec
- Multi-platform testing (Ubuntu, macOS)
- Code coverage reporting
- Benchmarking
-
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):
./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
- Liquidation Engine: Complete with insurance fund, ADL, socialized losses
- X-Chain Settlement: On-chain clearing and settlement
- Advanced Orders: Iceberg, bracket, hidden orders
- Risk Management: Real-time margin monitoring
- 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))
// 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:
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:
// 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:
// 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
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
# 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
- Add X-Chain native DEX source
- Add A-Chain attestation source
- Add C-Chain AMM with liquid tokens
- Add Zoo Chain EVM AMM source
- Add Q-Chain quantum finality verification
- Symbol normalization layer
- 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):
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:
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:
- Network round-trip time < 25ms (half the ticker interval)
- Vote collection from K=3 validators in parallel
- Quorum threshold met (typically 2/3 = 67%)
- No Byzantine behavior detected
Adaptive Threshold Mechanism:
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 ⚠️
// 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 ❌
// 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:
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:
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
// 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
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 ✅
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):
// 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 ⚠️
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 ❌
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
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):
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):
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:
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:
// 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 ❌
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:
- ✅ GPU-accelerated matching — GPU-native deterministic per-book matcher (
pkg/lx/orderbook_gpu.go, CGO_ENABLED=1, unifiedlux-gpubackend, runtime-select CUDA/HIP/Metal; byte-identical toMatchOrderCPU, parity-verified); the default CGO-off build matches on CPU - ❌ Commit-reveal MEV protection
- ⚠️ Cross-chain settlement
5.2 GPU Acceleration Status
File: /pkg/mlx/mlx.go (does not exist — see Verdict below)
MLX Engine (Apple Silicon GPU):
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, hardcodedOrdersPerSecond: 1000000.0), not a Metal kernel; there is nopkg/mlx. - GPU-native per-book matcher (real):
pkg/lx/orderbook_gpu.go(+pkg/lxgpu/), CGO_ENABLED=1, unifiedlux-gpubackend (runtime-select CUDA > HIP > Metal > CPU), byte-identical toMatchOrderCPUand 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:
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:
// 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 ✅
- Multi-engine design allows performance optimization per deployment
- 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-gpubackend, parity-verified GPU==CPU) up to 12.76B ord/s (AMD 8060S) / 9.13B (GB10); the default CGO-off build matches on CPU - FPGA wire protocol is well-designed for hardware acceleration
- Test coverage is comprehensive (multi-node, Byzantine, partition tests)
- Quantum-resistant crypto architecture is sound (BLS+Corona hybrid)
7.2 Critical Gaps ❌
- No P2P network layer -
/pkg/network/does not exist - Mock cryptographic implementations - Corona not integrated
- No vote collection mechanism - Consensus cannot finalize
- DPDK not implemented - Kernel bypass claims are aspirational
- FPGA integration missing - All device drivers are stubs
- MEV protection absent - No commit-reveal scheme
- No message protocol specification - Vertex/vote wire formats undefined
7.3 Implementation Priority List
P0 (Blocking):
- Implement P2P network layer with QZMQ/ZeroMQ
- Integrate real Corona signatures from threshold package
- Implement vote collection and aggregation
- 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
-
Create
/pkg/network/package with:- QZMQ transport layer
- Message serialization (protobuf or custom binary)
- Peer management and health checking
- Retransmission and timeout logic
-
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)
-
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:
// 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:
import "github.com/luxfi/metric"
var (
consensusLatency = metric.NewHistogram(...)
voteCount = metric.NewCounter(...)
byzantineDetected = metric.NewCounter(...)
)
8.3 Documentation Improvements
- Add IETF-style RFC for network protocol
- Document all message formats with packet diagrams
- Add sequence diagrams for consensus protocol
- Document Byzantine behavior detection logic
- 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:
- Compound Word Overuse - 30+ compound identifiers found (OrderBook, TradingEngine, ClearingHouse, etc.). Recommend decomposition to simpler names following Go conventions.
- 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
- Begin compound word deprecation cycle (type aliases → migration → removal)
- Standardize error handling with sentinel errors
- Extract SDK business logic to shared gRPC service
- 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.
API Gateway (2025-12-26)
Overview
A new provider-based API gateway has been added to pkg/gateway/ that aggregates multiple DEX providers (Uniswap, native Lux) with priority-based routing and fallback support.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ API GATEWAY │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP Server (pkg/gateway/server.go) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ /v1/trade/venues /v1/trade/quote /v1/trade/swap │ │
│ │ /v1/trade/pools /v1/trade/price /v1/trade/order │ │
│ │ /v1/trade/approval/check /v1/trade/permit2/check │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ Router (pkg/gateway/router.go) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ - Multi-provider aggregation │ │
│ │ - Best quote selection (by output amount) │ │
│ │ - Fallback on provider failure │ │
│ │ - Parallel quote fetching │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ Provider Registry (pkg/gateway/registry.go) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Priority 10: Lux Native (Lux/Zoo chains) │ │
│ │ Priority 100: Uniswap (All chains, fallback) │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ Providers │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ lux/provider.go │ │ uniswap/provider.go │ │
│ │ - Native DEX pools │ │ - api.uniswap.org │ │
│ │ - Precompile 0x0400 │ │ - liquidity.backend-prod │ │
│ │ - Oracle integration│ │ - entry-gateway.backend │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Provider Interfaces
// Core interfaces in pkg/gateway/provider.go
type QuoteProvider interface {
GetQuote(ctx, req QuoteRequest) (*SwapQuote, error)
GetQuotes(ctx, req QuoteRequest) ([]SwapQuote, error)
}
type SwapProvider interface {
QuoteProvider
BuildSwap(ctx, quote, recipient, deadline) (*SwapTransaction, error)
}
type LiquidityProvider interface {
GetPools(ctx, req PoolsRequest) ([]Pool, error)
GetPositions(ctx, req PositionsRequest) ([]Position, error)
}
type ConversionProvider interface {
CreateLead(ctx, lead) (*ConversionLead, error)
TrackEvent(ctx, event) error
}
Uniswap Provider (Fallback)
Delegates to 3 Uniswap APIs:
| API | Endpoint | Purpose |
|---|---|---|
| Core API | api.uniswap.org |
Quotes, tokens, prices |
| Liquidity | liquidity.backend-prod.api.uniswap.org |
Pools, positions |
| Conversion | entry-gateway.backend-*.api.uniswap.org |
Analytics tracking |
Lux Native Provider (Priority)
Stubs ready for implementation:
- Quote from DEX precompile (0x0400)
- Pool queries from PoolManager
- Native token list for Lux/Zoo chains
- Oracle integration from
pkg/lx/oracle.go
Supported Chains
| Chain | ID | Native Provider | Uniswap Fallback |
|---|---|---|---|
| Lux | 96369 | Yes (priority) | Yes |
| Zoo | 200200 | Yes (priority) | Yes |
| Ethereum | 1 | No | Yes |
| Arbitrum | 42161 | No | Yes |
| Optimism | 10 | No | Yes |
| Polygon | 137 | No | Yes |
| Base | 8453 | No | Yes |
| BNB | 56 | No | Yes |
Usage
# Start gateway server
go run cmd/gateway/main.go -addr :8080
# Get best quote from all providers
curl -X POST http://localhost:8080/v1/trade/quote \
-H "Content-Type: application/json" \
-d '{
"tokenIn": "0x...",
"tokenOut": "0x...",
"chainId": 96369,
"amount": "1000000000000000000",
"isExactIn": true
}'
# Health check
curl http://localhost:8080/healthz
# List providers
curl http://localhost:8080/providers
Tests
# Run gateway tests (12 tests, all passing)
go test -v ./pkg/gateway/...
Future Work
-
Lux Native Implementation
- Connect to DEX precompile at 0x0400
- Integrate with orderbook in
pkg/lx/ - Add native oracle prices from Chainlink/Pyth sources
-
Caching Layer
- Quote caching with TTL
- Pool state caching
- Token list caching
-
Advanced Routing
- Multi-hop route optimization
- Split routes across providers
- MEV-protected execution
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
- Never
sm.Getinsideexecute(). The object's bytes ride in theTxImportbody;Block.verifySeamImports(gated onvm.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 onexecution root mismatch. - 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 requiresdepositAuthorityin the D chain-config (/data/configs/chains/D/config.json, schema inzapingest.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
seamAccountthat no signature can authorize, so it cannot place a resting order.
So a devnet trade proof needs the deposit authority configured first.