Files
zeekayandHanzo Dev 4f8df54e87 fix(dex): exact-integer ZAP wire + ledger-in-root — kill cross-arch/restart execRoot forks, silent money desync, and ceil/floor strand
Consensus-correctness fix for the native DEX settlement VM. Root cause of the
launch-blocking forks: order size/price were IEEE-754 float64 on the ZAP wire, so
every validator re-derived the settlement integer via uint64(float)/int64(float*1e8),
whose out-of-range result is implementation-defined per the Go spec and DIFFERS BY
CPU ARCHITECTURE (arm64 saturates to 2^64-1; amd64 wraps to 2^63). Two honest
validators on different archs then derived different reserves/balances/fills from the
SAME ordered block.

F1/F3/F5 — exact-integer size/price end to end:
- pkg/zapwire: Place/Submit/Fill carry price as uint64 fixed-point (×1e8 = PriceInt)
  and size as uint64 atomic base units. No float64 on the wire. Removed PutFloat64/
  Float64. Decode is exact bytes->uint64 (deterministic on every arch).
- pkg/dchain/execute: decode the exact integers, set the authoritative PriceUnits/
  SizeUnits lane; the float Price/Size become derived projections only (never a
  source of a consensus value). Reject price==0/size==0/price>maxPriceUnits.
- pkg/dex/persist: the DEXOrder/DEXTrade row stores the EXACT PriceInt (in
  DEXPrice.Integer) and EXACT base units (Quantity/Remaining); RowToOrder restores
  the integer lane verbatim (identity — no float round-trip) and Filled=Qty-Remaining.
  Removed floatToFixedQty/fixedQtyToFloat/floatToFixedPrice (the non-idempotent
  round(size*1e8)/Q64.64 encoding that clamped at ~1.845e11 and forked on restart).
- pkg/dex matcher: cross and level-key on the exact integer PriceUnits.
- pkg/dchain/settle: orderLock/floorsToZeroLock take exact integers; removed
  sizeToUnits/priceToInt float conversions and the dead restoreIntegerLane.

F2 — commit the custody ledger into the execution root:
- state.ComposeRoot/ExecutionRoot gain a ledgerRoot term (Merkle over balance:/
  locked:/orderasset: rows). Validators can no longer finalize agreeing on the book
  while holding different money.

F4 — release the ceil-lock/floor-charge residual on full fill:
- decrementMakerReserves now closes a fully-filled maker's reserve and returns the
  ceil(size*price)-floor residual to the owner (a filled order can never be
  cancelled), through the same unlock path a cancel uses. No more permanently
  frozen quote.

PROVEN: the two-phase cross-arch harness (arm64 build / amd64 Rosetta verify) now
derives byte-identical execRoots AND byte-identical custody ledgers for the
identical block (uint64(1e20) still differs at the language level — 0xffff.. vs
0x8000.. — but the exact-integer wire freezes the value in the bytes so no
per-validator float->int conversion remains). Red guards flipped to assert the fix;
conservation/byzantine/batch-parity/KAT/e2e all green (go test ./... -count=1),
go vet clean.

OUT OF SCOPE (flagged, NOT fixed here): redteam_unbacked_deposit_test documents a
SEPARATE critical finding (F9) — dex_deposit credits an arbitrary self-signed amount
with no C-side backing proof, exposed on the public RPC. That needs an access-control/
custody-model decision, not a mechanical fix, and is left for a follow-up.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-02 09:42:54 -07:00

630 lines
20 KiB
Go
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.
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command dexbench is a ZAP load generator and cross-arch determinism checker
// for the standalone D-Chain DEX venue (`dexd run`). It is a CLIENT: it speaks
// the FROZEN dex_* ZAP wire (pkg/zapwire) over github.com/luxfi/rpc, signs every
// money-moving frame exactly as the chains/dexvm proxy and the 0x9010 precompile
// do, and measures the full mempool -> BuildBlock -> Verify -> Accept round trip.
//
// There is no `dexd bench` subcommand in the binary; this is the canonical
// load-gen the testnet harness drives. It is a pure-Go leaf (CGO_ENABLED=0): it
// imports only pkg/zapwire (the frozen wire), github.com/luxfi/crypto (secp256k1
// + keccak), and github.com/luxfi/rpc (the ZAP transport) — never the cgo/GPU
// matcher in pkg/dex, so it builds and runs on any host (the load client need not
// be the venue host).
//
// WIRE/AUTH PARITY — re-defined constants, pinned to source.
// A client cannot import the cgo-tagged pkg/dchain, so (exactly like the proxy
// relay and the precompile adapter) it RE-DEFINES the three small auth pieces.
// They MUST stay byte-identical to pkg/dchain/auth.go + pkg/dchain/tx.go:
//
// - account fold : keccak256(accountDomain ‖ scheme[1] ‖ addr[20])[:16]
// (pkg/dchain/auth.go Account16 / accountDomain)
// - auth digest : keccak256(txAuthDomain ‖ scheme[1] ‖ type[1] ‖ nonce[8] ‖ body)
// (pkg/dchain/auth.go txAuthDigest / txAuthDomain)
// - auth envelope : scheme[1] ‖ nonce[8] ‖ sigLen[2] ‖ pubLen[2] ‖ sig ‖ pub
// (pkg/dchain/auth.go TxAuth.encode)
// - tx-type bytes : ensure_market=1 place=2 cancel=3 submit=4 (pkg/dchain/tx.go)
// - RPC payload : [frozen body] ‖ [auth envelope] (handler.go parseTxFrame)
//
// crypto.Keccak256(a,b,...) hashes the concatenation byte-identically to the
// server's hash.ComputeKeccak256Array — same primitive, so the digests match.
//
// MODES:
//
// dexbench -addrs h1:9099,h1:9100,h2:9099 -conns 32 -duration 30s
// open the venues, fan out -conns signed-write workers per venue, and
// report throughput + p50/p99/p999 latency over the wire.
//
// dexbench -verify -addrs amd64box:9099,arm64box:9099
// drive the IDENTICAL deterministic order sequence into two venues and
// assert byte-identical consensus output (order ids + fills). This is the
// cross-arch (amd64⇄arm64) determinism proof over the live boxes.
package main
import (
"context"
"crypto/ecdsa"
"encoding/binary"
"flag"
"fmt"
"os"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/luxfi/crypto"
"github.com/luxfi/dex/pkg/zapwire"
"github.com/luxfi/rpc"
)
// --- auth constants re-defined from pkg/dchain (see package comment) ----------
const (
txAuthDomain = "lux.dchain.tx.auth.v1" // pkg/dchain/auth.go
accountDomain = "lux.dchain.account.v1" // pkg/dchain/auth.go
schemeSecp = byte(0) // dex.AuthSecp256k1 (pkg/dex/auth.go)
txEnsure = byte(1) // TxEnsureMarket (pkg/dchain/tx.go)
txPlace = byte(2) // TxPlace
txSubmit = byte(4) // TxSubmit
)
// account is one signing identity: a deterministic secp256k1 key, its derived
// 16-byte settlement account (the zapwire user[16] field), and a monotone nonce.
// Each worker owns its own account so its nonce stream is strictly sequential
// (the consensus gate enforces monotone per-account nonces; one in-flight signed
// write per account keeps the stream gap-free).
type account struct {
priv *ecdsa.PrivateKey
user string // string(account16[:]) — the wire user field
nonce uint64
}
// newAccount derives a deterministic identity from a seed label. Deterministic
// derivation is what makes -verify reproducible across hosts: the same label
// yields the same key, account, and signatures on the amd64 and arm64 boxes.
func newAccount(label string) (*account, error) {
d := crypto.Keccak256([]byte("dexbench.secp.v1"), []byte(label))
var priv *ecdsa.PrivateKey
var err error
for { // re-hash on the vanishingly rare out-of-range scalar (matches tests)
priv, err = crypto.ToECDSA(d)
if err == nil {
break
}
d = crypto.Keccak256(d)
}
addr := crypto.PubkeyToAddress(priv.PublicKey).Bytes() // 20-byte keccak address
acc := crypto.Keccak256([]byte(accountDomain), []byte{schemeSecp}, addr)[:zapwire.UserSize]
return &account{priv: priv, user: string(acc)}, nil
}
// sign returns the auth envelope for (typ, nonce, body) under the account key and
// advances the nonce. The envelope is appended to the frozen body to form the RPC
// payload.
func (a *account) sign(typ byte, body []byte) ([]byte, error) {
var n [8]byte
binary.BigEndian.PutUint64(n[:], a.nonce)
digest := crypto.Keccak256([]byte(txAuthDomain), []byte{schemeSecp}, []byte{typ}, n[:], body)
sig, err := crypto.Sign(digest, a.priv) // 65-byte r‖s‖v
if err != nil {
return nil, err
}
env := make([]byte, 0, 1+8+2+2+len(sig))
env = append(env, schemeSecp)
env = append(env, n[:]...)
var l [2]byte
binary.BigEndian.PutUint16(l[:], uint16(len(sig)))
env = append(env, l[:]...)
env = append(env, 0, 0) // pubLen = 0 (secp recovers the key)
env = append(env, sig...)
a.nonce++
out := make([]byte, 0, len(body)+len(env))
out = append(out, body...)
return append(out, env...), nil
}
// marketID deterministically maps a market index to a 32-byte poolId. The venue
// accepts any 32-byte poolId; deterministic ids keep -verify reproducible.
func marketID(i int) [32]byte {
var p [32]byte
copy(p[:], crypto.Keccak256([]byte(fmt.Sprintf("dexbench.market.%d", i))))
return p
}
type config struct {
addrs []string
conns int
markets int
duration time.Duration
warmup time.Duration
op string
size float64
price float64
verify bool
dialTO time.Duration
callTO time.Duration
}
func main() {
var (
addrs = flag.String("addrs", "127.0.0.1:9099", "comma-separated venue ZAP endpoints (host:port)")
conns = flag.Int("conns", 16, "signed-write worker connections PER endpoint")
markets = flag.Int("markets", 4, "distinct markets each worker spreads orders across")
duration = flag.Duration("duration", 30*time.Second, "measured load duration")
warmup = flag.Duration("warmup", 3*time.Second, "warmup excluded from stats")
op = flag.String("op", "place", "load op: place (resting write) | cross (place+submit, exercises matcher)")
size = flag.Float64("size", 1.0, "order size")
price = flag.Float64("price", 100.0, "base price")
verify = flag.Bool("verify", false, "determinism mode: drive identical seq into 2 endpoints, compare output")
dialTO = flag.Duration("dial-timeout", 10*time.Second, "per-endpoint dial timeout")
callTO = flag.Duration("call-timeout", 30*time.Second, "per-call timeout")
)
flag.Parse()
cfg := config{
addrs: splitAddrs(*addrs),
conns: *conns,
markets: *markets,
duration: *duration,
warmup: *warmup,
op: *op,
size: *size,
price: *price,
verify: *verify,
dialTO: *dialTO,
callTO: *callTO,
}
if len(cfg.addrs) == 0 {
fmt.Fprintln(os.Stderr, "dexbench: -addrs is required")
os.Exit(2)
}
if cfg.markets < 1 {
cfg.markets = 1
}
if cfg.verify {
if err := runVerify(cfg); err != nil {
fmt.Fprintf(os.Stderr, "dexbench verify: %v\n", err)
os.Exit(1)
}
return
}
if err := runLoad(cfg); err != nil {
fmt.Fprintf(os.Stderr, "dexbench load: %v\n", err)
os.Exit(1)
}
}
func splitAddrs(s string) []string {
var out []string
for _, a := range strings.Split(s, ",") {
if a = strings.TrimSpace(a); a != "" {
out = append(out, a)
}
}
return out
}
// dial opens a ZAP client to addr (the default rpc transport is ZAP).
func dial(ctx context.Context, addr string) (rpc.Client, error) {
return rpc.Dial(ctx, addr)
}
// ensureMarket creates a market (admin frame: body only, no auth envelope).
func ensureMarket(ctx context.Context, c rpc.Client, pool [32]byte) error {
resp, err := c.CallRaw(ctx, zapwire.MethodEnsureMarket, zapwire.EncodeEnsureMarket(pool))
if err != nil {
return err
}
if len(resp) < 9 || resp[8] != zapwire.StatusPlaced {
return fmt.Errorf("ensure_market not placed: %x", resp)
}
return nil
}
// placeSell rests a SELL limit order. In a load made only of resting SELLs there
// is never an opposite bid to cross, so every order rests — a clean, sustainable
// unit for measuring the consensus-write + wire round trip.
func placeSell(ctx context.Context, c rpc.Client, a *account, pool [32]byte, price, size float64) (uint64, error) {
// Exact-integer wire: price is ×1e8 fixed-point, size is whole base units.
var pu, sz uint64
if price > 0 {
pu = uint64(price * float64(zapwire.PriceScale))
}
if size > 0 {
sz = uint64(size)
}
body := zapwire.EncodePlace(pool, zapwire.SideSell, pu, sz, a.user)
payload, err := a.sign(txPlace, body)
if err != nil {
return 0, err
}
resp, err := c.CallRaw(ctx, zapwire.MethodPlace, payload)
if err != nil {
return 0, err
}
id, status, err := zapwire.DecodeAck(resp)
if err != nil {
return 0, err
}
if status != zapwire.StatusPlaced {
return 0, fmt.Errorf("place rejected: status=%d resp=%x", status, resp)
}
return id, nil
}
// submitBuy crosses the book with a marketable BUY and returns the fills.
func submitBuy(ctx context.Context, c rpc.Client, a *account, pool [32]byte, limit, size float64) ([]zapwire.Fill, error) {
var pu, sz uint64
if limit > 0 {
pu = uint64(limit * float64(zapwire.PriceScale))
}
if size > 0 {
sz = uint64(size)
}
body := zapwire.EncodeSubmit(pool, zapwire.SideBuy, false, pu, sz, a.user)
payload, err := a.sign(txSubmit, body)
if err != nil {
return nil, err
}
resp, err := c.CallRaw(ctx, zapwire.MethodSubmit, payload)
if err != nil {
return nil, err
}
return zapwire.DecodeFills(resp)
}
// --- load mode ---------------------------------------------------------------
const sampleCap = 1 << 20 // per-worker latency samples retained for percentiles
type workerStat struct {
ops uint64
errs uint64
fills uint64
latNanos []int64
}
func runLoad(cfg config) error {
fmt.Printf("dexbench load: endpoints=%v conns/endpoint=%d markets=%d op=%s duration=%s warmup=%s\n",
cfg.addrs, cfg.conns, cfg.markets, cfg.op, cfg.duration, cfg.warmup)
type wkr struct {
addr string
idx int
}
var workers []wkr
for _, addr := range cfg.addrs {
for i := 0; i < cfg.conns; i++ {
workers = append(workers, wkr{addr: addr, idx: i})
}
}
stats := make([]workerStat, len(workers))
var started int64
var measuring int32
stop := make(chan struct{})
var wg sync.WaitGroup
dialCtx, dialCancel := context.WithTimeout(context.Background(), cfg.dialTO)
defer dialCancel()
for wi := range workers {
w := workers[wi]
st := &stats[wi]
st.latNanos = make([]int64, 0, 1<<16)
c, err := dial(dialCtx, w.addr)
if err != nil {
return fmt.Errorf("dial %s: %w", w.addr, err)
}
acc, err := newAccount(fmt.Sprintf("%s#%d", w.addr, w.idx))
if err != nil {
return err
}
// Ensure this worker's markets exist (idempotent). For cross mode also seed
// a deep resting SELL the taker submits against.
setupCtx, setupCancel := context.WithTimeout(context.Background(), cfg.callTO)
for m := 0; m < cfg.markets; m++ {
if err := ensureMarket(setupCtx, c, marketID(m)); err != nil {
setupCancel()
return fmt.Errorf("worker %s#%d ensure market %d: %w", w.addr, w.idx, m, err)
}
}
setupCancel()
wg.Add(1)
go func() {
defer wg.Done()
defer c.Close()
atomic.AddInt64(&started, 1)
runWorker(cfg, c, acc, st, &measuring, stop)
}()
}
// Wait for all workers to connect+setup, then run warmup, flip the measuring
// flag, run the measured window, and stop.
for atomic.LoadInt64(&started) < int64(len(workers)) {
time.Sleep(5 * time.Millisecond)
}
if cfg.warmup > 0 {
time.Sleep(cfg.warmup)
}
t0 := time.Now()
atomic.StoreInt32(&measuring, 1)
time.Sleep(cfg.duration)
atomic.StoreInt32(&measuring, 0)
elapsed := time.Since(t0)
close(stop)
wg.Wait()
report(cfg, stats, elapsed)
return nil
}
// runWorker drives signed writes as fast as the venue accepts them (closed-loop:
// one in-flight signed op per account, so the nonce stream stays sequential).
// Latency is recorded only while measuring==1.
func runWorker(cfg config, c rpc.Client, acc *account, st *workerStat, measuring *int32, stop <-chan struct{}) {
var maker *account
if cfg.op == "cross" {
maker, _ = newAccount("maker:" + acc.user)
// Seed a deep resting SELL per market so taker BUYs cross it.
for m := 0; m < cfg.markets; m++ {
ctx, cancel := context.WithTimeout(context.Background(), cfg.callTO)
_, _ = placeSell(ctx, c, maker, marketID(m), cfg.price, cfg.size*float64(1<<20))
cancel()
}
}
mkt := 0
for {
select {
case <-stop:
return
default:
}
pool := marketID(mkt)
mkt = (mkt + 1) % cfg.markets
ctx, cancel := context.WithTimeout(context.Background(), cfg.callTO)
start := time.Now()
var opErr error
var nFills int
switch cfg.op {
case "cross":
fills, err := submitBuy(ctx, c, acc, pool, cfg.price, cfg.size)
opErr = err
nFills = len(fills)
if err == nil && nFills == 0 {
// Liquidity drained: re-seed and retry next loop.
_, _ = placeSell(ctx, c, maker, pool, cfg.price, cfg.size*float64(1<<20))
}
default: // place
_, opErr = placeSell(ctx, c, acc, pool, cfg.price+1, cfg.size)
}
lat := time.Since(start).Nanoseconds()
cancel()
if atomic.LoadInt32(measuring) == 1 {
if opErr != nil {
st.errs++
continue
}
st.ops++
st.fills += uint64(nFills)
if len(st.latNanos) < sampleCap {
st.latNanos = append(st.latNanos, lat)
}
} else if opErr != nil {
// pre/post window error: surface once via errs so a broken run is visible
st.errs++
}
}
}
func report(cfg config, stats []workerStat, elapsed time.Duration) {
var totOps, totErr, totFills uint64
var lat []int64
for i := range stats {
totOps += stats[i].ops
totErr += stats[i].errs
totFills += stats[i].fills
lat = append(lat, stats[i].latNanos...)
}
sort.Slice(lat, func(i, j int) bool { return lat[i] < lat[j] })
thru := float64(totOps) / elapsed.Seconds()
fmt.Printf("\n=== dexbench RESULT ===\n")
fmt.Printf(" op : %s\n", cfg.op)
fmt.Printf(" endpoints : %d (%v)\n", len(cfg.addrs), cfg.addrs)
fmt.Printf(" workers : %d (%d/endpoint)\n", len(cfg.addrs)*cfg.conns, cfg.conns)
fmt.Printf(" elapsed : %s\n", elapsed.Round(time.Millisecond))
fmt.Printf(" ops (acked) : %d\n", totOps)
fmt.Printf(" errors : %d\n", totErr)
if cfg.op == "cross" {
fmt.Printf(" fills : %d\n", totFills)
}
fmt.Printf(" throughput : %.0f ops/sec\n", thru)
if len(lat) > 0 {
fmt.Printf(" latency (µs) : p50=%.1f p90=%.1f p99=%.1f p999=%.1f max=%.1f (n=%d)\n",
pct(lat, 0.50)/1e3, pct(lat, 0.90)/1e3, pct(lat, 0.99)/1e3, pct(lat, 0.999)/1e3,
float64(lat[len(lat)-1])/1e3, len(lat))
}
fmt.Println()
}
// pct returns the p-quantile of a pre-sorted slice (nearest-rank).
func pct(sorted []int64, p float64) float64 {
if len(sorted) == 0 {
return 0
}
i := int(p * float64(len(sorted)))
if i >= len(sorted) {
i = len(sorted) - 1
}
return float64(sorted[i])
}
// --- verify (cross-arch determinism) mode ------------------------------------
// runVerify drives the IDENTICAL deterministic order sequence into every endpoint
// and asserts byte-identical consensus output. Each standalone venue bootstraps
// the same deterministic genesis and runs the same deterministic VM, so identical
// ordered input MUST yield identical order ids and fills regardless of host arch.
// This is the live amd64⇄arm64 determinism proof.
func runVerify(cfg config) error {
if len(cfg.addrs) < 2 {
return fmt.Errorf("verify needs >=2 endpoints (e.g. amd64box:9099,arm64box:9099)")
}
fmt.Printf("dexbench verify: driving identical order sequence into %v\n", cfg.addrs)
ctx, cancel := context.WithTimeout(context.Background(), cfg.callTO*4)
defer cancel()
type conn struct {
addr string
c rpc.Client
// one signing identity per endpoint, derived from the SAME label so the
// signatures (hence tx ids) are identical across endpoints.
maker *account
taker *account
}
var conns []*conn
for _, addr := range cfg.addrs {
c, err := dial(ctx, addr)
if err != nil {
return fmt.Errorf("dial %s: %w", addr, err)
}
defer c.Close()
mk, _ := newAccount("verify.maker")
tk, _ := newAccount("verify.taker")
conns = append(conns, &conn{addr: addr, c: c, maker: mk, taker: tk})
}
pool := marketID(0)
// Deterministic two-sided book then a crossing taker — mirrors the e2e.
type step struct {
kind string // "ensure" | "place" | "submit"
side uint8
price float64
size float64
}
steps := []step{
{kind: "ensure"},
{kind: "place", side: zapwire.SideSell, price: 100.0, size: 10.0},
{kind: "place", side: zapwire.SideSell, price: 101.0, size: 5.0},
{kind: "place", side: zapwire.SideSell, price: 102.0, size: 7.0},
{kind: "submit", side: zapwire.SideBuy, price: 102.0, size: 18.0},
}
// Collect each endpoint's observable output (order ids + fills) per step.
type outcome struct {
ackID uint64
fills []zapwire.Fill
}
results := make([][]outcome, len(conns))
for ci, cn := range conns {
for _, s := range steps {
switch s.kind {
case "ensure":
if err := ensureMarket(ctx, cn.c, pool); err != nil {
return fmt.Errorf("%s ensure: %w", cn.addr, err)
}
results[ci] = append(results[ci], outcome{})
case "place":
var spu, ssz uint64
if s.price > 0 {
spu = uint64(s.price * float64(zapwire.PriceScale))
}
if s.size > 0 {
ssz = uint64(s.size)
}
body := zapwire.EncodePlace(pool, s.side, spu, ssz, cn.maker.user)
payload, err := cn.maker.sign(txPlace, body)
if err != nil {
return err
}
resp, err := cn.c.CallRaw(ctx, zapwire.MethodPlace, payload)
if err != nil {
return fmt.Errorf("%s place: %w", cn.addr, err)
}
id, _, err := zapwire.DecodeAck(resp)
if err != nil {
return fmt.Errorf("%s place ack: %w", cn.addr, err)
}
results[ci] = append(results[ci], outcome{ackID: id})
case "submit":
var spu, ssz uint64
if s.price > 0 {
spu = uint64(s.price * float64(zapwire.PriceScale))
}
if s.size > 0 {
ssz = uint64(s.size)
}
body := zapwire.EncodeSubmit(pool, s.side, false, spu, ssz, cn.taker.user)
payload, err := cn.taker.sign(txSubmit, body)
if err != nil {
return err
}
resp, err := cn.c.CallRaw(ctx, zapwire.MethodSubmit, payload)
if err != nil {
return fmt.Errorf("%s submit: %w", cn.addr, err)
}
fills, err := zapwire.DecodeFills(resp)
if err != nil {
return fmt.Errorf("%s submit fills: %w", cn.addr, err)
}
results[ci] = append(results[ci], outcome{fills: fills})
}
}
}
// Compare every endpoint against endpoint 0.
base := results[0]
mismatch := 0
for ci := 1; ci < len(results); ci++ {
for si := range base {
a, b := base[si], results[ci][si]
if a.ackID != b.ackID || !fillsEqual(a.fills, b.fills) {
mismatch++
fmt.Printf(" MISMATCH step=%d %s: %s=%+v %s=%+v\n",
si, steps[si].kind, cfg.addrs[0], a, cfg.addrs[ci], b)
}
}
}
fmt.Printf("\n=== dexbench VERIFY ===\n")
fmt.Printf(" endpoints : %v\n", cfg.addrs)
fmt.Printf(" steps : %d\n", len(steps))
// Report the crossing fills observed (same on every endpoint when PASS).
for si, s := range steps {
if s.kind == "submit" {
fmt.Printf(" submit fills @%s : %d fill(s)\n", cfg.addrs[0], len(base[si].fills))
for fi, f := range base[si].fills {
fmt.Printf(" fill %d: %.4f @ %.4f (takerSide=%d)\n", fi, float64(f.Size), float64(f.Price)/float64(zapwire.PriceScale), f.TakerSide)
}
}
}
if mismatch == 0 {
fmt.Printf(" RESULT : PASS — byte-identical consensus output across all endpoints (cross-arch determinism holds)\n\n")
return nil
}
return fmt.Errorf("%d mismatched outputs across endpoints (determinism BROKEN)", mismatch)
}
func fillsEqual(a, b []zapwire.Fill) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}