Files
node/CHANGELOG.md
T
Hanzo DevandGitHub 5e9942cee1 feat(platformvm): multi-version codec — v0 (Apricot/Banff) + v1 (current) — byte-preserving TxID/BlockID across migration (#123)
Closes the codec-version trap that surfaced when the v1.23.x ("Apricot/Banff") tx + block layout was rip-replaced in 409297a089 without bumping the wire-version prefix: mainnet/testnet on-disk blobs (~1.08M+ C-Chain blocks) lost a decoder, and any code path that round-tripped a tx through tx.Initialize re-marshaled it under the new layout — rotating TxID and breaking chain-commitment continuity.

Strategy A per cryptographer / orchestrator brief:

* Register both layouts on the platformvm tx + block codec.Managers under
  distinct wire-version prefixes:
  - CodecVersionV0=0 = v1.23.x slot map (TransferInput=5, hole=6, ...,
    AddPermissionlessValidator=25, ..., DisableL1Validator=39)
  - CodecVersionV1=1 = current slot map (with MintOutput/MintOp at 6/8,
    +4-skip for Banff txs at 27-30, CreateSovereignL1Tx at 36,
    SlashValidatorTx at 41, CreateAssetTx/OperationTx at 42-43)
  - txs.Codec / block.Codec dispatch by the standard 2-byte wire prefix.

* New byte-preserving init: tx.InitializeFromBytes(c, version, signedBytes)
  and tx.InitializeFromBytesAtVersion(c, version) bind the tx to its
  original signedBytes without re-marshalling. tx.Initialize stays as the
  fresh-build path; from-DB / from-wire paths route through the
  byte-preserving variant. TxID = hash(signedBytes) under the version it
  was written at, forever.

* New vms/platformvm/block/v0/ subpackage: 9 v0-only block kinds
  (ApricotProposalBlock, BanffProposalBlock, ... at slots 0-4 + 29-32).
  Pure DTOs — no codec, no Visit. The block package wraps the decoded
  v0.Block in a liftedV0Block adapter that:
  - returns the original bytes verbatim (no re-marshal),
  - BlockID = hash(raw v0 bytes),
  - dispatches Visit to the v1 Visitor arms (ApricotProposalBlock /
    BanffProposalBlock -> ProposalBlock, etc.),
  - re-binds embedded txs at v0 via InitializeFromBytesAtVersion so
    inner TxIDs are also byte-preserved.

* genesis.Parse is wire-version-aware: pre-codec-v1 genesis blobs decode
  at v0, new blobs at v1. The matching codec is used for tx re-binding.

* L1-tx slot map shifts +1 to accommodate CreateSovereignL1Tx at 36
  (RegisterL1Validator 36->37, SetL1ValidatorWeight 37->38,
  IncreaseL1ValidatorBalance 38->39, DisableL1Validator 39->40). Test
  fixtures regenerated.

* 22 fee-calculator fixtures + 11 serialization fixtures bumped to use
  the v1 wire prefix (0x0001) and the post-CreateSovereignL1Tx slot map.

Migration notes:
* Mainnet + testnet P-Chain DBs: NO rebuild. Pre-codec-v1 blocks
  continue to decode through the v0 path with original BlockID and
  TxIDs preserved. New blocks are written at v1 from the cut-over
  height onward.
* Devnet: must be rebuilt before rolling to v1.28.0. Its existing
  blobs carry wire-version 0 but use the post-rip slot map (not the
  v0 Apricot/Banff layout) — decoding them through the v0 path would
  read the wrong types. A fresh bootstrap at v1.28.0 writes v1 bytes
  from height 0 and is internally consistent thereafter.

Tests:
* TestCodecVersionV0V1Coexist, TestParseDispatchesByVersion,
  TestTxIDStabilityRoundTrip, TestCrossVersionRefuses (txs)
* TestParseV0ApricotProposalBlock, TestParseV0BanffStandardBlock,
  TestParseV1RoundTrip, TestVersionPrefixDispatch (block)
* 150 packages, 0 failures under -race
2026-05-31 01:38:42 -07:00

4.7 KiB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.28.0]

Added

  • Multi-version P-Chain block + tx codec. v1.23.x ("Apricot/Banff") wire layout is now registered as CodecVersionV0 on the platformvm tx and block codec.Managers; the current canonical layout is CodecVersionV1. Bytes carry their wire version in the standard 2-byte codec prefix and txs.Parse / block.Parse dispatch by it.
  • Byte-preserving tx Initialization: tx.InitializeFromBytes(c, version, signedBytes) and tx.InitializeFromBytesAtVersion(c, version) bind a tx to its original signedBytes without re-marshalling. TxID = hash(signedBytes) is therefore stable across the v0->v1 migration — mainnet and testnet chain commitments hash back to byte-identical inputs.
  • vms/platformvm/block/v0/ subpackage holding the pure-data v0 block kinds (ApricotProposalBlock, BanffProposalBlock, ... — 9 types, slots 0-4 + 29-32). The package is a one-way decoder only; the liftedV0Block adapter in the parent package translates each v0 block kind into the corresponding canonical v1 Visitor arm without ever re-marshalling.
  • Cross-version tx + block tests: TestCodecVersionV0V1Coexist, TestParseDispatchesByVersion, TestTxIDStabilityRoundTrip, TestCrossVersionRefuses, TestParseV0ApricotProposalBlock, TestParseV0BanffStandardBlock, TestParseV1RoundTrip, TestVersionPrefixDispatch.

Changed

  • tx.Initialize(c) is retained for the fresh-build path only (wallet, builder). The from-DB / from-wire paths in genesis/genesis.go and block/{standard,proposal}_block.go now go through InitializeFromBytesAtVersion against the codec version the surrounding container was decoded under.
  • genesis.Parse is wire-version-aware: pre-codec-v1 P-Chain genesis blobs on mainnet / testnet decode at CodecVersionV0; new blobs are written at CodecVersionV1.
  • The L1-tx slot map advances by one to accommodate CreateSovereignL1Tx at slot 36: RegisterL1ValidatorTx=37, SetL1ValidatorWeightTx=38, IncreaseL1ValidatorBalanceTx=39, DisableL1ValidatorTx=40 (was 36-39). Both pre-rip mainnet/testnet bytes (no slot conflict; L1 txs did not exist) and current code paths line up with the new slot map.
  • Test fixtures in vms/platformvm/txs/fee/calculator_test.go and the serialization tests under vms/platformvm/txs/*_test.go are regenerated to use the v1 wire prefix (0x0001) and the post-CreateSovereignL1Tx slot map.

Migration notes

  • Mainnet + testnet P-Chain DBs do NOT need to be rebuilt: pre-codec-v1 blocks continue to decode via the v0 path with their original BlockID = hash(v0 bytes) preserved. Newly accepted blocks are written at v1.
  • Devnet, which was bootstrapped post-rip at wire-version 0 but with the post-rip slot map, must be rebuilt before rolling to v1.28.0: its existing blobs would now decode against the v0 slot map (the pre-rip layout) and produce wrong types. A fresh bootstrap at v1.28.0 writes v1 bytes from height 0 and is internally consistent thereafter.

[1.13.5-alpha] - 2025-01-23

Added

  • L1 (Layer 1) validator support with complete transaction types:
    • ConvertNetToL1Tx - Convert existing chains to L1
    • RegisterL1ValidatorTx - Register new L1 validators
    • SetL1ValidatorWeightTx - Adjust validator weights
    • IncreaseL1ValidatorBalanceTx - Increase validator balance
    • DisableL1ValidatorTx - Disable validators
  • LP-118 protocol implementation for warp message handling:
    • Signature aggregation support
    • BLS signature verification
    • Cached handler for performance optimization
    • Handler adapter for P2P integration
  • Complete wallet support for L1 validator operations
  • Extended AppSender interface for cross-chain messaging

Fixed

  • P2P test package compatibility issues
  • Set package import conflicts (math/set vs utils/set vs consensus/utils/set)
  • Interface compatibility between consensus and local packages
  • Handler function signatures for proper interface implementation
  • Mock testing with gomock package updates
  • BLS signature handling in tests
  • AppError type conversions between packages
  • All wallet examples now compile and run correctly

Changed

  • Updated import paths to use luxfi packages consistently
  • Improved error handling in P2P message handlers
  • Enhanced test coverage for LP-118 protocol
  • Standardized AppError usage across packages

Technical Details

  • 100% of internal packages (351 packages) now build successfully
  • All tests pass in modified packages
  • Full CI/CD pipeline configured with GitHub Actions
  • Compatible with Go 1.21.12+

[1.13.4] - Previous Release

[Previous release notes...]