genesis canonicalized the field to EVMAddr per the EVMAddr/UTXOAddr
naming lock. Following the strip-aliases cascade through the workspace
(crypto v1.19.16, utxo v0.3.3, node v1.27.13, sdk v1.16.62).
luxfi/genesis renamed UnparsedAllocation → AllocationJSON,
UnparsedStaker → StakerJSON, with field ETHAddr (was EVMAddr in an
earlier rev). Update network/config.go to match.
engines/k8s/engine.go: //go:build k8s. k8s.io/client-go transitively
pulls google.golang.org/protobuf via k8s.io/kube-openapi → gnostic-models.
Opt-in build: go build -tags k8s ./... to enable Kubernetes deploy
support. Default builds skip the file entirely.
scripts/check-no-grpc.sh: extend the CI gate to fail on any of:
- google.golang.org/grpc
- grpc-ecosystem/grpc-gateway
- google.golang.org/protobuf
- go.opentelemetry.io/proto/otlp/*
- k8s.io/client-go
Each has a documented -tags escape hatch. The default-tag dep graph
must be clean of all five.
Results:
bash scripts/check-no-grpc.sh → ok
binary size: 37.9 MB → 26.7 MB (-11.2 MB)
strings grep protobuf|grpc → 0 hits
Per the project rule: ZAP internal, ZIP edge. ZAP is now the only
wire protocol the default netrunner binary contains.
Per the project rule: ZAP internal, no google.golang.org/protobuf on the
wire OR in the netrunner rpcpb package by default.
rpcpb/rpc.pb.go: //go:build grpc — protoc-gen-go output stays available
behind the legacy tag for external integrations that still want the
proto Go types.
rpcpb/types.go: //go:build !grpc — hand-written plain Go structs for
every Request/Response/spec/info type. Same names, same field types,
same json: tags so JSON wire compat with rpc.pb.go is preserved. Plus
the proto-style GetX() helpers callers expect.
Default-build dep graph for rpcpb is now SINGLE-FILE: rpcpb/types.go,
zero google.golang.org/protobuf, zero google.golang.org/grpc.
scripts/check-no-grpc.sh: CI gate. Walks the default-tag dep graph
and fails non-zero if google.golang.org/grpc or grpc-ecosystem/grpc-gateway
reappears. Wire this into CI to make the rule stick.
Per the project rule 'ZAP internal, ZIP edge': netrunner's 26-method
ControlService + PingService move off google.golang.org/grpc and
grpc-gateway onto a luxfi/zap envelope. Default builds now have ZERO
google.golang.org/grpc transitive deps.
New package: netrunner/zaprpc/
- protocol.go: MsgType (uint8 range, encoded in ZAP flags upper byte).
One stable wire ID per method. Append-only.
- dispatch.go: typed Bind[Req, Resp](d, msg, handler) — register a
'func(ctx, *Req) (*Resp, error)' handler. JSON encode/decode +
envelope framing happen here, handlers stay business-logic-only.
- server.go: thin wrapper around zap.Node hosting a Dispatcher.
- client.go: typed Call[Req, Resp](ctx, c, msg, req) — same DX as the
old gRPC client but transport is ZAP. Includes connect-retry around
the boot race.
server/bind.go: one-shot registration of all 26 RPC methods onto a
Dispatcher. The canonical map of 'what this server exposes'.
server/server.go: gRPC server + grpc-gateway HTTP bridge are gone.
Run() now starts the ZAP server and waits for rootCtx.Done. The
'soldier-on' isClientCanceled helper goes with the gRPC stream code.
client/client.go: full rewrite. Same Client interface (28 methods),
implementation is now one-line zaprpc.Call per method. StreamStatus
becomes a client-driven Status() poll on the requested interval —
ZAP has no native server-streaming, but the observable contract
(channel of ClusterInfo) is unchanged.
Deleted:
- rpcpb/rpc_grpc.pb.go (gRPC service stubs)
- rpcpb/rpc.pb.gw.go (grpc-gateway HTTP bridge)
The rpcpb message types (rpc.pb.go) stay — they're plain Go structs
with json: tags, used as the JSON payload format inside ZAP envelopes.
A future cleanup pass can replace them with hand-written structs to
drop google.golang.org/protobuf from the dep tree too.
go.mod cleanup: drop github.com/grpc-ecosystem/grpc-gateway/v2,
google.golang.org/grpc, google.golang.org/genproto/googleapis/api.
Pin luxfi/proto v1.0.0 (was luxfi/protocol v0.0.4 — stale rename
artifact). Add luxfi/zap v0.2.0.
Tests: zaprpc/zaprpc_test.go round-trips request/response and proves
the error path propagates through the envelope.
No backwards compat — forwards perfection per the project rule.
network/config.go:
- 'beacon node P2P port not ready, continuing anyway' was a soldier-on
pattern: if the beacon's staking port isn't up before the next node
tries to dial it, bootstrap will silently fail. Fail loudly instead.
- Switch UnparsedAllocation field names ETHAddr→EVMAddr, LUXAddr→UTXOAddr
to track the genesis package rename.
Single LICENSING.md file referencing the canonical three-tier IP and
licensing strategy at github.com/luxfi/.github/blob/main/profile/README.md.
LICENSE file is unchanged; this only adds a navigational pointer.
Renames LUX_-prefixed env vars to canonical, non-noisy forms across
genesis_config, blockchain, network, examples, tests, genkeys, server:
- LUX_MNEMONIC -> MNEMONIC
- LUX_PRIVATE_KEY -> PRIVATE_KEY
- LUX_KEYS_DIR -> KEYS_DIR
- LUX_BINARY_PATH -> BINARY_PATH
- LUX_NETWORK_TYPE -> NETWORK_TYPE (doc only)
- LUX_GPU_EVM -> GPU_EVM (doc only)
- LUX_GPU_WORKER_PID-> GPU_WORKER_PID
Also fixes an obvious infinite-recursion bug in
local/genesis_config.go::getMnemonic() that the previous LUX_MNEMONIC
fallback line had become — the helper now reads MNEMONIC then
LIGHT_MNEMONIC and returns. Without this fix, callers reaching the
fallback would have hung the process.
BREAKING: external callers (CI, dev scripts, operators) must update
env var names. Per CLAUDE.md no-backwards-compatibility rule, the old
forms are removed.
luxfi/sdk v1.16.57 renamed APIBlockchain.ChainID → APIBlockchain.NetID
to match the platformvm wire format. Update server/network.go's two
call sites to use blockchain.NetID instead of blockchain.ChainID;
the local chainInfo struct field is left as `chainID ids.ID` so
downstream code that reads it isn't affected.
Bumps luxfi/sdk to v1.16.57 + transitive Z-Wing PQ stack tracked in
v1.18.1.
luxfi/constants and luxfi/genesis just split LocalID and CustomID
into distinct constants:
LocalID = 1337 canonical local single/multi-node dev
CustomID = 0 sentinel for user-defined non-well-known networks
This repo now uses LocalID everywhere it actually meant the local
dev network, and constants.LocalID for the same in places that
imported through luxfi/constants. Plus configs.LocalnetID →
configs.LocalID and configs.LocalnetChainID → configs.LocalChainID
to track the renamed constants.
Net effect on netrunner:
* NewLocalConfig uses configs.LocalID (was configs.CustomID/1337)
* NewCanonicalCustomConfig uses configs.LocalID
* NewLocalConfigFromMnemonic uses configs.LocalID
* network/config.go default switch now lists LocalID alongside Mainnet/Testnet
* server/network.go default networkID set to LocalID for the local server
The Custom* constants remain available for genuine user-defined
networks; on those, addresses use the "custom" HRP from
luxfi/constants and look like P-custom1..., X-custom1...
zapwire (Z-Wing PQ control RPC) and network tests still green; the
heavy local/ integration tests that actually spawn luxd are out of
scope for this change.
Replace plain TCP zap.Dial / zap.Listen with zwing.DialZAP /
zwing.ListenZAP. zapwire.Dial and zapwire.NewServer now require a
*zwing.Config carrying a LocalIdentity — there is no cleartext mode.
Every netrunner control connection is X-Wing-encapsulated,
ChaCha20-Poly1305-encrypted, and identity-pinned via Ed25519 +
ML-DSA-65 hybrid signatures.
Wire format and opcode set are unchanged; only the dial/listen
seams are upgraded. The 14 e2e tests are updated to mint ephemeral
identities per test (newTestRig) and pin the server side, which
also proves the handshake actually authenticates.
Pairs with luxfi/zwing v0.2.1 (DialZAP / ListenZAP) and luxfi/api
v1.0.10 (zap.NewListener).
100% native ZAP, no protobuf, no gRPC, no codegen. Hand-written Go
types in zapwire/types/ encode/decode against luxfi/api/zap.Buffer
and zap.Reader directly. Server dispatches via a single zap.Handler
that switches on opcode (and sub-opcode for fan-out via OpStart).
Opcodes 60..63 reserved for netrunner control. Sub-opcodes (under
OpStart) provide RPC fan-out without consuming the limited opcode
range. Default port: 9999.
Migration plan:
Phase 1 (this commit): Ping, RPCVersion, Health, URIs, Status — 5
of 26 control RPCs landed with full e2e test coverage
Phase 2: lifecycle (Start, Stop, AddNode, RemoveNode, RestartNode,
PauseNode, ResumeNode, AttachPeer, SendOutboundMessage)
Phase 3: chain ops (CreateBlockchains, CreateChains, etc.)
Phase 4: snapshots (Save/Load/Remove/GetSnapshotNames + Hot)
Phase 5: streaming (StreamStatus over persistent ZAP conn)
Phase 6: swap netrunner/client/client.go + cli callers, delete rpcpb
Tests: go test ./zapwire/... — 5 RPCs e2e, all green.
- Replace github.com/luxfi/geth/crypto (nonexistent) with
github.com/luxfi/crypto for HexToECDSA, GenerateKey, S256, Sign, FromECDSA
- Use common.PubkeyToAddress and common.Keccak256 (geth/common wrappers)
to avoid Address type mismatch between crypto/common and geth/common
- Fix log.NewWrappedCore (nonexistent) to log.New in consensus/dex tests
- Fix big.NewInt(1e19) overflow — 1e19 exceeds int64 max
- Fix toCallMsg returning interface{} instead of ethereum.CallMsg
- Add missing ethereum import in dex_chaos_test.go
Verified: go vet -tags chaos ./tests/ && go vet -tags gpu_chaos ./tests/
Fix compilation of gpu_evm_chaos_test.go by using proper ethereum.CallMsg
type from luxfi/geth root package for eth_call operations. All 8 tests
compile and list correctly with -tags gpu_chaos.
Tests: WriteConflict, ReadWriteSerializability, ECRecoverBatch,
Keccak256Batch, CrashMidBlock, MemoryPressure, StateRootConsistency,
PrecompileBatchVerify.