mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
Every offset the C++ P-chain had was stated by hand, in four places that had
to agree: txs.hpp named the envelope and each of nineteen transactions'
fields, txs_wire.hpp named the strides, txs_wire.cpp did the arithmetic, and
block.hpp said it again for blocks. Four more files — genesis, warp, warpmsg
and components — each named their own. That is the same number written down
six times, and the fix for it is a generator.
Four schemas now say it once, and zapgen prints the readers, the builders and
the offsets from them:
schema/wire.zap transactions, blocks, credentials, an owner set,
and the two fx envelopes an output is ordered by
schema/genesis.zap the genesis blob
schema/warp.zap the message and the aggregated proof over it
schema/warpmsg.zap what an L1 and the P-chain say to each other
One schema per namespace, because a package name is what a backend renders as
a namespace and a file declares one.
What is left beside them — 283 lines, down from 1,463 — holds not one offset.
It holds the ARRANGEMENT, which is a fact about this chain rather than about
the format: an output's owner addresses do not live in the output, they live
in one run shared by the whole transaction, and the output names a slice of
it. That is what lets a transaction be read without walking it, and it was a
decision this chain made, so it is stated once instead of at the nineteen
places a transaction is built.
The bytes did not move, and that is checked rather than asserted. txs_test,
block_test, genesis_test and components_test compare 39 buffers against hex
the GO REFERENCE printed — a round trip would only prove this port agrees
with itself, which is what a fork also does. All 18 suites pass, and the
chain's answers to all 159 P-chain corpus vectors are byte-identical to the
answers it gave before.
Two things came out of doing it. The generator now refuses a field named as
the struct that holds it: warp's Hash message has a Hash field, and the
accessor for it is a constructor with a return type that the compiler rejects
three files from the schema that caused it — one rule in the front end, all
three backends. And zap.cmake pinned the generator to the runtime's tag, a
version that does not exist in the generator's repository, so that target had
never run; the generator has its own pin now, at zap-proto/go v1.9.0, which
reproduces every committed header byte for byte.
54 lines
2.3 KiB
CMake
54 lines
2.3 KiB
CMake
# Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
# SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
#
|
|
# The one ZAP implementation the C++ chains speak.
|
|
#
|
|
# ZAP is a bidirectional binary protocol with pipelining, and its C++ SDK is a
|
|
# published artifact — github.com/zap-proto/cpp, pinned to a tag below. It is
|
|
# reached as a package, never as a path into a checkout and never vendored: a
|
|
# chain that carries its own copy of the wire is a chain that can drift from
|
|
# it, which is exactly what happened when both of these chains hand-wrote
|
|
# frame handling and the two copies stopped agreeing.
|
|
#
|
|
# An installed package wins, so a machine that has one does not re-fetch. Set
|
|
# -DCMAKE_PREFIX_PATH=<install> to use it.
|
|
|
|
# The runtime the chains link, and the generator that prints what calls it.
|
|
# Two repositories, two versions: a tag from one does not exist in the other,
|
|
# and naming one tag for both is how the generator target came to point at a
|
|
# version that was never published.
|
|
set(ZAP_TAG v0.1.1) # github.com/zap-proto/cpp — the runtime
|
|
set(ZAPGEN_TAG v1.9.0) # github.com/zap-proto/go — the generator
|
|
|
|
find_package(Zap QUIET)
|
|
if(NOT Zap_FOUND)
|
|
include(FetchContent)
|
|
FetchContent_Declare(Zap
|
|
GIT_REPOSITORY https://github.com/zap-proto/cpp.git
|
|
GIT_TAG ${ZAP_TAG}
|
|
GIT_SHALLOW TRUE)
|
|
FetchContent_MakeAvailable(Zap)
|
|
message(STATUS "zap: ${ZAP_TAG} from github.com/zap-proto/cpp")
|
|
else()
|
|
message(STATUS "zap: installed package ${Zap_VERSION}")
|
|
endif()
|
|
|
|
# zap_schema(NAME SCHEMA OUTDIR) declares the target that regenerates a chain's
|
|
# wire accessors from its schema.
|
|
#
|
|
# The output is committed beside the schema, so a build needs neither Go nor the
|
|
# generator; this target is for after an edit to the schema:
|
|
#
|
|
# cmake --build build --target <name>
|
|
#
|
|
# The generator is fetched at its own published tag. Its output is checked in,
|
|
# so what a build compiles is what someone read; running this target is how a
|
|
# schema edit reaches the header beside it.
|
|
function(zap_schema name schema outdir)
|
|
add_custom_target(${name}
|
|
COMMAND ${CMAKE_COMMAND} -E env GOFLAGS=-mod=mod
|
|
go run github.com/zap-proto/go/cmd/zapgen@${ZAPGEN_TAG}
|
|
-lang cpp -single -out ${outdir} ${schema}
|
|
COMMENT "zapgen ${ZAPGEN_TAG}: ${schema} -> ${outdir}")
|
|
endfunction()
|