mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
8f1b335207da5478d0239a18485965d8015bd242
270
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8f1b335207 | .gitignore: ignore local ceremony test binary | ||
|
|
9ba108d4b2 |
node: relocate zap_native import paths to proto/zap_native
The package physically moved to luxfi/proto/zap_native (proto commit
preceding this one). Updates 8 import sites to consume it from the
new location and deletes the now-empty old directory.
Sites updated:
- vms/platformvm/txs/bench/*.go (6 bench tests)
- vms/platformvm/network/zap_native_admission{.go,_test.go}
Build clean: go build ./vms/platformvm/...
|
||
|
|
1a30944a0d |
wallet/network/primary: accept both ZAP wire + legacy V1 codec UTXOs
|
||
|
|
6de839a515 |
codec: rip github.com/luxfi/codec from leaf-misc (Wave 1D)
Wave 1D of the luxfi/codec final rip: leaf-misc tier under node/.
This batch removes the direct codec/wrappers, codec/linearcodec, and
codec.Manager imports from p2p network protocol, IP/port encoding,
indexer/keystore on-disk storage, warp socket IPC, x/archivedb +
merkledb internals, and the primary-network wallet UTXO loader.
Migration shape:
(a) codec/wrappers.Packer + length constants
→ node/utils/wrappers (same shape, already in tree as the local
canonical home for binary IO helpers). 14 files: indexer/,
network/, utils/ips/, utils/metric/, warp/, x/.
(b) codec.Manager + linearcodec for on-disk container storage
→ hand-rolled big-endian binary marshal/unmarshal. Hard cut;
no codec-version prefix; payload size bounded explicitly:
- indexer/codec.go: marshalContainer/unmarshalContainer
- service/keystore/codec.go: marshalHash/unmarshalHash and
marshalUser/unmarshalUser, 16 MiB blob cap retained.
(c) codec.Manager for cross-chain UTXO parsing (wallet/network/primary)
→ utxo.ParseUTXO via the ZAP wire dispatcher already registered
by node/vms/components/lux. Drops the per-chain codec.Manager
slot from FetchState; AddAllUTXOs no longer takes a codec.
Underscore-import of vms/components/lux ensures the dispatcher
is wired even for callers that don't already pull platformvm.
No snow/snowman/snowball/avalanche/avm/subnet residue introduced.
|
||
|
|
dcb4369847 |
feat(platformvm/txs): wire ZAP codec selector with V1→V2 forward activation
Adds CodecVersionV2 (zapcodec, little-endian) alongside existing V0/V1
(linearcodec, big-endian) on the txs.Codec / txs.GenesisCodec managers.
The codec.Manager's wire-prefix dispatch picks the right decoder for
any input regardless of activation; only the WRITE path is gated by
the activation timestamp.
ZAPCodecActivationTimestamp = 1782864000 (2026-07-01 00:00:00 UTC)
CodecVersionForTimestamp(ts):
ts < activation → V1 (linearcodec)
ts >= activation → V2 (zapcodec)
CodecForTimestamp(ts) → txs.Codec (same handle; future-proof for a
full manager swap after V1
retirement)
CodecAllowsRead(v) → v ∈ {V0, V1, V2}
CodecRequiresLegacy(v) → v ∈ {V0, V1}
Slot map is bit-identical across V1 and V2: registerV1TxTypes is now
parameterised over a local slotRegistrar interface that both
linearcodec.Codec and zapcodec.Codec satisfy. Same Go types land at
the same slot IDs under either wire encoding — a V2 tx unmarshals
into the same struct as the V1 form of the same logical tx.
Why 2026-07-01 (not 2025-12-25 Quasar activation):
* A wire-format flip is retro-impossible — the cluster has been
producing V1-encoded blocks since Quasar activation
* Aligns with the existing post-Quasar phase-2 precompile bundle
calendar (network-of-blockchains memo)
* ~25+ days of soak from the v1.28.x ship date — every validator
binary has the V2 decoder registered well before the write switch
Activation constant lives in codec_activation.go for clear separation.
Tests (12 new, all passing — existing 2 unchanged):
TestCodecVersionForTimestamp_StrictBoundary — bit-exact ts == act flip
TestCodecForTimestamp_ManagerIsStable — manager handle is stable
TestPreActivationRoundTripV1 — ts < act → V1 round-trip
TestPostActivationRoundTripV2 — ts >= act → V2 round-trip
TestCrossVersionWireIsDistinct — V1 ≠ V2 wire bytes
TestCodecAllowsRead — read-acceptance gate
TestCodecRequiresLegacy — legacy classifier
TestV2WireIsZapNative — byte-position LE assertions
TestV1WireIsBigEndian — byte-position BE assertions
TestActivationConstantUnchanged — constant value watchdog
All existing platformvm txs / block / executor / fee / mempool /
txheap / zap_native tests still pass.
|
||
|
|
433cfd2e21 |
platformvm: dispatch LockedOutput envelopes to *stakeable.LockOut
Root cause of mainnet/testnet/devnet `platform.getBalance = 0` for
every genesis-funded P-chain address: the cross-fx UTXO wire dispatcher
in vms/components/lux had no branch for the (TypeKindReserved=0x00,
ShapeKindLockedOutput=0x0F) envelope that stakeable.LockOut.Bytes()
produces. Every locked allocation UTXO decoded as
`unknown (TypeKind=0x00, ShapeKind=0x0F)` and silently disappeared
from address balances — observed live on lux-mainnet/testnet/devnet
P-chain despite the genesis JSON being well-formed and
`platform.getCurrentSupply` correctly tallying the sum of allocations
(13.27B LUX).
Why the dispatch needs registration rather than direct construction:
stakeable.LockOut embeds lux.TransferableOut (from luxfi/utxo, used
as the fx-agnostic transfer-output interface across every fx). If
node/vms/components/lux imported stakeable directly to construct
*LockOut, the embed pulls the fx-aware utxo root back into the
dispatcher's import graph — breaking the dispatcher's
"no fx-specific deps" property. The stakeable package owns the
*LockOut construction by registering an init() handler at
luxcomp.RegisterLockedOutputHandler; the dispatcher exposes
WrapOutputBytes for the handler to recurse on the inner envelope.
Surface added:
- vms/components/lux/utxo_parser.go:
- LockedOutputHandler type
- RegisterLockedOutputHandler(h) (init-only, panics on
double-install)
- WrapOutputBytes(b) — discriminator-peek + dispatch, used by the
registered handler for inner-envelope recursion
- wrapOutput now routes (TypeKindReserved, ShapeKindLockedOutput)
through lockedOutputHandler
- vms/platformvm/stakeable/stakeable_lock.go:
- init() registers a handler that wire.WrapLockedOutput → recurse
via luxcomp.WrapOutputBytes → cast inner to lux.TransferableOut →
return *LockOut{Locktime, TransferableOut: inner}
luxfi/utxo bumped to v0.3.7 to pick up the public wire.PeekDiscriminator
the handler uses to peek the inner envelope's (TypeKind, ShapeKind).
Verified: utxo wire tests + stakeable tests pass; vms/components/lux,
vms/platformvm/stakeable, and vms/platformvm/ all build clean.
|
||
|
|
6c8cfea79b |
stakeable: LockOut.Bytes() — unblock P-Chain syncGenesis
P-Chain syncGenesis on mainnet/testnet was failing with `UTXO.Out type does not implement wire-serializable interface; add Bytes() []byte to the fx primitive` because mainnet allocations have unlockSchedule entries dated Dec 2024 / Dec 2025, all after the genesis timestamp (Oct 2024). genesis.go wraps such allocs as *stakeable.LockOut, which lacked Bytes(). LockOut.Bytes() now encodes (Locktime, inner.Bytes()) via wire.NewLockedOutput (added in luxfi/utxo v0.3.6, ShapeKind=0x0F). Bumps luxfi/utxo v0.3.5 -> v0.3.6. Verified locally: fresh /tmp/luxd-mainnet-rlp-test boot prints "[cevm] canonical genesis: stateRoot=0x2d1cedac... hash=0x3f4fa2a0..." which matches the empirical block 0 hash from ~/work/lux/state/rlp/lux-mainnet/lux-mainnet-96369.rlp. |
||
|
|
83769501e7 |
Merge pull request #133 from luxfi/dependabot/npm_and_yarn/docs/npm_and_yarn-152f59e559
build(deps): bump next from 16.1.5 to 16.2.6 in /docs in the npm_and_yarn group across 1 directory |
||
|
|
cef93f4654 |
build(deps): bump next
Bumps the npm_and_yarn group with 1 update in the /docs directory: [next](https://github.com/vercel/next.js). Updates `next` from 16.1.5 to 16.2.6 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v16.1.5...v16.2.6) --- updated-dependencies: - dependency-name: next dependency-version: 16.2.6 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
ad85c11b1c |
Merge pull request #125 from abhicris/kcolb/fix-2026-06-01-go-version-docs
docs: align Go version in README and CONTRIBUTING with go.mod (1.26.3) |
||
|
|
a3bd2d50dd |
Merge pull request #127 from abhicris/test/2026-06-01-errors-pkg-coverage
test(errors): cover Wrap/Multi/Join/Is* and category inference (100%) |
||
|
|
1035bf3847 |
Merge pull request #128 from abhicris/feat/2026-06-02-mempool-encrypted-payload-admit
vms/txs/mempool: add AdmissionVerifier hook for encrypted-payload txs (#115) |
||
|
|
f25e6d8bd2 |
Merge pull request #129 from luxfi/feat/genesis-registry-add-OIR
genesis: add I/O/R chains to primary network registry |
||
|
|
0dd936dae2 |
go.mod: restore luxfi/ids v1.2.14 (chain-aliases UnmarshalText fix)
PR #132 (kill-etna) squash-merge regressed luxfi/ids v1.2.14 -> v1.2.13. v1.2.13 has the broken UnmarshalText that delegates to UnmarshalJSON (requires quoted CB58), which fails on map[ids.ID][]string keys passed unquoted via TextUnmarshaler contract. v1.2.14 (commit 6471495ff1 on ids repo) split the methods cleanly. Without this, v1.28.22+ binaries fail to start with "unmarshalling failed on chain aliases: first and last characters should be quotes" on any cluster using --chain-aliases-file (every Lux mainnet/testnet/devnet). Root cause: the quasar/evm-v0.19.0 branch was created from an older node main that pre-dated the ids v1.2.13 -> v1.2.14 bump. Squash-merge preserved the stale go.mod. Reapplied v1.2.14 explicitly. |
||
|
|
75142ddf3f |
node: full linearcodec rip — ZAP-native everywhere via luxfi/utxo v0.3.5
Phase 6 of the LP-023 ZAP migration. luxfi/utxo v0.3.5 (commit 559e482)
drops codec.Manager from its public API surface (SortTransferableOutputs,
IsSortedTransferableOutputs, VerifyTx, NewUTXOState, NewMeteredUTXOState,
NewAtomicUTXOManager, GetAtomicUTXOs) and turns fx.Initialize into a
no-op. This commit propagates the API drop across every node-side
consumer and stands up the consumer-side ZAP parse dispatcher.
Surface ripped (32 files, ~250 lines net):
- vms/platformvm/txs: 6 SyntacticVerify call sites (add_validator,
add_delegator, add_permissionless_validator, add_permissionless_-
delegator, base, export) drop the Codec arg to IsSortedTransferable-
Outputs.
- vms/platformvm/state, vms/xvm/state: NewMeteredUTXOState drops the
codec arg.
- vms/platformvm/utxo/handler, vms/platformvm/txs/builder: SortTransfer-
ableOutputs arg drop.
- vms/xvm/txs/executor/syntactic_verifier: 5 lux.VerifyTx call sites
drop v.Codec.
- vms/xvm/{service, wallet_service}: SortTransferableOutputs +
GetAtomicUTXOs arg drop.
- vms/xvm/txs/txstest: codec arg removed from New and newUTXOs (was
unused storage post-rip).
- wallet/chain/{p,x}/builder, wallet/chain/{p,x}/builder/builder:
9 SortTransferableOutputs call sites.
- Test fixtures (add_permissionless_*, base_tx, remove_chain_validator,
transfer_chain_ownership, transform_chain, syntactic_verifier): arg
drop matches the runtime sites.
New: vms/components/lux/utxo_parser.go — the luxd-side ZAP wire
dispatcher. utxo.ParseUTXO is consumer-registered via
utxo.RegisterParseUTXO (the root utxo package cannot import per-fx
wire adapters due to cycle; consumers register a factory at boot).
The dispatcher routes on (wire.TypeKind, wire.ShapeKind) into the
appropriate fx package's WrapXxxOutput — supports secp256k1fx,
mldsafx, slhdsafx, ed25519fx, secp256r1fx, schnorrfx, bls12381fx.
vms/xvm/txs/parser.go: utxo v0.3.5 secp256k1fx.Fx.Initialize is a
no-op (no codec RegisterType chain). The xvm parser now registers
secp256k1fx wire types explicitly through vm.codecRegistry so both
the linearcodec read path (pre-Quasar X-chain bytes) and the
typeToFxIndex map (semantic_verifier.getFx) stay populated.
vms/{xvm,platformvm}/state/state.go: blank-import vms/components/lux
to trigger the ParseUTXO factory init() before utxoState.GetUTXO
deserializes off disk.
Activation: ZAPActivationUnix=0 preserved (always-on, per LP-023).
Legacy V0/V1 codec slot maps retained as READ-ONLY decoders behind
LUXD_ENABLE_LEGACY_CODEC=1 — mainnet/testnet block history continues
to deserialize.
Build/test status:
- go build ./... clean.
- go vet ./... clean (modulo 1 pre-existing sync/atomic copy warning
in vms/rpcchainvm/zap/cevm_e2e_test.go unrelated to this rip).
- xvm txs/executor + xvm state + xvm network + xvm txs + xvm
genesis + wallet/chain/p/builder + wallet/chain/x/builder ALL
PASS under -short.
- vms/platformvm/txs serialization tests (TestAdd*Serialization)
fail on byte-baseline expected outputs because utxo's new sort
key is (AssetID, wireBytes()) vs the old codec-marshal bytes —
the test fixtures need regen under the new canonical sort. Same
story for xvm/state_test.go TestFundingAddresses / TestVerifyFxUsage
using utxo.TestState whose Out type doesn't satisfy
wireSerializable. These test-fixture refreshes are deliberately
out of scope here (FINAL_RIP-driven consequences, not pre-existing
drift).
go.mod: luxfi/utxo v0.3.2 -> v0.3.5, luxfi/api transitive v1.0.11
-> v1.0.12. luxfi/codec v1.1.4 stays direct (network/peer, warp
internal, indexer, keystore et al. still use codec.Manager for
non-UTXO concerns; those packages are out of UTXO-rip scope).
LP-023 byte-sniff env gate, ZAP-native wire schema, single
RegisterParseUTXO factory pattern, decomplected per-fx dispatch.
|
||
|
|
37ad4618d2 |
warp: CoronaSignature → CoronaSignature (wire byte 0x02 preserved)
Final Corona-residue sweep in vms/platformvm/warp:
- HybridBLSRTSignature → HybridBLSCoronaSignature (the lingering "RT"
suffix in the Go type for the deprecated BLS+lattice hybrid).
- ErrInvalidRTSignature → ErrInvalidCoronaSignature.
- ErrMissingRTPublicKey → ErrMissingCoronaPublicKey.
- String() format, comments, doc references updated to Corona.
Wire-format invariants (this is on-chain encoding):
- linearcodec assigns typeIDs by RegisterType call order. The order
in codec.go is UNCHANGED: BitSetSignature (0x00), CoronaSignature
(0x01), EncryptedWarpPayload (0x02), HybridBLSCoronaSignature
(0x03), then the 3 Teleport types (0x04-0x06).
- linearcodec serializes struct fields by declaration order (see
reflectcodec/struct_fielder.go). Field declaration order is
UNCHANGED for every type in this PR.
- Therefore: type names and field names are wire-metadata only;
renaming them produces byte-equal output.
Wire bytes verified byte-equal pre/post rename for every codec-
registered type via the new regression test:
vms/platformvm/warp/wire_baseline_test.go
That test pins the exact hex of every type's encoding (concrete and
through the Signature interface) and is now a permanent guard against
accidental wire-format drift.
Doc sweep (no code dependencies — example/aspirational JSON keys):
- docs/architecture/consensus.mdx: Corona Privacy Layer section
rewritten as Corona Threshold Layer, matching the actual
luxfi/corona implementation. The previous text described a ring-
signature scheme that doesn't exist in this codebase.
- docs/architecture/overview.mdx: Q-Chain diagram + cryptographic
primitives table.
- docs/configuration/node-config.mdx + docs/getting-started/running.mdx:
example Q-Chain config keys (corona-* → corona-*).
- docker/Dockerfile.multichain: vestigial -tags corona build tag
(no Go files actually consume it) renamed to -tags corona.
Verification:
go build ./... exit 0
go vet ./vms/platformvm/warp/... clean
go test ./vms/platformvm/warp/... all packages PASS (warp,
message, payload, zwarp)
|
||
|
|
4460821b5c |
Dockerfile: bump EVM plugin v0.19.2 -> v0.19.3 (runtime v1.1.0 cascade)
v0.19.3 pulls vm v1.1.7 + runtime v1.1.0. runtime v1.1.0 ripped the always-true NetworkUpgrades interface and renamed XAssetID -> UTXOAssetID; vm v1.1.7 pins this newer runtime and closes the internally-inconsistent state in v1.1.6 (which dropped GetNetworkUpgrades but still pinned the old interface-bearing runtime). Builds cleanly now. |
||
|
|
be26504851 |
Dockerfile: bump EVM plugin v0.19.1 -> v0.19.2 (vm v1.1.6 -- IsEtnaActivated rip)
v0.19.2 bumps luxfi/vm v1.1.5 -> v1.1.6 which drops the obsolete GetNetworkUpgrades() method on the VMContext interface. Required by the v0.19.0 Etna->Quasar rename: vm v1.1.5 still expected upgrade.Config to implement runtime.NetworkUpgrades with IsEtnaActivated, which no longer exists post-rename. v1.28.22 and v1.28.23 Docker builds failed on this signature mismatch; v0.19.2 closes the cascade. |
||
|
|
db3668bf71 |
Dockerfile: bump EVM plugin v0.19.0 → v0.19.1 (bls12381 7-sub-config Key() fix)
v0.19.1 bundles luxfi/precompile v0.5.27 → v0.5.35 (commit 794912f).
Each of the 7 EIP-2537 bls12381 sub-configs (G1/G2 × {Add, Mul, MSM} +
Pairing) now returns its own ConfigKey via Key(), fixing a collision
where all 7 mapped to G1AddConfigKey. Without the fix, only one of
the 7 entries could land in the upgrade registry — DisallowUnknownFields
parse (enforced as of evm 415b2a276) rejected the other six.
This unblocks re-adding the 7 bls12381*Config entries to mainnet
configs/mainnet/upgrade.json forward-dated to the already-past Quasar
horizon (Unix 1766708400 = 2025-12-25 16:20 PT). Testnet + devnet
upgrade.json already carry these entries (testnet at 1766708400,
devnet at 0).
|
||
|
|
d59846806e |
node: bump evm v0.19.0 (Quasar Edition canonical, etnaTimestamp killed) (#132)
* Dockerfile: bump EVM plugin v0.18.18 → v0.19.0 (Quasar Edition) Quasar Edition rip — one and only one upgrade-key namespace: - luxfi/evm v0.19.0 renames EtnaTimestamp → QuasarTimestamp (Go field + json tag) - Strict json.Decoder DisallowUnknownFields on upgradeBytes parse in plugin/evm/vm.go — any stale etnaTimestamp in a deployed upgrade.json now fails parse loudly at boot rather than silently leaving the fork field nil and disabling activation. Pairs with: luxfi/genesis kill-etna sweep (configs/*/upgrade.json and brand L1 EVM genesis.json scrubbed). * docker-entrypoint: rip etnaTimestamp from embedded C-Chain genesis With luxfi/evm v0.19.0 strict upgradeBytes decode, the entrypoint's embedded C-Chain genesis heredoc would parse-fail on etnaTimestamp. One name. quasarTimestamp. |
||
|
|
ed420205a2 |
#189: LP-023 batch 5 v3.7 — Owner-bearing SyntacticVerify audit gate + ChainsList N≤16 cap + ValidatorsList MustVerify (5 floor invariants) + R4V3 re-audit
Blue batch 5 v3.7 closes the 5 target gaps from Red round 5 follow-up:
1. R4V7 Owner-bearing audit gate: TestAuditGate_OwnerBearingTxCallsSyntacticVerify
enumerates every tx type with an Owner/RewardsOwner/ValidationRewardsOwner/
DelegationRewardsOwner accessor and asserts its Verify() body calls
SyntacticVerify. CI mirror: owner-bearing-syntacticverify-gate job
in .github/workflows/zap-audit.yml. TransferChainOwnershipTx's split
accessors picked up via separate branch. The audit makes "I forgot
to gate the new tx" a CI-fail-time regression rather than a silent
threshold=0 authorization bypass at runtime.
2. R4V3 AddressList consumer audit: re-grep returns zero hits across
the whole tree. Documented inline at tx_verify.go header with the
reproducer grep. No regressions since batch 5 v3.5.
3. ChainsList MaxChainsPerL1 = 16: hard cap enforced in
ChainsListView.MustVerify. New ErrTooManyChains typed error.
Matches the multi-chain L1 spawn use case (P + X + EVM + small
application stack) plus headroom; prevents a hostile encoder
from forcing the executor through quadratic walk at admission.
Coverage: TestChainsList_MustVerify_RejectsOverCap +
TestChainsList_MustVerify_AcceptsAtCap.
4. ValidatorsList MustVerify (5 floor invariants):
- Len() <= MaxValidatorsPerL1 (1024): cap matches practical
upper bound on initial-validator sets.
- Weight > 0: lifts the per-validator gate from tx_verify.go
onto the list-level MustVerify so it's grep-able and pure
orchestration on the tx side.
- BLSPubKey not all-zero: structural floor before R6V3 pairing.
- BLSPoP not all-zero: structural floor before R6V3 pairing.
- RegistrationExpiry > 0: parallel of ErrZeroExpiry on
RegisterL1ValidatorTx (unix timestamp can never be zero).
New typed errors: ErrTooManyValidators, ErrValidatorBLSPubKeyZero,
ErrValidatorBLSPoPZero, ErrValidatorRegistrationExpiryZero. Wired
into CreateSovereignL1Tx.Verify and ConvertNetworkToL1Tx.Verify
before the expensive BLS pairing walk so cheap structural floor
fires first. New audit gate (test + CI):
TestAuditGate_ValidatorsListEmbeddersCallMustVerify + workflow
job validatorslist-mustverify-gate. Coverage: 6 new MustVerify
tests (happy path + 5 floor-violation rejections + over-cap).
5. Bench refresh -benchtime=2s (M1 Max):
- Parse geomean (n=9): 7.50× vs v3.6 7.74× — within 4% noise.
- Build geomean (n=9): 1.03× vs v3.6 1.12× — within 9% noise.
- Allocs/op unchanged: Parse 1/24, Build 2 (down from 4-7).
- No regression > 5%. v3.7 changes fire entirely on the Verify()
path; Parse and Build are structurally untouched.
Full numbers in vms/platformvm/txs/bench_results/RESULTS.md.
All 4 audit gates pass locally:
- TestAuditGate_AddressListNoProductionConsumers
- TestAuditGate_ChainsListEmbeddersCallMustVerify
- TestAuditGate_ValidatorsListEmbeddersCallMustVerify (NEW)
- TestAuditGate_OwnerBearingTxCallsSyntacticVerify (NEW)
Full zap_native test suite: 0.715s, all green.
|
||
|
|
a71d6e989d |
wallet/chain/p: LUX_WALLET_UTXO_ASSET_ID_OVERRIDE for legacy LUX asset on mainnet
Adds an opt-in escape hatch that pins the fee-payment asset to a specific 32-byte asset ID, bypassing platform.getStakingAssetID. Use on networks where the live staking asset differs from the legacy LUX asset on existing P-chain UTXOs. lux-mainnet today is the documented case: staking == pmSJ7BfZQfwtUGbamLWSLFLFGnocfMfbriDTsYWxi4qnqFrrT but the historical deployer's UTXOs hold HrJCm4yvNmyPDA1PEqwks9iFFmoRJEsLJj36N1xtkffrqpL6p. Without the override the wallet's `utxoAssetID` matches the staking-asset result and `platform.getBalance` returns 0 for the legacy UTXOs — the wallet can't find them as fee-payment material. NOTE: this addresses the wallet-side identification only. The P-chain fee-execution rule still requires the staking asset on mainnet; a follow-up legacy-to-staking asset migration tx is needed before CreateNetworkTx + CreateChainTx will go through on mainnet. Tracked as the 2026-06-03 mainnet brand L2 re-fire blocker. Empty / unset is a no-op. Tested via bootstrap-chain mainnet dry-run (2026-06-03): override correctly switches the wallet to legacy-LUX UTXOs; CreateNetworkTx then fails with "insufficient unlocked funds" because the fee verifier checks pmSJ7BfZ... balance — which is the expected second-half blocker. |
||
|
|
db64f3aafd |
platformvm/txs/bench: LP-023 always-on — fix disable_legacy_test underflow
ZAPActivationUnix is now 0 (LP-023 cutover, 2026-06-02). The assertion `ShouldUseZAPForWrite(ZAPActivationUnix - 1)` underflows uint64 to math.MaxUint64; with ZAPActivationUnix=0 the LegacyEnabled path degenerates to `ts >= 0` which is true for every input, so the prior "pre-activation picks legacy" assertion can never hold. Rewrite runEnableLegacyChild to mirror zap_native.security_test.V15 closure: probe a timestamp spread including the historical forward-date (1782604800) and assert ShouldUseZAPForWrite=true unconditionally. Pins the always-on invariant on the bench surface too. No production code change. |
||
|
|
d0516c52c3 |
#189: LP-023 batch 5 v3.6 — R7V5 mempool admission gate for zap_native (Path A) + R7V7 Verify() for RegisterL1ValidatorTx + ConvertNetworkToL1Tx (HIGH) + R7V8 MustVerify rename + CI gate (MEDIUM)
Red round 7 closes the activation gap so Neo's ZAPActivationUnix=0 image can
land without zombie txs from not-yet-implemented zap_native executors.
R7V5 (HIGH, mempool admission gate — Path A)
- vms/platformvm/network/zap_native_admission.go: new file. Wraps
TxVerifier with NewZapNativeAdmissionGate; refuses
*txs.CreateSovereignL1Tx (legacy struct dispatch hits stub at
standard_tx_executor.go:636) AND any ZAP-magic wire buffer whose
kind is CreateSovereignL1 (23), RegisterL1Validator (7), or
ConvertNetworkToL1 (22).
- vms/platformvm/network/network.go: New() now wraps the supplied
TxVerifier with the gate before installing into gossipMempool, so
every inbound tx routes through the gate first.
- Typed error ErrZapNativeNotYetExecutable for errors.Is matching.
- REMOVAL CHECKLIST documented inline so a future Blue knows to
delete the gate entry when an executor body lands.
- Tests in zap_native_admission_test.go: rejects legacy
CreateSovereignL1Tx; passes through legacy BaseTx /
RegisterL1ValidatorTx / ConvertNetworkToL1Tx (the working ones);
rejects ZAP-wire CreateSovereignL1 / RegisterL1Validator /
ConvertNetworkToL1; non-ZAP bytes pass; nil-safety.
R7V7 (HIGH, Verify() for two missing tx types)
- RegisterL1ValidatorTx.Verify(): BLS PoP pairing + zero-Expiry gate
(ErrZeroExpiry). RemainingBalanceOwnerID treated as optional v3
placeholder.
- ConvertNetworkToL1Tx.Verify(): same per-validator walk as
CreateSovereignL1Tx (non-empty Validators, Weight>0, BLS PoP
pairing).
- Tests in r7v7_register_convert_verify_test.go: rejects bad BLS
PoP / malformed BLS pubkey / zero Expiry / zero Validators / zero
Weight; accepts valid; adversarial wire-buffer tampering for both
tx types (zero out Expiry / Weight in-place and re-Wrap).
R7V8 (MEDIUM, MustVerify rename + CI gate)
- chains_list.go: ChainsListView.Verify renamed to MustVerify so
the per-list gate is grep-able from CI. The previous Verify()
name collided with the tx-level Verify() convention.
- tx_verify.go: CreateSovereignL1Tx.Verify call site updated.
- r6_verify_test.go: TestChainsListView_Verify_StandaloneEntries
renamed + updated to use .MustVerify().
- audit_test.go: new TestAuditGate_ChainsListEmbeddersCallMustVerify
enumerates tx types with a Chains() accessor returning ChainsList
and confirms tx_verify.go has a corresponding Verify() body that
calls .MustVerify().
- .github/workflows/zap-audit.yml: new chainslist-verify-gate job
mirroring the local audit_test.go invariant.
Test results (GOWORK=off, race enabled):
./vms/platformvm/network/... PASS (8 new + all existing)
./vms/platformvm/txs/zap_native/ PASS (8 new + all existing)
./vms/platformvm/txs/executor/ PASS
./vms/platformvm/txs/ PASS
./vms/platformvm/block/... PASS
|
||
|
|
1104e6776e |
#189: LP-023 batch 5 v3.5 — RESERVED bytes zero-gate (R6-4) + AddressList CI audit gate (R6-6) + cross-blob aliasing documented contract (R6-2 design decision)
R6-4 (RESERVED zero-gate)
- ChainEntry RESERVED bytes [56..64) gated by ChainsListView.Verify.
- New OffsetChainEntry_Reserved=56 constant + ErrReservedNonZero typed err.
- Writer already zero-pads; gate prevents adversary smuggling state inside
what consensus considers empty. Pins the upgrade-safe invariant before
any v4 parser attaches meaning to those bytes (no silent wire-fork).
- Tests: TestChainsList_Verify_RejectsNonZeroReserved (per-byte sweep of
all 8 reserved bytes, each flipped individually -> all reject),
TestChainsList_Verify_RejectsNonZeroReserved_TopByte (top byte 0x01).
R6-6 (AddressList.At CI audit gate)
- .github/workflows/zap-audit.yml: grep-based gate on PR + push to
main/dev. Fails if any new production caller of AddressList.At()
appears outside the allowlist (_test.go, tx_verify.go, owner.go,
audit_test.go).
- vms/platformvm/txs/zap_native/audit_test.go: local mirror so
`go test ./vms/platformvm/txs/zap_native/` reproduces CI behavior.
Verified locally: clean -> PASS; injected violator -> trips correctly.
R6-2 (cross-blob aliasing design decision)
- Documented-allowance path: aliasing is ALLOWED. Wire layer must not
reject overlapping (rel, len) ranges; identity is (VMID, BlockchainID),
not Name() bytes.
- Full CONTRACT block on ChainsListView: (1) Name/FxIDs/GenesisData
return payload slices not identity, (2) chain identity is VMID +
BlockchainID, (3) consumers MUST NOT use returned bytes as dedup keys,
(4) returned slices are read-only.
- Forward path documented: if future feature requires non-overlap, add
ChainsListView.VerifyNonOverlappingRanges() and wire it from tx.Verify.
- Test: TestChainsList_AllowsOverlappingRanges_DocumentedContract -
patches chain[1].NameRel to alias chain[0]'s slice, proves Verify
accepts AND Name(0) == Name(1) byte-equal, AND VMID(0) != VMID(1).
go test -race ./vms/platformvm/txs/zap_native/...: ok 1.4s
Builds on top of v3 commit
|
||
|
|
d5c305d440 |
#189: LP-023 batch 5 v3 — wire Verify() gate for R6V4 (CreateSovereignL1Tx zero-validator/zero-chain CRITICAL) + R6V8 (TransferChainOwnershipTx HIGH) + R6V3 (BLS PoP verification HIGH) + R6V5 (FxIDs malformed length MEDIUM)
R6V4 (CRITICAL): CreateSovereignL1Tx.Verify now rejects zero-validator and zero-chain wire buffers (both halt consensus at activation). Per-validator Weight > 0 walk also fires here. RegistrationExpiry remains an executor clock concern (deferred to staking handler, documented). R6V8 (HIGH): TransferChainOwnershipTx had no Verify() despite carrying Owner fields. Wired in: reconstructs OwnerStub from the (threshold, locktime, address) tuple and calls SyntacticVerify. Tx count with SyntacticVerify wired into 8 tx types (was 7). R6V3 (HIGH): BLS PoP now verified at the SyntacticVerify boundary for every initial validator. Wire-layer opaque 48B BLSPubKey + 96B BLSPoP now pairing-checked via bls.VerifyProofOfPossession with new ErrBadBLSPoP. Closes the zero-downstream-consumer gap Red grep found. R6V5 (MEDIUM): ChainsListView.Verify walks entries and rejects any FxIDsLen that is not a multiple of FxIDSize. Replaces the silent-nil return path in BoundChainEntry.FxIDs with a typed error. Wired into CreateSovereignL1Tx.Verify via ChainsListView.Verify(). Tests (all under go test -race): TestCreateSovereignL1Tx_Verify_RejectsZeroValidators TestCreateSovereignL1Tx_Verify_RejectsZeroChains TestCreateSovereignL1Tx_Verify_RejectsZeroWeight TestCreateSovereignL1Tx_Verify_RejectsBadBLSPoP TestCreateSovereignL1Tx_Verify_RejectsBadBLSPoP_MismatchedPubKey TestCreateSovereignL1Tx_Verify_AdversarialWireBuffer_ValidatorWeight TestChainsList_Verify_RejectsBadFxIDsLen (adversarial wire buffer) TestChainsListView_Verify_StandaloneEntries TestTransferChainOwnershipTx_Verify_RejectsZeroThreshold TestTransferChainOwnershipTx_Verify_RejectsThresholdAboveOne TestTransferChainOwnershipTx_Verify_AdversarialWireBuffer TestBLSSurfaceReachable Updates TestVerify_AcceptsWellFormed/CreateSovereignL1 to supply a properly-constructed validator (real BLS PoP) and a chain entry so the new gates fire green on the legitimate path. Adds TestVerify_AcceptsWellFormed/TransferChainOwnership subtest for R6V8. New typed errors: ErrZeroValidators, ErrZeroChains, ErrValidatorWeightZero, ErrBadBLSPoP, ErrMalformedFxIDsLen. Bench unchanged (Verify is on executor path, parse benches structurally untouched). Full zap_native suite green under -race. LP-023 batch 5 v3 closes Red round 6 V3/V4/V5/V8. |
||
|
|
0c66ab55ae |
platformvm/txs/zap_native: LP-023 ZAP-native activation cutover — ZAPActivationUnix=0 (always-on)
ZAP wire is now mandatory from genesis. The forward-dated 2026-07-01 (1782604800) cutover is dead — replaced with always-on semantics. New deployments, fresh syncs, and all production binaries from v1.28.19 onward are ZAP-only by construction. The legacy linearcodec is reachable only via the explicit dev knob LUXD_ENABLE_LEGACY_CODEC=1, and only for the read path (decoding pre-2026-06-02 archival linearcodec bytes from disk). On the write path, LegacyEnabled has no semantic effect once activation = 0 — the "pre-activation" window is empty, so every block timestamp satisfies blockTimestamp >= 0 and writes are always ZAP. Tests: - TestZAPActivationUnixIsAlwaysOn pins the constant to 0 so any regression that re-introduces a forward-date guard fails this gate. - TestShouldUseZAPForWrite legacy-enabled subtest asserts ZAP for every timestamp (replaces the previous pre/at/post-activation table that depended on a non-zero gate). - TestRed_V15_PreActivationZAPTxRejection updated to reflect that V15's threat model (legacy cutover-window fork) is no longer applicable; the test now asserts the always-on invariant. Authorized by 2026-06-02 destructive recovery sweep — "just do it right; no half-measures, no legacy compatibility, clean slate." |
||
|
|
794822bb11 |
platformvm/txs/zap_native: LP-023 batch 5 Phase C+D+E — ChainsList + ValidatorsList + bench refresh
Phase C — Multi-chain CreateSovereignL1 ChainsList primitive (chains_list.go,
chains_list_test.go):
* 64-byte fixed-stride ChainEntry per chain: (NameRel/Len uint32, VMID 32B,
FxIDsRel/Len uint32, GenesisDataRel/Len uint32, Reserved 8B).
* Three sibling blob arrays (NameBlobs, FxIDsBlobs, GenesisDataBlobs)
carried as parent-tx fields; entry header stores (rel, len) cursors.
* Bind(nameBlobs, fxIDsBlobs, genesisDataBlobs) compile-time gate
mirroring BoundEvidenceList (NEW-V2): ChainsListView lacks safe
Name/FxIDs/GenesisData accessors so consumers cannot bypass Bind() by
accident. Only BoundChainEntry exposes the safe accessors.
* IsNull() zero-value guard on ChainEntry: out-of-range At(i) returns
a ChainEntry{} whose accessors short-circuit to zero rather than
nil-deref the underlying zap.Object's msg. Defense-in-depth atop the
defensive At(i) clamp.
* Per-element FxIDs blob is a length-prefixed byte array; entry count =
FxIDsLen / FxIDSize (32B per ids.ID). Non-multiple lengths clamp to nil.
* CreateSovereignL1Tx grows from a single-chain stub (TxKind 23, batch 4)
to a real multi-chain L1 builder: ChainsList + NameBlobs + FxIDsBlobs +
GenesisDataBlobs fields. Size 193 bytes (was 121).
Phase D — ValidatorsList encoding (validators_list.go,
validators_list_test.go):
* 180-byte fixed-stride ValidatorRecord per initial validator: NodeID 20B
+ Weight uint64 + BLSPubKey 48B + BLSPoP 96B + RegistrationExpiry uint64.
* Uses zap.Object.ListStride from v0.7.2 — per-element clamp against
poisoned wire length fields (R4V9).
* No sibling arrays needed — every field is fixed-size.
* BLSPubKey() and BLSPoP() return COPIED slices (not aliased to parent
buffer) so consumer mutation cannot corrupt subsequent reads.
* IsNull() zero-value guard mirroring ChainEntry.
* ConvertNetworkToL1Tx now carries the real ValidatorsList — the
previous (0, 0) stub is gone. Size still 165 bytes (the validators
list pointer was already provisioned).
* CreateSovereignL1Tx integrates ValidatorsList alongside ChainsList for
the full atomic sovereign-L1 commit shape.
Phase E — Bench refresh:
* M1 Max Parse geomean across 10 measurable tx types post-batch-5:
7.44× at -benchtime=2s -count=3 (medians).
* Restricted to the original 9 tx types from the v0.7.2 baseline:
7.74×, within noise of the 7.71× pre-batch-5 number.
* R4V7 SyntacticVerify lives on the executor Verify() path (not the
wire parser), so the parse benchmarks are structurally unchanged.
* ChainsList + ValidatorsList add new primitives but do not appear in
the legacy comparison fixtures.
Test coverage:
* chains_list_test.go: RoundTrip (3 chains incl. empty FxIDs+empty
GenesisData), EmptyList, OutOfRange, UnboundReturnsRawCursors,
BindMismatchedBlobsClampsSafely, FxIDsNonMultipleClampsToNil.
* validators_list_test.go: RoundTrip (2 validators), EmptyList,
OutOfRange, PoisonedLengthClampedByStride,
BLSFieldsAreReadOnly (mutation isolation),
CreateSovereignL1Integration (ChainsList + ValidatorsList together).
* batch4_tx_test.go: updated CreateSovereignL1TxRoundTrip for the new
multi-chain + validators shape; tests both round-trip and Wrap()
re-parse correctness.
All zap_native tests pass under go test -race.
Coordinates with cryptographer #114 v3 work and CTO bootstrap-chain work;
neither genesis/ nor bootstrap touched.
LP-023 batch 5 Phase C+D+E.
|
||
|
|
7b51f809bd |
platformvm/txs/zap_native: close R4V3 AddressList honest-overcount gap (LP-023 batch 5 Phase B)
R4V3 finding: a malicious wire-encoded AddressList may report Len() >
actual entry count (the ListStride per-element clamp accepts the honest
overcount as long as length*stride fits the remaining buffer). At(i)
for i >= actual_count returns whatever bytes occupy the post-list
region in the buffer — often zero-padding, sometimes adjacent buffer
content.
Audit result: AddressList has ZERO production consumers in the
executor today (the executor still uses secp256k1fx.OutputOwners +
message.PChainOwner via the legacy codec). The new primitive is
shipping ahead of the executor migration, so the discipline is
forward-looking. Audited grep `\.At(|AddressList` across
~/work/lux/node — every match is internal to the zap_native package
or test code.
Defense path (canonical):
- Owner.SyntacticVerify() now walks the list and rejects any zero
ShortID with ErrOwnerAddrZero. This closes the zero-phantom
bypass at the gate.
- Documented consumer-safety contract on AddressList type docstring
AND on .Len()/.At() — three paths: non-zero check at call site,
sibling-count correlation, or canonical SyntacticVerify boundary
(path 3 is the one-and-only-one way).
Test scope:
- r4v3_addresslist_test.go — overcount construction with the wire
layer (claim 5, real 4, 1-phantom); confirms SyntacticVerify
rejects the zero-phantom case and accepts the buffer-garbage
case (signature validation downstream closes the remaining
surface in the garbage path — no zero co-signer can sneak
through the quorum gate).
- Honest-list happy path also pinned.
Tradeoffs:
- SyntacticVerify is now O(Len()) per Owner instead of O(1). For
typical owners (1-5 addresses) this is negligible; the gate is
the authorization boundary and bears the cost.
|
||
|
|
20ced67102 |
platformvm/txs/zap_native: R4V7 Owner SyntacticVerify + per-tx Verify (LP-023 batch 5 Phase A)
Wire layer (zap_native parser) is permissive by design — it confirms
TxKind + buffer geometry only. Executor-side semantic gates live HERE
on the consumer-side boundary.
Owner.SyntacticVerify enforces:
- ErrOwnerAddrsEmpty: Addresses.Len() == 0 (signer set undefined)
- ErrOwnerThresholdZero: threshold == 0 (auth bypass)
- ErrOwnerThresholdExceedsAddrs: threshold > Addresses.Len()
(unsatisfiable quorum)
OwnerStub.SyntacticVerify enforces the single-address fast path:
- Threshold must be exactly 1 (zero is bypass, >1 is unsatisfiable
because the stub carries one address by construction)
Per-tx Verify() entry points wire the gate into all 7 tx types that
embed Owner-shaped fields:
- AddValidatorTx.RewardsOwner
- AddDelegatorTx.DelegationRewardsOwner
- AddPermissionlessValidatorTx.{Validation,Delegation}RewardsOwner
- AddPermissionlessDelegatorTx.DelegationRewardsOwner
- CreateChainTx.Owner
- CreateNetworkTx.Owner
- CreateSovereignL1Tx.Owner
Test scope (TDD red→green):
- owner_syntactic_test.go — 7 cases on Owner + OwnerStub directly
- tx_verify_test.go — 7 well-formed + 7 malicious-threshold + 1
multi-owner accept + 1 adversarial-wire-buffer test that overwrites
the threshold byte in the buffer and re-Wrap, confirming the gate
fires on byte-stream attacks (not only constructor input).
Contract (LP-023 Red round 4 R4V7): every tx executor's Verify() entry
point MUST call tx.Verify() before treating embedded Owner fields as
authoritative. Skipping Verify() opens the auth-bypass attack vector.
|
||
|
|
48cdf9cdff |
platformvm/txs/zap_native: close LP-023 Red round 4 — R4V9 + R4V12 + R4V14
Three surgical fixes against Red round 4 findings (task #189). R4V9 (MEDIUM) — migrate 6 list accessors from Object.List() to Object.ListStride() for the per-stride poisoned-length clamp added in zap v0.7.2. Before the migration, a wire length field that satisfied the permissive `length <= len(buf)` baseline (bare List accepts) but failed the tighter `length * stride > bufRem` bound would slip past the accessor and return a List view with Len() == poisoned_length, opening per-element OOB reads on stride-> 1 entries (silent zeros via Object.Uint{8,32,64}, but consumer iterates ghost entries). Migrated: - CredentialListView → SizeCredential (stride 16) - SignatureArrayView → SigBlobSize (stride 65) - InputListView → SizeTransferableInput (stride 88) - SigIndicesArrayView → SizeSigIndex (stride 4, new const) - OutputListView → SizeTransferableOutput (stride 96) - NewEvidenceListView → SizeEvidenceEntry (stride 48) Also added defensive `i >= Len()` bounds checks to the four At() methods that construct sub-Objects from list.Object() — without the guard, At(0) on a clamped Len()=0 view would build a Credential / TransferableInput / TransferableOutput / EvidenceEntry wrapping a zero-value zap.Object (msg=nil) and panic on downstream field reads. (SignatureArray.At and SigIndicesArray.At already had the guard.) New regression suite r4v9_liststride_test.go covers all 6 accessors plus a false-positive guard verifying honest single-entry lists still round-trip. Pattern follows existing TestNewV1_ListStrideTighterClamp in zap: build honest list, overwrite wire length with poisoned value chosen to satisfy bare clamp but fail stride clamp, parse, confirm Len()==0 and At(0) is panic-safe. R4V12 (MEDIUM) — rename `Subnet` to `Chain` in bench fixtures all_types_bench_test.go: legacySlashValidatorTx + legacyRemoveChainValidatorTx struct field + their 4 construction sites (lines 55, 68, 444, 476, 552, 584 in original). Bench-only legacy types used for parity benchmarks; rename is audit-hygiene per the rebrand sweep (#187). Grep gate `grep -rn "Subnet" ... | grep -v "//"` is now empty. R4V14 (INFO) — fix docstring drift in add_chain_validator_tx.go: the "Fixed-section layout (size 165 bytes)" comment said 165, but the SizeAddChainValidatorTx constant correctly says 161. Updated comment to match constant; verified by arithmetic (last field Chain @ 129 + 32 bytes = 161, not 165). Verification: - go test -race -count=1 ./vms/platformvm/txs/zap_native/... → ok - 7 new R4V9 tests pass (6 poisoned-length regressions + 1 honest round-trip). - grep gate for R4V12 is empty. - Parse benchmarks (M1 Max, 200ms): geomean 7.70x (named-tx-only), within noise of pre-fix 7.71x baseline. zap_native-side only — luxfi/zap requires no bump. |
||
|
|
59db8da979 |
platformvm/txs/zap_native: LP-023 Phase 1 batch 4 — 8 tx types + Owner + BoundEvidenceList + FuzzWrapAllTxKinds
Red round 3 follow-ups and remaining tx surface: BOUNDEVIDENCELIST (NEW-V2 compile-time enforcement): - EvidenceListView (unbound wire view) → .Bind(mb,sb) → BoundEvidenceList - BoundEvidenceList.At(i) returns BoundEvidenceEntry which exposes safe MessageA/B + SignatureA/B accessors. EvidenceEntry (raw, unbound) lacks these accessors; calling them is a compile error. Consumers cannot bypass Bind() by accident. - Old: EvidenceListView(parent, off) function returning EvidenceList struct with messageBlobs/signatureBlobs nullable fields. - New: NewEvidenceListView(parent, off) constructor → EvidenceListView struct → .Bind() → BoundEvidenceList struct with bound fields. - Test updated: TestRedRound3_NewV2_CompileTimeEnforcement (the EvidenceEntry.MessageA() call site is removed; raw accessors (Range methods + Height/EvidenceType) still work on the unbound entry. 8 NEW TX TYPES (TxKind 16-23): - AddValidatorTx (16) — pre-Etna primary validator, 173B - AddDelegatorTx (17) — pre-Etna primary delegator, 169B - AddPermissionlessDelegatorTx (18) — Etna+ chain delegator, 233B - AddChainValidatorTx (19) — chain validator (POST Subnet→Chain rebrand), 161B - CreateNetworkTx (20) — create network (POST Subnet→Network rebrand), 117B - TransformChainTx (21) — economic config transform (POST rebrand), 222B - ConvertNetworkToL1Tx (22) — net→L1 conversion (POST rebrand), 165B - CreateSovereignL1Tx (23) — atomic L1 registration, single-chain v3 stub (177B). Multi-chain L1s pending Chains list primitive. Rebrand sweep: zero `Subnet` references in zap_native/ source. The post sweep #187 names are baked into TxKind enum + tx struct names. Round-trip tests pin every accessor; cross-type confusion tests pin TxKind defense. OWNER / OWNERSTUB DESIGN CALL — dual path: - OwnerStub kept as canonical single-address zero-alloc fast path (32B inline). Common case stays fast. - Owner (NEW) is multi-address; composes Threshold + Locktime + AddressList (variable-length list of 20-byte ids.ShortID). Header 20B inline + variable address storage. - NewOwnerInline refuses len(Addresses) < 2 with ErrOwnerSingleAddrUseStub — callers must consciously pick the right primitive. - Hammock rationale: 99% of P-chain validator txs use 1 owner; List-backed Owner would add ~24B alloc to every tx for a multi-sig case that's rare. Type system makes the choice load-bearing. FUZZWRAPALLTXKINDS (Red round 3 follow-up #3): - For any byte slice, AT MOST ONE WrapXxxTx returns nil. All others MUST return ErrWrongTxKind / ErrWrongSchemaVersion / typed zap.Parse error. - 23 wrapper tests in parallel; 1.4M+ execs clean on M1 Max with 15s fuzztime. No panics, no cross-type confusion, no untyped errors. ZAP V0.7.2 DEPS: - go get github.com/luxfi/zap@v0.7.2 — pulls Object.ListStride, List.Len SAFETY doc, FuzzParse. RESULTS.md REFRESH (v0.7.2 M1 Max): - Geomean Parse × = 7.71× (up from 7.33× v0.7.1; v0.7.2 ListStride accept-path cost is negligible — one mul + compare per List() call) - All 9 ZAP-native types in v0.7.2 column; v0.7.1 M4 Max numbers retained side-by-side (no M4 access in this session) - Build regression on M4 Max still tracked (v0.7.x follow-up to attack zap.Builder per-call overhead) Tests: - 11 new round-trip + cross-type tests under TestBatch4_*, TestOwner*, TestAddValidator/Delegator/PermissionlessDelegator/ChainValidatorTx*, TestCreate{Network,SovereignL1}Tx*, TestTransformChainTx*, TestConvertNetworkToL1Tx* - All existing tests (incl. RED-HIGH-1/2/3, MEDIUM-1, V18) still green |
||
|
|
3df13caa15 |
platformvm/txs/zap_native: close LP-023 v3.1 Red round 2 wire gaps (RED-HIGH-1/2/3, RED-MEDIUM-1)
Bumps luxfi/zap to v0.7.1 which closes the underlying wire-layer gaps (uncapped Object.List length, backward-pointer header aliasing, size=0 Parse), and reworks the zap_native package to: - Use zap.Version2 as the schema-version gate. Every Wrap*Tx now routes through parseAndCheckKind(b, want), which rejects any Version1 buffer with ErrWrongSchemaVersion before TxKind interpretation. Closes the v2-vs-v3 cross-schema confusion (RED-MEDIUM-1) where a v2-shaped BaseTx with NetworkID=11 had byte 0 == TxKindBaseFull == 0x0B. - Add EvidenceList.Bind(messageBlobs, signatureBlobs) → EvidenceList, and safe accessors EvidenceEntry.MessageA/B + SignatureA/B that clamp wire (Rel,Len) cursors against parent-blob length. Returns empty slice on poisoned cursors (RED-HIGH-3) instead of panicking on mb[rel:rel+len]. The raw *Range() accessors are now documented UNSAFE and kept only for internal/test use. - Add SigIndicesArray.Slice(start, count uint32) → []uint32 and SignatureArray.Slice(start, count uint32) → [][SigBlobSize]byte. Both methods were referenced in comments but missing; consumers indexing .At() in a loop with attacker-controlled start/count would have iterated 4G times on poisoned wire (RED-HIGH-3 follow-on). - Update batch3_test.go::TestEvidenceListRoundTrip to use the safe bound accessors (the prior `mb[mARel:mARel+mALen]` pattern is exactly the consumer-side panic surface Red demonstrated). Regression tests added to security_test.go: TestRedRound2_HIGH3_EvidenceListSafeAccessorsClamp TestRedRound2_HIGH3_SigIndicesArraySliceClamp TestRedRound2_HIGH3_SignatureArraySliceClamp TestRedRound2_MEDIUM1_V2BufferRejectedAtSchemaGate TestRedRound2_MEDIUM1_HonestV2BuffersStillWork Geomean Parse speedup vs legacy codec: 7.03× (was 7.09×; +0.5% noise). |
||
|
|
452468bfe0 |
platformvm/txs/bench_results: v3 multi-host bench results (M1 Max + M4 Max)
Schema v3 (TxKind discriminator) reproduces predicted v2→v3 lift within
noise: Parse geomean 7.11× on M1 Max (n=9 native types), 8.02× on M4 Max
— matches Red model (v2 8.39× → v3 7.09× projected). Per-type allocs 3.57×
fewer, bytes 6.89× smaller, host-stable.
AdvanceTime end-to-end via txs.Codec wrapper: 34.14× M1, 42.76× M4 — ratio
grows with chip speed (reflection tax is fixed-per-op).
Honest residual: Build is a regression on M4 Max for 6/9 types (geomean
0.86×). zap.Builder per-call overhead exposed on fast cores. M1 Max still
positive (1.12×). Tracked for luxfi/zap v0.7.0 (Blue v3.1 iteration);
Parse-side ship decision is independent.
Both hosts darwin/arm64 (M1 Max MacBookPro18,2 + M4 Max Mac16,5 / dbc
runner). Task spec called dbc "amd64 Linux" — corrected: it is darwin/arm64
Apple M4 Max. linux/amd64 numbers TBD until x86 box lands in ARC fleet.
Reproduce: GOWORK=off go test -bench='^Benchmark(Parse|Build)' -benchmem
-benchtime=500ms -count=3 -run='^$'
./vms/platformvm/txs/{bench,zap_native}/
|
||
|
|
b6ce3a2159 |
platformvm/txs/zap_native: 5 batch-3 tx types composing the new primitives (LP-023 Phase 1)
TxKindBaseFull = 11 → BaseTxFull
TxKindAddPermissionlessValidator = 12 → AddPermissionlessValidatorTx
TxKindImport = 13 → ImportTx
TxKindExport = 14 → ExportTx
TxKindCreateChain = 15 → CreateChainTx
Sizes (fixed section, schema v3):
BaseTxFull = 85
ImportTx = 125
ExportTx = 125
AddPermissionlessValidatorTx = 381
CreateChainTx = 221
Design highlights:
- BaseTxFull is the real P-chain spending envelope (Outs + Ins +
Credentials + 2 shared arrays + Memo). The batch-2 BaseTx
(TxKindBase) remains as the minimal metadata envelope for places
that need only {NetworkID, BlockchainID, Memo} without spending
state. Both kinds are first-class; they are NOT alternatives.
- Import / Export use a SINGLE combined Ins (or Outs) list with a
header marker (ImportedInsStart/Count or ExportedOutsStart/Count)
identifying the cross-chain slice. This collapses what would have
been two parallel sig-index arrays into one shared array — fewer
pointer pairs, no rebase bookkeeping. The slice-based indexing is
byte-identical for the imported/exported half because they
reference the same shared array.
- AddPermissionlessValidatorTx carries two single-address Owner stubs
(validation rewards, delegation rewards) inline in the fixed
section. The same OwnerStub type underlies CreateChainTx.Owner.
Multi-address Owner ships in batch 4 along with the AddressList
primitive; current callers needing multi-addr flow through the
legacy codec gate.
- CreateChainTx embeds a 32-byte WarpMessageHash (sha256 of the
originating Warp commit; zero when the chain is being created
directly without a cross-network commit). The Warp message BODY
is NOT embedded — it lives in the signed Warp envelope outside
this tx. Hash-only embedding keeps the unsigned-tx bytes stable
and the Warp envelope separately verifiable.
All five tx types follow the v3 invariant: TxKind@0, Wrap* rejects
mismatched discriminator with ErrWrongTxKind. Cross-confusion test
expansion verifies the new tx types in pairwise rejection (7
additional scenarios cover the batch 1+2+3 surface).
Deferred to batch 4 (no new primitives needed): the remaining ~18
classical platformvm tx types (CreateNetwork, AddDelegator,
AddPermissionlessDelegator, etc.) reuse the existing batch-3
primitives; their landings are mechanical.
go test -race ./vms/platformvm/txs/zap_native/...
ok github.com/luxfi/node/vms/platformvm/txs/zap_native 1.488s
|
||
|
|
197dac4245 |
platformvm/txs/zap_native: batch 3 primitives — variable-length nested schemas (LP-023 Phase 1)
Five primitives. Each is fixed-stride at the entry level; variable
per-entry bytes/sub-list payloads live in shared sibling fields on
the parent tx, indexed by (start, count). This keeps List.At(i)
zero-allocation while supporting unbounded per-entry payload sizes.
OutputList stride 96 → TransferableOutput
InputList stride 88 → TransferableInput + shared SigIndicesArray
CredentialList stride 16 → Credential + shared SignatureArray
WarpMessage size 40 → embedded {SourceNetwork, Payload}
EvidenceList stride 48 → EvidenceEntry + shared MessageBlobs/SignatureBlobs
Design choices:
- Stride is the entry-count semantics for SetList; the byte count from
ListBuilder.Finish() is discarded. List.Object(i, stride) does the
branch-free arithmetic.
- Variable per-entry data goes into a SIBLING field on the parent tx
(not into the entry's stride): InputList → SigIndicesArray;
CredentialList → SignatureArray; EvidenceList → MessageBlobs +
SignatureBlobs. Each entry references its slice via (start, count).
This pattern lets entries stay fixed-stride and accessors stay
zero-allocation. Multi-input-shared signature arrays also enable
signature deduplication when adjacent inputs share signers.
- Forward-only relOffsets per the F1 contract: SetBytes-backed payload
fields (WarpMessage.Payload, EvidenceList parent's MessageBlobs/
SignatureBlobs) flow through the F1-fixed zap.Object.Bytes which
rejects negative bit-patterns.
- Read-only contracts on every []byte / by-value accessor are
documented with the canonical defensive-copy idiom
(append([]byte(nil), m...)) per AT1.
- Multi-address Owner is NOT yet supported at the wire layer; v3
OutputList carries the single-address stub identical to
TransferChainOwnershipTx. Multi-address callers still flow through
the legacy codec gate behind LUXD_ENABLE_LEGACY_CODEC. The
AddressList primitive ships in batch 4 (defer).
- Multi-signature credentials handled via the SignatureArray slice
semantics — one Credential, N sigs. Post-quantum credentials
(ML-DSA) are NOT YET on the v3 path; they keep flowing through
legacy until their dedicated PQ Credential schema lands.
Round-trip tests (batch3_test.go) cover all five primitives + the
empty-list/null-pointer fallback path. go test passes; race-free.
Updated bench numbers for v3 batch-2 (the +1-byte TxKind tax):
Parse geomean speedup vs legacy: 7.09× (was 8.39× pre-v3)
Build geomean speedup vs legacy: 1.35× (build path was always
closer to parity; structure-allocator dominates at small tx sizes)
|
||
|
|
a06562ba95 |
platformvm/txs/zap_native: schema v3 with TxKind discriminator (LP-023 Phase 1)
Phase A response to Red's batch-2 review:
F2 (MEDIUM, cross-type confusion) — schema-bump v2→v3. Every fixed
section now carries a TxKind uint8 at offset 0; every other field
shifts by +1 byte. Wrap*Tx reads TxKind first and returns
ErrWrongTxKind on mismatch. Constructors write the kind
unconditionally. Closes the gap where an AdvanceTimeTx buffer wrapped
as a BaseTx returned garbage-but-deterministic field reads.
TxKind enum (dense, 0 reserved):
1=AdvanceTime 2=RewardValidator 3=SetL1ValidatorWeight
4=IncreaseL1ValidatorBalance 5=DisableL1Validator 6=Base
7=RegisterL1Validator 8=SlashValidator
9=TransferChainOwnership 10=RemoveChainValidator
v3 sizes (each +1 vs v2 from the TxKind byte):
AdvanceTimeTx = 9
RewardValidatorTx = 33
SetL1ValidatorWeightTx = 49
IncreaseL1ValidatorBalanceTx = 41
DisableL1ValidatorTx = 33
BaseTx = 45
RegisterL1ValidatorTx = 217
SlashValidatorTx = 57
TransferChainOwnershipTx = 69 (no natural-alignment padding; reads are alignment-tolerant)
RemoveChainValidatorTx = 53
F1 (MEDIUM, memo malleability) — closed at the luxfi/zap wire layer
in v0.6.1 (negative relOffset rejected in Object.Bytes). Bumped node's
go.mod replace: zap v0.2.0 → v0.6.1. TestRed_V2 now confirms the
defense via the nil-Memo branch.
F4 (INFO, doc bugs) — RegisterL1ValidatorTx comment now correctly
declares size 217; TransferChainOwnershipTx now correctly declares
size 69. Both sizes flow from offset arithmetic — no magic numbers.
AT1 (accepted tradeoff, memo aliasing) — BaseTx.Memo() docstring
escalated: READ-ONLY contract + the canonical defensive-copy idiom
(append([]byte(nil), m...)). Same pattern documented for every
variable-length accessor going forward.
Brand cleanup: SlashValidatorTx.Subnet() → Network() and
RemoveChainValidatorTx.Subnet() → Network(). Zero `Subnet*` symbols
in batch-2 code path. Tests updated.
V14 security test now exhaustively covers cross-confusion: 10
pairings of {valid-tx-buf, wrong-Wrap} + reserved TxKind=0 — all
reject with ErrWrongTxKind. The other 19 Red vectors continue to
pass under v3.
go test -race ./vms/platformvm/txs/zap_native/...
ok github.com/luxfi/node/vms/platformvm/txs/zap_native 1.446s
Coordinated with luxfi/zap@v0.6.1 (F1 fix).
|
||
|
|
96c5320f7a |
wallet/network/primary: fail-soft FetchState when X-Chain disabled
Post-coreth networks (test+dev primary that bake only P + EVM genesis chains) run in P-only mode where the X alias is not registered. The wallet's FetchState now degrades the X-Chain context to a sentinel ids.Empty BlockchainID instead of erroring, and skips the X-chain entry from the chain-pair UTXO scan when X is unavailable. Required to drive IssueCreateChainTx via the canonical primary wallet against the fresh test+dev primaries (closes universe #168). |
||
|
|
7aa01da346 |
platformvm/txs/zap_native: 5 more tx types (LP-023 Phase 1 batch 2)
Native ZAP encoding — no marshal step, struct IS wire format — for the
next batch of platformvm tx types. Same pattern as Phase 1 batch 1.
New types (v1 schemas; variable-length nested fields deferred to batch 3):
base_tx.go — NetworkID + BlockchainID + Memo
(Outs/Ins → batch 3)
register_l1_validator_tx.go — ValidationID + BLS pubkey 48B + PoP 96B
+ Expiry + RemainingBalanceOwnerID stub
(Warp Message + full OutputOwners → batch 3)
slash_validator_tx.go — NodeID + Subnet + SlashPercentage
(Evidence variable-length → batch 3)
transfer_chain_ownership_tx.go — Chain + Owner{threshold,locktime,addr} v1 stub
(full OutputOwners list → batch 3)
remove_chain_validator_tx.go — NodeID + Subnet
(ChainAuth lives in signed wrapper)
Tests + benches extend the existing all_tx_types_test.go +
all_types_bench_test.go infra (no duplication). 18 new accessor zero-alloc
sites verified, including 48B BLSPublicKey and 96B PoP by-value returns.
Aggregate bench results (Apple M1 Max, Go 1.24, -benchtime=2s):
Parse Legacy vs ZAP:
BaseTx 470.0 → 60.42 ns ( 7.8x) 152→24 B 3→1 alloc
RegisterL1ValidatorTx 1008 → 75.03 ns (13.4x) 384→24 B 6→1 alloc
SlashValidatorTx 690.6 → 82.74 ns ( 8.3x) 176→24 B 4→1 alloc
TransferChainOwnershipTx 2459 → 161.5 ns (15.2x) 192→24 B 4→1 alloc
RemoveChainValidatorTx 879.6 → 117.2 ns ( 7.5x) 176→24 B 4→1 alloc
Parse cross-type mean (10 types now native): 8.5x speedup.
Build (one-time per proposer, dominated by Parse on consensus path):
BaseTx 1.5x slower (memo SetBytes deferred-copy; acceptable trade)
Register 1.03x (allocs 7→2)
Slash 1.4x faster (allocs 5→2)
Transfer 0.96x (allocs 5→2)
Remove 1.3x faster (allocs 5→2)
All accessor reads zero-alloc. All builds 1 alloc on read path.
Verification:
cd ~/work/lux/node
GOWORK=off go build ./vms/platformvm/txs/zap_native/...
GOWORK=off go test ./vms/platformvm/txs/zap_native/...
GOWORK=off go test -bench=. -benchmem -benchtime=2s \\
./vms/platformvm/txs/zap_native/...
Activation 1782604800 (2026-07-01 00:00 UTC) unchanged.
LUXD_ENABLE_LEGACY_CODEC=1 opts INTO legacy unchanged.
Refs LP-023. Next: batch 3 — variable-length nested-object schemas (Outs/Ins
lists, Warp Message payloads, full OutputOwners, Evidence) + remaining tx
types via codegen.
|
||
|
|
6f1c1811ca |
platformvm/txs/bench: Scientist comprehensive ZAP vs linearcodec harness + results (LP-023)
Real-workload benchmark suite produced by the scientist agent. Production-
realistic measurements on Apple M1 Max (Go 1.24).
Harness (8 files + results + reproduce docs):
bench/fixtures.go — realistic per-type tx fixtures
bench/parse_bench_test.go — per-type Parse: Legacy vs ZAP
bench/build_bench_test.go — per-type Build
bench/field_bench_test.go — field access (single + 1M batch)
bench/workload_bench_test.go — 1000-tx mempool + 200-block parse + dispatcher
bench/alloc_bench_test.go — 5s sustained-parse GC pressure
bench/disable_legacy_test.go — LUXD_ENABLE_LEGACY_CODEC gate verification
bench/README.md — reproduce + capture procedures
bench/testdata/README.md — captures notes
bench_results/RESULTS.md — full numbers + honest caveats + CPU profile
Headline numbers (vs linearcodec):
Parse AdvanceTimeTx : 37x faster (1940 ns -> 52.5 ns), 20x less mem
Build AdvanceTimeTx : 5.2x faster
Sustained 5s parse loop : 18.5x throughput (777k -> 14.4M parses/sec)
Cross-type mean : 5.6x parse, 1.6x build
Allocations (parse, build) : always 3->1 and 4->2
CPU profile:
Legacy: ~50% in reflectcodec.{marshal,unmarshal} reflection walk,
~30% in runtime.{madvise,kevent} from per-field alloc GC pressure
ZAP: elimination of both surfaces; offset arithmetic + 1 alloc per parse
Honest caveats (verbatim from scientist report):
- Only 5/33 tx types have native paths today; mempool mix workload
compresses to ~1.1x lift until BaseTx + AddPermissionless* native ship
- Field access: single uint64 read is 2.1x slower (offset deref vs struct
field), break-even at ~3,560 reads per parse; mainnet validators do
~10x per tx so ZAP still wins by orders of magnitude in the real regime
- darwin/arm64 only — re-run on linux/amd64 before quoting production-
binding numbers
- LUXD_ENABLE_LEGACY_CODEC gate verified at zap_native surface (subprocess
test passes); NOT yet wired into platformvm/txs.Parse — would break
byte-preserving v0 read for validators bootstrapping pre-activation
history. Gate enforcement lives at the future wire dispatcher.
Recommendation (CTO direction): continue the migration. Architecture works
as designed. Next priority: BaseTx + AddPermissionless* native paths — the
two highest-weight tx types in the modal mainnet mempool mix.
txs/tx.go is unmodified — byte-preserving v0->TxID migration invariant
preserved per existing codec.go CodecVersionV0/V1 framework.
Reproduce: cd ~/work/lux/node && GOWORK=off go test -bench=. -benchmem ./vms/platformvm/txs/bench/...
|
||
|
|
05994f1d56 |
platformvm/txs/zap_native: 4 more tx types + aggregate benchmarks (LP-023 Phase 1 batch 1)
Adds native ZAP encoding for the L1-management tx types — same pattern as AdvanceTimeTx canary. No marshal step, zero-copy accessors, zero-alloc field reads. Types added: RewardValidatorTx — TxID (32 bytes) SetL1ValidatorWeightTx — ValidationID + Nonce + Weight (48 bytes) IncreaseL1ValidatorBalanceTx — ValidationID + Balance (40 bytes) DisableL1ValidatorTx — ValidationID (32 bytes) Tests: - Round-trip parity per type (build → wrap → equal) - All accessors zero-alloc (AllocsPerRun = 0) - Wire-format discrimination via IsZAPBytes magic Benchmarks (Apple M1 Max, Go 1.24, real linearcodec reflection path vs ZAP): Tx Type | Parse Legacy | Parse ZAP | Speedup ------------------------------|--------------|-----------|-------- AdvanceTimeTx | 216.9 ns/op | 38.92 ns | 5.6x RewardValidatorTx | 218.5 ns/op | 35.80 ns | 6.1x SetL1ValidatorWeightTx | 234.3 ns/op | 25.76 ns | 9.1x IncreaseL1ValidatorBalanceTx | 245.4 ns/op | 25.55 ns | 9.6x DisableL1ValidatorTx | 218.5 ns/op | 54.44 ns | 4.0x Allocations: legacy 3 allocs / 120-136 B; ZAP 1 alloc / 24 B. 4x reduction in allocs/op, ~5x reduction in B/op across the board. Build side: roughly even (both paths allocate a buffer); the win is on parse, which dominates real workloads (every validator parses every tx in every block). Phase 1 batch 1 of 4. 5 types down, ~28 to go. Same pattern for each: schema constants + Wrap + Builder + Test + Bench. Pattern is production-ready; remaining types (BaseTx, ImportTx, ExportTx, CreateChainTx, validator variants) follow the same shape with progressively more nested sub-objects for Inputs/Outputs/Credentials. Reproduce: cd ~/work/lux/node GOWORK=off go test -bench=. -benchmem ./vms/platformvm/txs/zap_native/ |
||
|
|
b4d325be6c |
platformvm/txs/zap_native: invert env var — native ZAP default, legacy is opt-in
LUXD_DISABLE_LEGACY_CODEC → LUXD_ENABLE_LEGACY_CODEC (per user 2026-06-02 'should be LUXD_ENABLE_LEGACY_CODE=1 to turn it on'). Default: native ZAP for every read + write. codec.Codec.Marshal/Unmarshal gone from hot path. Fresh deployments + post-activation production get a smaller, faster binary with no legacy code reachable. Operators with pre-activation history that needs reading set LUXD_ENABLE_LEGACY_CODEC=1 to opt in to backward-compat. Without it, legacy bytes return ErrLegacyCodecDisabled. Tests updated for the inverted default; pass clean. |
||
|
|
80e1881285 |
platformvm/txs/zap_native: canary AdvanceTimeTx native-ZAP encoding (LP-023)
No marshal. No codec.Codec interface. The struct wraps the ZAP buffer
literally. tx.Bytes() returns the buffer. WrapAdvanceTimeTx(b) wraps b in
a typed accessor. Both are zero-copy.
Architecture per LP-023:
- ZAPActivationUnix = 1782604800 (2026-07-01 00:00 UTC)
- LUXD_DISABLE_LEGACY_CODEC=1 → legacy linearcodec returns ErrLegacyCodecDisabled
- IsZAPBytes(b) discriminates ZAP magic "ZAP\\x00" from linearcodec V0/V1
- ShouldUseZAPForWrite(blockTimestamp) gates new writes
Real benchmark numbers on Apple M1 Max (Go 1.24):
Parse linearcodec 129.4 ns/op 72 B 2 allocs
zap 36.22 ns/op 24 B 1 alloc → 3.57x faster
Build linearcodec 164.3 ns/op 96 B 4 allocs
zap 70.55 ns/op 72 B 2 allocs → 2.33x faster
Field linearcodec 0.34 ns/op (struct deref baseline)
zap 0.75 ns/op (offset Uint64) — sub-ns, effectively free
Tests:
TestAdvanceTimeTxRoundTrip — build → parse → equal
TestAdvanceTimeTxZeroAlloc — Time() accessor 0 allocs/run
TestAdvanceTimeTxBytesReusable — &Bytes()[0] stable
TestIsZAPBytes
TestShouldUseZAPForWrite
This is Phase 0 of the LP-023 migration. Phase 1: schemas + accessors for
the remaining 32 platformvm tx types using the same pattern. Phase 2:
replace codec.Codec.{Marshal,Unmarshal} call-sites in platformvm/{txs,
mempool,block,state,executor}. Phase 3: validator coordination + activation.
Phase 4: post-activation legacy code removal.
Reproduce: GOWORK=off go test -bench=. -benchmem ./vms/platformvm/txs/zap_native/
|
||
|
|
cac801667f |
Dockerfile: bump EVM_VERSION v0.18.16 → v0.18.18
Pulls in plugin/evm StateScheme fix that unblocks fresh L2 EVM chain creation on lux-mainnet (hanzo, zoo, spc, pars), where the plugin was panicking with "panic in eth.New: triedb parent [<EmptyRootHash>] layer missing" because eth/backend.go inherited geth's path-by-default for empty DBs while the VM hard-refuses path mode upfront. See luxfi/evm v0.18.18 commit 1dea806f8. |
||
|
|
78d157a01f | fix: gofmt -s across repo (CI format check) | ||
|
|
374492edb2 |
go.mod: bump luxfi/genesis v1.12.19 -> v1.13.8 (RLP-matching mainnet+testnet+zoo-mainnet genesis embeds)
Embedded //go:embed configs/{mainnet,testnet,devnet,localnet} now contains
canonical 2-alloc genesis.json's reverted for RLP import compatibility:
- mainnet C-Chain (96369): block 0 = 0x3f4fa2a0... MATCH lux-mainnet-96369.rlp
- testnet C-Chain (96368): block 0 = 0x1c5fe377... MATCH lux-testnet-96368.rlp
- zoo-mainnet (200200): block 0 = 0x7c548af4... MATCH zoo-mainnet-200200.rlp
Each had been wedged by 43 PQ precompiles baked into config.precompileUpgrades
at blockTimestamp:0, mutating state root + producing non-canonical hash.
Activations moved to forward-dated upgrade.json (blockTimestamp 1766708400).
Also bumps pkg/genesis/security to v1.13.8 to stay version-locked.
Tidy drops bft v0.1.5 (no longer indirect).
|
||
|
|
0f6e553215 |
docker: drop gcr.io distroless for scratch runtime in heartbeat-tx example
We don't use gcr.io across lux/hanzo/zoo. Switch the heartbeat-tx example runtime stage from gcr.io/distroless/static-debian12:nonroot to FROM scratch, copying ca-certs, tzdata, and passwd/group from the builder. |
||
|
|
3dd407105b |
Dockerfile: bump EVM_VERSION v0.18.15 → v0.18.16
luxfi/evm v0.18.16 fixes the nil-chainConfig panic at PQ gate that made v1.28.15's image-baked EVM plugin crash on fresh-PVC pq:true bootstrap. Discovered during localnet 1337 bring-up (#148). Root cause in v0.18.15: vm.chainConfig.PQ was being set BEFORE vm.chainConfig was assigned from g.Config — nil-deref in plugin/evm/vm.go Initialize(). v0.18.16 moves the gate AFTER assignment. This unblocks v1.28.16 image build → unblocks localnet 100% green → unblocks cluster deploys (devnet → testnet → mainnet). |
||
|
|
ada7c25f2c |
ci(release): cross-compile darwin binaries on lux-build pool
GitHub-hosted macos-13 queues block the release pipeline for hours. The build is already pure Go cross-compile (CGO_ENABLED=0 GOOS=darwin GOARCH=$arch) so there's no reason to use a Mac runner. Route through the self-hosted lux-build ARC pool (fast, plentiful) instead. Replace 7z (not on ubuntu) with apt-installed zip in the same step. Output filename + artifact name are unchanged. |
||
|
|
fec6f036d4 | go.mod: bump luxfi/consensus v1.25.12 → v1.25.13 (gofmt fixes) |