mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 20:47:52 +00:00
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 b1e265a708 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
This commit is contained in:
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [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.Manager`s; 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
|
||||
|
||||
@@ -19,7 +19,36 @@
|
||||
"v1.23.24",
|
||||
"v1.23.25",
|
||||
"v1.26.12",
|
||||
"v1.26.13"
|
||||
"v1.26.13",
|
||||
"v1.27.0",
|
||||
"v1.27.1",
|
||||
"v1.27.2",
|
||||
"v1.27.3",
|
||||
"v1.27.4",
|
||||
"v1.27.5",
|
||||
"v1.27.6",
|
||||
"v1.27.7",
|
||||
"v1.27.8",
|
||||
"v1.27.9",
|
||||
"v1.27.10",
|
||||
"v1.27.11",
|
||||
"v1.27.12",
|
||||
"v1.27.13",
|
||||
"v1.27.14",
|
||||
"v1.27.15",
|
||||
"v1.27.16",
|
||||
"v1.27.17",
|
||||
"v1.27.18",
|
||||
"v1.27.19",
|
||||
"v1.27.20",
|
||||
"v1.27.21",
|
||||
"v1.27.22",
|
||||
"v1.27.23",
|
||||
"v1.27.24",
|
||||
"v1.27.25",
|
||||
"v1.27.26",
|
||||
"v1.27.27",
|
||||
"v1.28.0"
|
||||
],
|
||||
"41": [
|
||||
"v1.13.2"
|
||||
@@ -134,4 +163,4 @@
|
||||
"v1.8.5",
|
||||
"v1.8.6"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -76,8 +76,8 @@ var (
|
||||
// These should match the latest git tag
|
||||
const (
|
||||
defaultMajor = 1
|
||||
defaultMinor = 26
|
||||
defaultPatch = 13
|
||||
defaultMinor = 28
|
||||
defaultPatch = 0
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package block
|
||||
@@ -10,56 +10,99 @@ import (
|
||||
"github.com/luxfi/codec"
|
||||
"github.com/luxfi/codec/linearcodec"
|
||||
"github.com/luxfi/codec/wrappers"
|
||||
"github.com/luxfi/node/vms/platformvm/block/v0"
|
||||
"github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
const CodecVersion = txs.CodecVersion
|
||||
const (
|
||||
// CodecVersionV0 is the v1.23.x ("Apricot/Banff") wire layout. It is
|
||||
// retained as a READ-ONLY decoder so that pre-codec-v1 blocks on
|
||||
// disk continue to deserialize and so that v0-derived BlockIDs and
|
||||
// TxIDs remain stable. All write paths MUST use CodecVersionV1.
|
||||
CodecVersionV0 = txs.CodecVersionV0
|
||||
|
||||
// CodecVersionV1 is the current canonical block-codec wire layout.
|
||||
// Every block built by this binary is written at v1.
|
||||
CodecVersionV1 = txs.CodecVersionV1
|
||||
|
||||
// CodecVersion is the canonical write version. All Marshal call
|
||||
// sites in this package use CodecVersion so that any future bump of
|
||||
// the write target updates exactly one symbol.
|
||||
CodecVersion = CodecVersionV1
|
||||
)
|
||||
|
||||
var (
|
||||
// GenesisCodec allows blocks of larger than usual size to be parsed.
|
||||
// While this gives flexibility in accommodating large genesis blocks
|
||||
// it must not be used to parse new, unverified blocks which instead
|
||||
// must be processed by Codec.
|
||||
GenesisCodec codec.Manager
|
||||
|
||||
// Codec is the v1 write/read codec.Manager. It carries ONLY the v1
|
||||
// slot map and so decodes only v1-prefixed bytes. v0 bytes must go
|
||||
// through ParseBytes (which routes to v0Codec for version 0).
|
||||
Codec codec.Manager
|
||||
|
||||
// genesisLinearCodec is the underlying codec for GenesisCodec.
|
||||
// This is exposed for registering additional types from other packages.
|
||||
// GenesisCodec is the v1 large-size codec.Manager. Same v1 slot map
|
||||
// as Codec but with an unbounded maximum size for genesis decode and
|
||||
// state-read fallback paths.
|
||||
GenesisCodec codec.Manager
|
||||
|
||||
// v0Codec is the v1.23.x read-only codec.Manager. It registers v0
|
||||
// block + tx slots and unmarshals into v0.Block (the v0 interface),
|
||||
// not block.Block — these are two distinct interface destinations.
|
||||
// External packages MUST NOT Marshal at v0; that is verified at
|
||||
// Parse time (Parse never re-marshals).
|
||||
v0Codec codec.Manager
|
||||
|
||||
// v0GenesisCodec mirrors v0Codec at large size.
|
||||
v0GenesisCodec codec.Manager
|
||||
|
||||
// genesisLinearCodec is the underlying v1 codec for GenesisCodec.
|
||||
// This is exposed for registering additional types from other
|
||||
// packages (e.g. state) at the canonical write version. The v0
|
||||
// codecs are CLOSED to external registration — their slot maps are
|
||||
// frozen at the v1.23.x layout.
|
||||
genesisLinearCodec linearcodec.Codec
|
||||
)
|
||||
|
||||
func init() {
|
||||
c := linearcodec.NewDefault()
|
||||
gc := linearcodec.NewDefault()
|
||||
cV1 := linearcodec.NewDefault()
|
||||
gcV1 := linearcodec.NewDefault()
|
||||
cV0 := linearcodec.NewDefault()
|
||||
gcV0 := linearcodec.NewDefault()
|
||||
|
||||
errs := wrappers.Errs{}
|
||||
for _, c := range []linearcodec.Codec{c, gc} {
|
||||
for _, c := range []linearcodec.Codec{cV1, gcV1} {
|
||||
errs.Add(RegisterBlockTypes(c))
|
||||
}
|
||||
for _, c := range []linearcodec.Codec{cV0, gcV0} {
|
||||
errs.Add(v0.RegisterBlockTypes(c))
|
||||
}
|
||||
|
||||
Codec = codec.NewDefaultManager()
|
||||
GenesisCodec = codec.NewManager(math.MaxInt32)
|
||||
v0Codec = codec.NewDefaultManager()
|
||||
v0GenesisCodec = codec.NewManager(math.MaxInt32)
|
||||
errs.Add(
|
||||
Codec.RegisterCodec(CodecVersion, c),
|
||||
GenesisCodec.RegisterCodec(CodecVersion, gc),
|
||||
Codec.RegisterCodec(CodecVersionV1, cV1),
|
||||
GenesisCodec.RegisterCodec(CodecVersionV1, gcV1),
|
||||
v0Codec.RegisterCodec(CodecVersionV0, cV0),
|
||||
v0GenesisCodec.RegisterCodec(CodecVersionV0, gcV0),
|
||||
)
|
||||
if errs.Errored() {
|
||||
panic(errs.Err)
|
||||
}
|
||||
genesisLinearCodec = gc
|
||||
genesisLinearCodec = gcV1
|
||||
}
|
||||
|
||||
// RegisterGenesisType registers a type with the GenesisCodec.
|
||||
// This is used by other packages (e.g. state) to register types that are
|
||||
// only ever encountered in genesis bytes.
|
||||
// RegisterGenesisType registers a type with the GenesisCodec at the
|
||||
// canonical write version (v1). This is used by other packages (e.g.
|
||||
// state) to register types that are only ever encountered in genesis
|
||||
// bytes. The v0 codec is closed to external registration — its slot
|
||||
// map is frozen at the v1.23.x layout.
|
||||
func RegisterGenesisType(val interface{}) error {
|
||||
return genesisLinearCodec.RegisterType(val)
|
||||
}
|
||||
|
||||
// RegisterBlockTypes registers the canonical block type IDs. There is exactly
|
||||
// one type per block kind: StandardBlock, ProposalBlock, CommitBlock,
|
||||
// AbortBlock. Tx types come from txs.RegisterTypes.
|
||||
// RegisterBlockTypes registers the canonical v1 block type IDs. There
|
||||
// is exactly one type per block kind: ProposalBlock, AbortBlock,
|
||||
// CommitBlock, StandardBlock. Tx types come from txs.RegisterTypes
|
||||
// (which registers the v1 tx slot layout).
|
||||
func RegisterBlockTypes(targetCodec linearcodec.Codec) error {
|
||||
return errors.Join(
|
||||
txs.RegisterTypes(targetCodec),
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
hash "github.com/luxfi/crypto/hash"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/runtime"
|
||||
"github.com/luxfi/node/vms/platformvm/block/v0"
|
||||
"github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
// errLegacyAtomicBlock is returned when v0 decoding produces an
|
||||
// ApricotAtomicBlock. The modern P-only network does not accept atomic
|
||||
// blocks; any pre-Banff atomic block on disk must be handled by the
|
||||
// caller (typically by skipping the entry — atomic block IDs are not
|
||||
// reachable from the canonical Banff-era last-accepted chain).
|
||||
var errLegacyAtomicBlock = errors.New("apricot atomic block is not supported on the P-only network")
|
||||
|
||||
// liftV0 wraps a v0-decoded block in an adapter that implements
|
||||
// block.Block and dispatches Visit to the canonical v1 Visitor.
|
||||
//
|
||||
// The lifted block:
|
||||
// - returns b verbatim from Bytes() (no re-marshal),
|
||||
// - reports BlockID = hash(b),
|
||||
// - reports Height/Parent/Txs delegated to the v0 struct,
|
||||
// - dispatches Visit per the kind table:
|
||||
// ApricotProposalBlock, BanffProposalBlock -> StandardBlock
|
||||
// (proposal/commit/abort are dead block kinds on the P-only
|
||||
// network — modern blocks are all StandardBlock with proposal
|
||||
// txs inlined as decision txs; on the historical decode path
|
||||
// we surface them via the StandardBlock visitor so the
|
||||
// executor's StandardBlock arm runs the embedded txs without
|
||||
// requiring a separate Apricot/Banff visitor surface),
|
||||
// ApricotStandardBlock, BanffStandardBlock -> StandardBlock,
|
||||
// ApricotAbortBlock, BanffAbortBlock -> AbortBlock,
|
||||
// ApricotCommitBlock, BanffCommitBlock -> CommitBlock.
|
||||
// - re-initializes each embedded tx via tx.InitializeFromBytes
|
||||
// against the v0 tx codec, byte-preserving inner TxIDs.
|
||||
//
|
||||
// initVisit selects the v1 Visitor arm. The lifting table is the
|
||||
// migration's only place where a v0 block kind maps to a v1 kind; once
|
||||
// every block on disk is v1, this file becomes dead code and can be
|
||||
// removed.
|
||||
func liftV0(b v0.Block, raw []byte) (Block, error) {
|
||||
switch v := b.(type) {
|
||||
case *v0.ApricotAtomicBlock:
|
||||
_ = v
|
||||
return nil, errLegacyAtomicBlock
|
||||
}
|
||||
|
||||
blkID := hash.ComputeHash256Array(raw)
|
||||
lifted := &liftedV0Block{
|
||||
blockID: blkID,
|
||||
raw: raw,
|
||||
v0: b,
|
||||
parentID: b.ParentID(),
|
||||
height: b.Height(),
|
||||
timeUnix: b.TimestampUnix(),
|
||||
txs: b.Txs(),
|
||||
}
|
||||
if err := lifted.initTxs(); err != nil {
|
||||
return nil, fmt.Errorf("v0 lift: %w", err)
|
||||
}
|
||||
return lifted, nil
|
||||
}
|
||||
|
||||
// liftedV0Block is the block.Block adapter around a v0-decoded block.
|
||||
// It does not embed CommonBlock — the canonical CommonBlock owns its
|
||||
// own bytes via SetBytes, which would re-derive BlockID from a
|
||||
// marshalled v1 layout. The lifted block instead computes BlockID once
|
||||
// at construction time from the original v0 bytes and stashes them.
|
||||
type liftedV0Block struct {
|
||||
blockID ids.ID
|
||||
raw []byte
|
||||
v0 v0.Block
|
||||
parentID ids.ID
|
||||
height uint64
|
||||
timeUnix uint64
|
||||
txs []*txs.Tx
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) ID() ids.ID { return b.blockID }
|
||||
func (b *liftedV0Block) Parent() ids.ID { return b.parentID }
|
||||
func (b *liftedV0Block) Bytes() []byte { return b.raw }
|
||||
func (b *liftedV0Block) Height() uint64 { return b.height }
|
||||
func (b *liftedV0Block) Txs() []*txs.Tx { return b.txs }
|
||||
|
||||
func (b *liftedV0Block) Timestamp() time.Time { return time.Unix(int64(b.timeUnix), 0) }
|
||||
|
||||
func (b *liftedV0Block) initialize(_ []byte) error {
|
||||
// liftedV0Block has its identity (BlockID, raw bytes) committed at
|
||||
// liftV0 construction time from the original input bytes. The
|
||||
// argument here is the v1-marshalled bytes that block.Parse would
|
||||
// derive for a v1 block; for a v0 block we ignore it and keep raw.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) InitRuntime(rt *runtime.Runtime) {
|
||||
for _, tx := range b.txs {
|
||||
if tx == nil {
|
||||
continue
|
||||
}
|
||||
tx.Unsigned.InitRuntime(rt)
|
||||
}
|
||||
}
|
||||
|
||||
// initTxs runs InitializeFromBytes on every embedded tx using the v0
|
||||
// tx codec. The signed bytes the codec recorded during Unmarshal are
|
||||
// what we need; we recover them by re-marshalling the unsigned half
|
||||
// under v0 and slicing into the parent block's input bytes is not
|
||||
// safe (the parent block bytes interleave block-header fields). The
|
||||
// linearcodec leaves the unsigned/credentials split implicit in the
|
||||
// stream offset, so the cleanest byte-preserving handle we have is to
|
||||
// ask the v0 tx codec to Size(version, &tx.Unsigned) and then take
|
||||
// the prefix from the tx's own bytes — but those bytes have not been
|
||||
// set yet by linearcodec, only the struct fields.
|
||||
//
|
||||
// We therefore re-marshal each tx under the v0 codec to recover its
|
||||
// signedBytes. The wire format is deterministic and the resulting
|
||||
// bytes are byte-equal to whatever the v0 producer emitted; the
|
||||
// re-marshal is a closed loop over the v0 layout the tx was decoded
|
||||
// from. TxID = hash(re-marshalled v0 bytes) = TxID the chain
|
||||
// originally committed.
|
||||
//
|
||||
// This single re-marshal is allowed only here, only against the v0
|
||||
// codec, and only for txs whose Unsigned was decoded under v0. Under
|
||||
// the v1 path, txs are byte-preserved via tx.Parse without any
|
||||
// re-marshal.
|
||||
func (b *liftedV0Block) initTxs() error {
|
||||
for _, tx := range b.txs {
|
||||
if tx == nil {
|
||||
continue
|
||||
}
|
||||
if err := tx.InitializeFromBytesAtVersion(txs.GenesisCodec, txs.CodecVersionV0); err != nil {
|
||||
return fmt.Errorf("byte-preserve v0 tx: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) Visit(v Visitor) error {
|
||||
switch b.v0.(type) {
|
||||
case *v0.ApricotAbortBlock, *v0.BanffAbortBlock:
|
||||
return v.AbortBlock(b.asAbortBlock())
|
||||
case *v0.ApricotCommitBlock, *v0.BanffCommitBlock:
|
||||
return v.CommitBlock(b.asCommitBlock())
|
||||
case *v0.ApricotProposalBlock, *v0.BanffProposalBlock:
|
||||
return v.ProposalBlock(b.asProposalBlock())
|
||||
case *v0.ApricotStandardBlock, *v0.BanffStandardBlock:
|
||||
return v.StandardBlock(b.asStandardBlock())
|
||||
default:
|
||||
return fmt.Errorf("v0 lift: unhandled block kind %T", b.v0)
|
||||
}
|
||||
}
|
||||
|
||||
// asStandardBlock builds a *StandardBlock that mirrors the lifted v0
|
||||
// block. The returned block uses the SAME raw bytes (so its BlockID
|
||||
// matches b.blockID) and the SAME Txs slice (so the executor sees the
|
||||
// same set of txs). This is the synthetic v1 view of a v0 block.
|
||||
func (b *liftedV0Block) asStandardBlock() *StandardBlock {
|
||||
return &StandardBlock{
|
||||
Time: b.timeUnix,
|
||||
CommonBlock: b.commonBlock(),
|
||||
Transactions: b.txs,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) asProposalBlock() *ProposalBlock {
|
||||
// Modern ProposalBlock has Tx + Transactions tail. The lifted v0
|
||||
// block's Txs() already returns the canonical ordering for both
|
||||
// Apricot (single proposal Tx) and Banff (decision tail + proposal
|
||||
// Tx). We split off the trailing element as the proposal tx.
|
||||
tail := b.txs
|
||||
if len(tail) == 0 {
|
||||
return &ProposalBlock{
|
||||
Time: b.timeUnix,
|
||||
CommonBlock: b.commonBlock(),
|
||||
}
|
||||
}
|
||||
last := tail[len(tail)-1]
|
||||
return &ProposalBlock{
|
||||
Time: b.timeUnix,
|
||||
Transactions: tail[:len(tail)-1],
|
||||
CommonBlock: b.commonBlock(),
|
||||
Tx: last,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) asAbortBlock() *AbortBlock {
|
||||
return &AbortBlock{Time: b.timeUnix, CommonBlock: b.commonBlock()}
|
||||
}
|
||||
|
||||
func (b *liftedV0Block) asCommitBlock() *CommitBlock {
|
||||
return &CommitBlock{Time: b.timeUnix, CommonBlock: b.commonBlock()}
|
||||
}
|
||||
|
||||
// commonBlock builds a CommonBlock whose BlockID + bytes are the
|
||||
// lifted block's raw bytes / hash. This is the only place we copy the
|
||||
// raw bytes into a v1-typed block, and it is read-only — the caller
|
||||
// uses the synthetic block's visitor methods, never re-marshals it.
|
||||
func (b *liftedV0Block) commonBlock() CommonBlock {
|
||||
return CommonBlock{
|
||||
PrntID: b.parentID,
|
||||
Hght: b.height,
|
||||
BlockID: b.blockID,
|
||||
bytes: b.raw,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
hash "github.com/luxfi/crypto/hash"
|
||||
"github.com/luxfi/node/vms/platformvm/block/v0"
|
||||
"github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
// TestParseV0ApricotProposalBlock asserts that a hand-encoded
|
||||
// ApricotProposalBlock (slot 0 in v0) decodes via Parse and is lifted
|
||||
// into a *ProposalBlock with the original bytes preserved.
|
||||
//
|
||||
// The Apricot proposal flow on the modern P-only network is replayed
|
||||
// through the canonical Visitor.ProposalBlock arm. The fixture here
|
||||
// is constructed by re-marshalling a synthetic ApricotProposalBlock
|
||||
// at v0 — this is a closed-loop test of the v0 codec's encode + decode
|
||||
// path; on mainnet the bytes come from disk, not from re-encoding.
|
||||
func TestParseV0ApricotProposalBlock(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Build a synthetic v0 ApricotProposalBlock carrying an
|
||||
// AdvanceTimeTx proposal — the simplest decision form pre-Banff.
|
||||
proposalTx := &txs.Tx{Unsigned: &txs.AdvanceTimeTx{Time: 1_700_000_000}}
|
||||
require.NoError(proposalTx.InitializeFromBytesAtVersion(txs.GenesisCodec, txs.CodecVersionV0))
|
||||
|
||||
v0Blk := &v0.ApricotProposalBlock{
|
||||
CommonBlock: v0.CommonBlock{Hght: 7},
|
||||
Tx: proposalTx,
|
||||
}
|
||||
|
||||
raw, err := v0GenesisCodec.Marshal(CodecVersionV0, &([]v0.Block{v0Blk})[0])
|
||||
require.NoError(err, "v0 marshal of ApricotProposalBlock")
|
||||
|
||||
// Parse via the version-aware block.Parse path.
|
||||
parsed, err := Parse(GenesisCodec, raw)
|
||||
require.NoError(err, "parse v0 ApricotProposalBlock")
|
||||
|
||||
// The lifted block reports the canonical fields.
|
||||
require.Equal(uint64(7), parsed.Height())
|
||||
require.True(bytes.Equal(raw, parsed.Bytes()),
|
||||
"lifted v0 block must return original bytes verbatim")
|
||||
require.Equal(hash.ComputeHash256Array(raw), [32]byte(parsed.ID()),
|
||||
"lifted v0 block ID = hash(raw v0 bytes)")
|
||||
require.Len(parsed.Txs(), 1, "ApricotProposalBlock carries exactly one tx")
|
||||
require.Equal(proposalTx.TxID, parsed.Txs()[0].TxID,
|
||||
"embedded tx TxID byte-preserved")
|
||||
}
|
||||
|
||||
// TestParseV0BanffStandardBlock asserts that a hand-encoded
|
||||
// BanffStandardBlock (slot 32 in v0) decodes via Parse and lifts to
|
||||
// a *StandardBlock that carries the original timestamp.
|
||||
func TestParseV0BanffStandardBlock(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
decisionTx := &txs.Tx{Unsigned: &txs.AdvanceTimeTx{Time: 1_700_000_000}}
|
||||
require.NoError(decisionTx.InitializeFromBytesAtVersion(txs.GenesisCodec, txs.CodecVersionV0))
|
||||
|
||||
v0Blk := &v0.BanffStandardBlock{
|
||||
Time: 1_700_000_100,
|
||||
ApricotStandardBlock: v0.ApricotStandardBlock{
|
||||
CommonBlock: v0.CommonBlock{Hght: 42},
|
||||
Transactions: []*txs.Tx{decisionTx},
|
||||
},
|
||||
}
|
||||
|
||||
raw, err := v0GenesisCodec.Marshal(CodecVersionV0, &([]v0.Block{v0Blk})[0])
|
||||
require.NoError(err)
|
||||
|
||||
parsed, err := Parse(GenesisCodec, raw)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(uint64(42), parsed.Height())
|
||||
require.True(bytes.Equal(raw, parsed.Bytes()))
|
||||
require.Equal(hash.ComputeHash256Array(raw), [32]byte(parsed.ID()))
|
||||
require.Len(parsed.Txs(), 1)
|
||||
}
|
||||
|
||||
// TestParseV1RoundTrip asserts that the canonical v1 path is
|
||||
// byte-preserving the same way: a v1 block decoded via Parse keeps
|
||||
// its original bytes and its BlockID = hash(raw).
|
||||
func TestParseV1RoundTrip(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
tx := &txs.Tx{Unsigned: &txs.AdvanceTimeTx{Time: 1_700_000_000}}
|
||||
require.NoError(tx.InitializeFromBytesAtVersion(txs.GenesisCodec, txs.CodecVersionV1))
|
||||
|
||||
blk := &StandardBlock{
|
||||
Time: 1_700_000_100,
|
||||
CommonBlock: CommonBlock{Hght: 5},
|
||||
Transactions: []*txs.Tx{tx},
|
||||
}
|
||||
// Use the canonical block-build path (initialize sets bytes
|
||||
// from a Marshal).
|
||||
require.NoError(initialize(blk, &blk.CommonBlock))
|
||||
|
||||
raw := blk.Bytes()
|
||||
parsed, err := Parse(GenesisCodec, raw)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(uint64(5), parsed.Height())
|
||||
require.True(bytes.Equal(raw, parsed.Bytes()))
|
||||
require.Equal(blk.ID(), parsed.ID(), "BlockID stable across Parse")
|
||||
}
|
||||
|
||||
// TestVersionPrefixDispatch asserts that Parse dispatches by the
|
||||
// 2-byte wire prefix and rejects unknown versions.
|
||||
func TestVersionPrefixDispatch(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Empty / too-short input.
|
||||
_, err := Parse(GenesisCodec, []byte{})
|
||||
require.ErrorIs(err, ErrShortBytes)
|
||||
_, err = Parse(GenesisCodec, []byte{0x00})
|
||||
require.ErrorIs(err, ErrShortBytes)
|
||||
|
||||
// Unknown version (v2+).
|
||||
_, err = Parse(GenesisCodec, []byte{0x00, 0x02, 0x00})
|
||||
require.Error(err, "unknown version must error")
|
||||
}
|
||||
@@ -1,14 +1,97 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package block
|
||||
|
||||
import "github.com/luxfi/codec"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/codec"
|
||||
"github.com/luxfi/node/vms/platformvm/block/v0"
|
||||
)
|
||||
|
||||
// ErrShortBytes is returned when the input is shorter than the 2-byte
|
||||
// codec-version prefix.
|
||||
var ErrShortBytes = errors.New("block bytes too short for codec version prefix")
|
||||
|
||||
// Parse decodes a block byte stream produced by either the v0
|
||||
// (v1.23.x) or v1 (current) block codec. The codec.Manager argument
|
||||
// selects the SIZE class — Codec (1 MiB max) or GenesisCodec
|
||||
// (unbounded). The actual VERSION is taken from the 2-byte wire prefix
|
||||
// and dispatched to the matching slot map.
|
||||
//
|
||||
// Parse never re-marshals the input; BlockID = hash(b) verbatim and
|
||||
// b is stashed on the returned block's CommonBlock. This is the
|
||||
// byte-preserving path that keeps BlockIDs (and inner TxIDs) stable
|
||||
// across the v0->v1 migration.
|
||||
//
|
||||
// c must be one of block.Codec or block.GenesisCodec. The v0 codec is
|
||||
// selected internally based on the size class of c. Other codecs are
|
||||
// rejected with codec.ErrUnknownVersion.
|
||||
func Parse(c codec.Manager, b []byte) (Block, error) {
|
||||
if len(b) < 2 {
|
||||
return nil, ErrShortBytes
|
||||
}
|
||||
version := uint16(b[0])<<8 | uint16(b[1])
|
||||
|
||||
switch version {
|
||||
case CodecVersionV1:
|
||||
return parseV1(c, b)
|
||||
case CodecVersionV0:
|
||||
return parseV0(c, b)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %d", codec.ErrUnknownVersion, version)
|
||||
}
|
||||
}
|
||||
|
||||
// parseV1 is the canonical block-decode path. Bytes are unmarshalled
|
||||
// against the v1 slot map and the resulting Block keeps b as its
|
||||
// authoritative bytes.
|
||||
func parseV1(c codec.Manager, b []byte) (Block, error) {
|
||||
var blk Block
|
||||
if _, err := c.Unmarshal(b, &blk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blk, blk.initialize(b)
|
||||
}
|
||||
|
||||
// parseV0 decodes a v1.23.x ("Apricot/Banff") block. The v0 slot map
|
||||
// is closed; the result is a v0-typed block (one of v0.ApricotXxx /
|
||||
// v0.BanffXxx) that is wrapped in an adapter implementing block.Block.
|
||||
//
|
||||
// The adapter:
|
||||
// - reports the original bytes verbatim (Bytes() returns b),
|
||||
// - computes BlockID = hash(b),
|
||||
// - translates Visit calls to the canonical v1 visitor
|
||||
// (ApricotProposalBlock/BanffProposalBlock -> Visitor.ProposalBlock,
|
||||
// ApricotStandardBlock/BanffStandardBlock -> Visitor.StandardBlock,
|
||||
// etc.),
|
||||
// - re-initializes embedded txs using their byte-preserving
|
||||
// InitializeFromBytes path against the v0 tx codec, so inner TxIDs
|
||||
// remain hash(originalTxBytes) under the v0 layout.
|
||||
//
|
||||
// ApricotAtomicBlock is decoded but rejected at the block boundary: the
|
||||
// modern P-only network does not accept new atomic blocks, and any
|
||||
// pre-Banff atomic block left on disk should have been replaced by the
|
||||
// Banff history. We return an explicit error so the caller can decide
|
||||
// whether to surface it as DB corruption or as a soft skip.
|
||||
func parseV0(c codec.Manager, b []byte) (Block, error) {
|
||||
v0c := v0CodecFor(c)
|
||||
var v0blk v0.Block
|
||||
if _, err := v0c.Unmarshal(b, &v0blk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return liftV0(v0blk, b)
|
||||
}
|
||||
|
||||
// v0CodecFor returns the v0 codec.Manager that matches the size class
|
||||
// of c. We mirror the size class so that a GenesisCodec caller (large
|
||||
// max size) gets the v0GenesisCodec (also large) rather than v0Codec
|
||||
// (1 MiB).
|
||||
func v0CodecFor(c codec.Manager) codec.Manager {
|
||||
if c == GenesisCodec {
|
||||
return v0GenesisCodec
|
||||
}
|
||||
return v0Codec
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ func (b *ProposalBlock) Timestamp() time.Time { return time.Unix(int64(b.Time),
|
||||
|
||||
func (b *ProposalBlock) initialize(bytes []byte) error {
|
||||
b.CommonBlock.initialize(bytes)
|
||||
if err := b.Tx.Initialize(txs.Codec); err != nil {
|
||||
return fmt.Errorf("failed to initialize tx: %w", err)
|
||||
if err := b.Tx.InitializeFromBytesAtVersion(txs.Codec, txs.CodecVersionV1); err != nil {
|
||||
return fmt.Errorf("failed to initialize proposal tx: %w", err)
|
||||
}
|
||||
for _, tx := range b.Transactions {
|
||||
if err := tx.Initialize(txs.Codec); err != nil {
|
||||
return fmt.Errorf("failed to initialize tx: %w", err)
|
||||
if err := tx.InitializeFromBytesAtVersion(txs.Codec, txs.CodecVersionV1); err != nil {
|
||||
return fmt.Errorf("failed to initialize decision tx: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -29,7 +29,11 @@ func (b *StandardBlock) Timestamp() time.Time { return time.Unix(int64(b.Time),
|
||||
func (b *StandardBlock) initialize(bytes []byte) error {
|
||||
b.CommonBlock.initialize(bytes)
|
||||
for _, tx := range b.Transactions {
|
||||
if err := tx.Initialize(txs.Codec); err != nil {
|
||||
// Byte-preserving: re-derive signedBytes by re-marshalling at
|
||||
// the canonical write version (v1). This is the v1 read path
|
||||
// — v0 reads go through block.parseV0 + liftedV0Block, which
|
||||
// rebinds via InitializeFromBytesAtVersion(v0).
|
||||
if err := tx.InitializeFromBytesAtVersion(txs.Codec, txs.CodecVersionV1); err != nil {
|
||||
return fmt.Errorf("failed to initialize tx: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package v0 defines the v1.23.x ("Apricot/Banff") P-Chain block layout.
|
||||
//
|
||||
// These types EXIST ONLY FOR DECODING pre-codec-v1 blocks read from disk
|
||||
// (mainnet, testnet) and from the wire during catch-up. New blocks are
|
||||
// always written at codec version 1 using the canonical block types in
|
||||
// the parent block package.
|
||||
//
|
||||
// Invariants:
|
||||
// - Wire layout is byte-for-byte identical to v1.23.31's
|
||||
// vms/platformvm/block package (slot IDs, struct layout).
|
||||
// - Each v0 block satisfies block.Block by translating its Visit call
|
||||
// to the canonical v1 visitor method (ApricotProposalBlock ->
|
||||
// StandardBlock at proposal-tail position, etc.).
|
||||
// - Block bytes are byte-preserved (BlockID = hash(input bytes); the
|
||||
// input bytes are stashed on CommonBlock and returned verbatim by
|
||||
// Bytes()). The block is never re-marshaled.
|
||||
// - Embedded txs are byte-preserved via tx.InitializeFromBytes using
|
||||
// the codec version their bytes were decoded under.
|
||||
package v0
|
||||
|
||||
import (
|
||||
"github.com/luxfi/codec/linearcodec"
|
||||
"github.com/luxfi/codec/wrappers"
|
||||
"github.com/luxfi/node/vms/platformvm/stakeable"
|
||||
"github.com/luxfi/node/vms/platformvm/signer"
|
||||
"github.com/luxfi/node/vms/platformvm/txs"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
)
|
||||
|
||||
// CodecVersion is the wire-version prefix for the v1.23.x layout.
|
||||
const CodecVersion uint16 = 0
|
||||
|
||||
// RegisterBlockTypes registers the v0 block + tx type IDs in their
|
||||
// canonical v1.23.x order on the given linearcodec.
|
||||
//
|
||||
// Slot map (block codec):
|
||||
//
|
||||
// 0-4 Apricot blocks (Proposal, Abort, Commit, Standard, Atomic)
|
||||
// 5 secp256k1fx.TransferInput
|
||||
// 6 [skipped — was MintOutput, never used on P-Chain]
|
||||
// 7 secp256k1fx.TransferOutput
|
||||
// 8 [skipped — was MintOperation, never used on P-Chain]
|
||||
// 9-11 secp256k1fx.{Credential, Input, OutputOwners}
|
||||
// 12-20 Apricot txs (AddValidator..RewardValidator)
|
||||
// 21-22 stakeable.{LockIn, LockOut}
|
||||
// 23-26 Banff txs (RemoveChainValidator..AddPermissionlessDelegator)
|
||||
// 27-28 signer.{Empty, ProofOfPossession}
|
||||
// 29-32 Banff blocks (Proposal, Abort, Commit, Standard)
|
||||
// 33-34 Durango txs (TransferChainOwnership, BaseTx)
|
||||
// 35-39 Etna txs (ConvertChainToL1..DisableL1Validator)
|
||||
//
|
||||
// ConvertChainToL1Tx is registered as the wire-format-equivalent
|
||||
// ConvertNetworkToL1Tx (post-rename source type, identical struct
|
||||
// layout, same slot 35). Codec wire matching is by slot ID, not type
|
||||
// name; the rename did not change the serialized format.
|
||||
func RegisterBlockTypes(c linearcodec.Codec) error {
|
||||
errs := wrappers.Errs{}
|
||||
|
||||
// Slots 0-4: Apricot blocks.
|
||||
errs.Add(
|
||||
c.RegisterType(&ApricotProposalBlock{}),
|
||||
c.RegisterType(&ApricotAbortBlock{}),
|
||||
c.RegisterType(&ApricotCommitBlock{}),
|
||||
c.RegisterType(&ApricotStandardBlock{}),
|
||||
c.RegisterType(&ApricotAtomicBlock{}),
|
||||
)
|
||||
|
||||
// Slots 5-11: secp256k1fx with two historical skips at 6 and 8.
|
||||
errs.Add(c.RegisterType(&secp256k1fx.TransferInput{}))
|
||||
c.SkipRegistrations(1)
|
||||
errs.Add(c.RegisterType(&secp256k1fx.TransferOutput{}))
|
||||
c.SkipRegistrations(1)
|
||||
errs.Add(
|
||||
c.RegisterType(&secp256k1fx.Credential{}),
|
||||
c.RegisterType(&secp256k1fx.Input{}),
|
||||
c.RegisterType(&secp256k1fx.OutputOwners{}),
|
||||
)
|
||||
|
||||
// Slots 12-20: Apricot txs.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.AddValidatorTx{}),
|
||||
c.RegisterType(&txs.AddChainValidatorTx{}),
|
||||
c.RegisterType(&txs.AddDelegatorTx{}),
|
||||
c.RegisterType(&txs.CreateNetworkTx{}),
|
||||
c.RegisterType(&txs.CreateChainTx{}),
|
||||
c.RegisterType(&txs.ImportTx{}),
|
||||
c.RegisterType(&txs.ExportTx{}),
|
||||
c.RegisterType(&txs.AdvanceTimeTx{}),
|
||||
c.RegisterType(&txs.RewardValidatorTx{}),
|
||||
)
|
||||
|
||||
// Slots 21-22: stakeable locks.
|
||||
errs.Add(
|
||||
c.RegisterType(&stakeable.LockIn{}),
|
||||
c.RegisterType(&stakeable.LockOut{}),
|
||||
)
|
||||
|
||||
// Slots 23-26: Banff txs.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.RemoveChainValidatorTx{}),
|
||||
c.RegisterType(&txs.TransformChainTx{}),
|
||||
c.RegisterType(&txs.AddPermissionlessValidatorTx{}),
|
||||
c.RegisterType(&txs.AddPermissionlessDelegatorTx{}),
|
||||
)
|
||||
|
||||
// Slots 27-28: signer types.
|
||||
errs.Add(
|
||||
c.RegisterType(&signer.Empty{}),
|
||||
c.RegisterType(&signer.ProofOfPossession{}),
|
||||
)
|
||||
|
||||
// Slots 29-32: Banff blocks.
|
||||
errs.Add(
|
||||
c.RegisterType(&BanffProposalBlock{}),
|
||||
c.RegisterType(&BanffAbortBlock{}),
|
||||
c.RegisterType(&BanffCommitBlock{}),
|
||||
c.RegisterType(&BanffStandardBlock{}),
|
||||
)
|
||||
|
||||
// Slots 33-34: Durango txs.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.TransferChainOwnershipTx{}),
|
||||
c.RegisterType(&txs.BaseTx{}),
|
||||
)
|
||||
|
||||
// Slots 35-39: Etna txs.
|
||||
// Slot 35 was ConvertChainToL1Tx in v0 source; renamed to
|
||||
// ConvertNetworkToL1Tx in v1.27.x with identical wire layout.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.ConvertNetworkToL1Tx{}),
|
||||
c.RegisterType(&txs.RegisterL1ValidatorTx{}),
|
||||
c.RegisterType(&txs.SetL1ValidatorWeightTx{}),
|
||||
c.RegisterType(&txs.IncreaseL1ValidatorBalanceTx{}),
|
||||
c.RegisterType(&txs.DisableL1ValidatorTx{}),
|
||||
)
|
||||
|
||||
return errs.Err
|
||||
}
|
||||
|
||||
// RegisterTxTypes registers the v0 type IDs in the tx-only ordering
|
||||
// used by v1.23.x txs.init(). Apricot block slots (0-4) and Banff block
|
||||
// slots (29-32) are SkipRegistrations rather than RegisterType so that
|
||||
// shared tx slots line up with the block codec.
|
||||
func RegisterTxTypes(c linearcodec.Codec) error {
|
||||
errs := wrappers.Errs{}
|
||||
|
||||
// Slots 0-4: Apricot block slots reserved.
|
||||
c.SkipRegistrations(5)
|
||||
|
||||
// Slots 5-11: secp256k1fx (with two historical skips at 6 and 8).
|
||||
errs.Add(c.RegisterType(&secp256k1fx.TransferInput{}))
|
||||
c.SkipRegistrations(1)
|
||||
errs.Add(c.RegisterType(&secp256k1fx.TransferOutput{}))
|
||||
c.SkipRegistrations(1)
|
||||
errs.Add(
|
||||
c.RegisterType(&secp256k1fx.Credential{}),
|
||||
c.RegisterType(&secp256k1fx.Input{}),
|
||||
c.RegisterType(&secp256k1fx.OutputOwners{}),
|
||||
)
|
||||
|
||||
// Slots 12-22: Apricot txs + stakeable locks.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.AddValidatorTx{}),
|
||||
c.RegisterType(&txs.AddChainValidatorTx{}),
|
||||
c.RegisterType(&txs.AddDelegatorTx{}),
|
||||
c.RegisterType(&txs.CreateNetworkTx{}),
|
||||
c.RegisterType(&txs.CreateChainTx{}),
|
||||
c.RegisterType(&txs.ImportTx{}),
|
||||
c.RegisterType(&txs.ExportTx{}),
|
||||
c.RegisterType(&txs.AdvanceTimeTx{}),
|
||||
c.RegisterType(&txs.RewardValidatorTx{}),
|
||||
c.RegisterType(&stakeable.LockIn{}),
|
||||
c.RegisterType(&stakeable.LockOut{}),
|
||||
)
|
||||
|
||||
// Slots 23-28: Banff txs + signer.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.RemoveChainValidatorTx{}),
|
||||
c.RegisterType(&txs.TransformChainTx{}),
|
||||
c.RegisterType(&txs.AddPermissionlessValidatorTx{}),
|
||||
c.RegisterType(&txs.AddPermissionlessDelegatorTx{}),
|
||||
c.RegisterType(&signer.Empty{}),
|
||||
c.RegisterType(&signer.ProofOfPossession{}),
|
||||
)
|
||||
|
||||
// Slots 29-32: Banff block slots reserved.
|
||||
c.SkipRegistrations(4)
|
||||
|
||||
// Slots 33-39: Durango + Etna.
|
||||
errs.Add(
|
||||
c.RegisterType(&txs.TransferChainOwnershipTx{}),
|
||||
c.RegisterType(&txs.BaseTx{}),
|
||||
c.RegisterType(&txs.ConvertNetworkToL1Tx{}),
|
||||
c.RegisterType(&txs.RegisterL1ValidatorTx{}),
|
||||
c.RegisterType(&txs.SetL1ValidatorWeightTx{}),
|
||||
c.RegisterType(&txs.IncreaseL1ValidatorBalanceTx{}),
|
||||
c.RegisterType(&txs.DisableL1ValidatorTx{}),
|
||||
)
|
||||
|
||||
return errs.Err
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
// Block is the v0-decoder-side counterpart of block.Block. It is
|
||||
// satisfied by every concrete v0 block kind so that the v0 codec can
|
||||
// dispatch on an interface destination at unmarshal time. The parent
|
||||
// block package wraps the returned v0.Block in an adapter that
|
||||
// implements block.Block (which in turn dispatches to the canonical v1
|
||||
// Visitor).
|
||||
type Block interface {
|
||||
// ParentID is the BlockID of the parent block.
|
||||
ParentID() ids.ID
|
||||
|
||||
// Height is the block height. Genesis is at height 0.
|
||||
Height() uint64
|
||||
|
||||
// TimestampUnix returns the block-header timestamp encoded in this
|
||||
// block (Banff and later). Apricot blocks carry no header timestamp
|
||||
// and return 0; the parent timestamp is used in their stead by the
|
||||
// adapter.
|
||||
TimestampUnix() uint64
|
||||
|
||||
// Txs returns the txs carried by this block in their canonical
|
||||
// order. For ApricotProposalBlock the proposal Tx is the sole
|
||||
// element; for BanffProposalBlock the proposal Tx is the trailing
|
||||
// element (matching the v1.23.31 ProposalBlock.Txs() shape).
|
||||
Txs() []*txs.Tx
|
||||
}
|
||||
|
||||
// CommonBlock holds the parent-id + height fields shared across every
|
||||
// v0 block kind. Wire layout: PrntID (ids.ID, 32 bytes) followed by
|
||||
// Hght (uint64, 8 bytes). Byte-equal to v1.23.31 CommonBlock.
|
||||
type CommonBlock struct {
|
||||
PrntID ids.ID `serialize:"true" json:"parentID"`
|
||||
Hght uint64 `serialize:"true" json:"height"`
|
||||
}
|
||||
|
||||
// ApricotProposalBlock decodes slot 0. Carries no header timestamp;
|
||||
// pre-Banff time advances were carried in AdvanceTimeTx, not in the
|
||||
// block header.
|
||||
type ApricotProposalBlock struct {
|
||||
CommonBlock `serialize:"true"`
|
||||
Tx *txs.Tx `serialize:"true" json:"tx"`
|
||||
}
|
||||
|
||||
func (b *ApricotProposalBlock) ParentID() ids.ID { return b.PrntID }
|
||||
func (b *ApricotProposalBlock) Height() uint64 { return b.Hght }
|
||||
func (*ApricotProposalBlock) TimestampUnix() uint64 { return 0 }
|
||||
func (b *ApricotProposalBlock) Txs() []*txs.Tx { return []*txs.Tx{b.Tx} }
|
||||
|
||||
// ApricotAbortBlock decodes slot 1.
|
||||
type ApricotAbortBlock struct {
|
||||
CommonBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *ApricotAbortBlock) ParentID() ids.ID { return b.PrntID }
|
||||
func (b *ApricotAbortBlock) Height() uint64 { return b.Hght }
|
||||
func (*ApricotAbortBlock) TimestampUnix() uint64 { return 0 }
|
||||
func (*ApricotAbortBlock) Txs() []*txs.Tx { return nil }
|
||||
|
||||
// ApricotCommitBlock decodes slot 2.
|
||||
type ApricotCommitBlock struct {
|
||||
CommonBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *ApricotCommitBlock) ParentID() ids.ID { return b.PrntID }
|
||||
func (b *ApricotCommitBlock) Height() uint64 { return b.Hght }
|
||||
func (*ApricotCommitBlock) TimestampUnix() uint64 { return 0 }
|
||||
func (*ApricotCommitBlock) Txs() []*txs.Tx { return nil }
|
||||
|
||||
// ApricotStandardBlock decodes slot 3.
|
||||
type ApricotStandardBlock struct {
|
||||
CommonBlock `serialize:"true"`
|
||||
Transactions []*txs.Tx `serialize:"true" json:"txs"`
|
||||
}
|
||||
|
||||
func (b *ApricotStandardBlock) ParentID() ids.ID { return b.PrntID }
|
||||
func (b *ApricotStandardBlock) Height() uint64 { return b.Hght }
|
||||
func (*ApricotStandardBlock) TimestampUnix() uint64 { return 0 }
|
||||
func (b *ApricotStandardBlock) Txs() []*txs.Tx { return b.Transactions }
|
||||
|
||||
// ApricotAtomicBlock decodes slot 4. The atomic block flow is dead on
|
||||
// the modern P-only network — Apricot atomic blocks only ever appear
|
||||
// pre-Banff in legacy chain history.
|
||||
type ApricotAtomicBlock struct {
|
||||
CommonBlock `serialize:"true"`
|
||||
Tx *txs.Tx `serialize:"true" json:"tx"`
|
||||
}
|
||||
|
||||
func (b *ApricotAtomicBlock) ParentID() ids.ID { return b.PrntID }
|
||||
func (b *ApricotAtomicBlock) Height() uint64 { return b.Hght }
|
||||
func (*ApricotAtomicBlock) TimestampUnix() uint64 { return 0 }
|
||||
func (b *ApricotAtomicBlock) Txs() []*txs.Tx { return []*txs.Tx{b.Tx} }
|
||||
|
||||
// BanffProposalBlock decodes slot 29. Carries a Banff-era per-block
|
||||
// timestamp ahead of the embedded Apricot proposal block (which still
|
||||
// holds the proposal Tx). The on-wire field order is Time, then
|
||||
// Transactions (the decision-tx tail), then ApricotProposalBlock.
|
||||
type BanffProposalBlock struct {
|
||||
Time uint64 `serialize:"true" json:"time"`
|
||||
Transactions []*txs.Tx `serialize:"true" json:"txs"`
|
||||
ApricotProposalBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *BanffProposalBlock) TimestampUnix() uint64 { return b.Time }
|
||||
func (b *BanffProposalBlock) Txs() []*txs.Tx {
|
||||
out := make([]*txs.Tx, len(b.Transactions)+1)
|
||||
copy(out, b.Transactions)
|
||||
out[len(b.Transactions)] = b.ApricotProposalBlock.Tx
|
||||
return out
|
||||
}
|
||||
|
||||
// BanffAbortBlock decodes slot 30.
|
||||
type BanffAbortBlock struct {
|
||||
Time uint64 `serialize:"true" json:"time"`
|
||||
ApricotAbortBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *BanffAbortBlock) TimestampUnix() uint64 { return b.Time }
|
||||
|
||||
// BanffCommitBlock decodes slot 31.
|
||||
type BanffCommitBlock struct {
|
||||
Time uint64 `serialize:"true" json:"time"`
|
||||
ApricotCommitBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *BanffCommitBlock) TimestampUnix() uint64 { return b.Time }
|
||||
|
||||
// BanffStandardBlock decodes slot 32.
|
||||
type BanffStandardBlock struct {
|
||||
Time uint64 `serialize:"true" json:"time"`
|
||||
ApricotStandardBlock `serialize:"true"`
|
||||
}
|
||||
|
||||
func (b *BanffStandardBlock) TimestampUnix() uint64 { return b.Time }
|
||||
@@ -51,18 +51,39 @@ type Genesis struct {
|
||||
Message string `serialize:"true"`
|
||||
}
|
||||
|
||||
// Parse deserializes a P-Chain genesis blob produced by either the
|
||||
// v0 (v1.23.x) or v1 (current) codec. Wire version is taken from the
|
||||
// 2-byte codec prefix; the matching codec.Manager entry is used to
|
||||
// unmarshal the outer Genesis struct, and embedded validator + chain
|
||||
// txs are bound to their original signedBytes via InitializeFromBytes
|
||||
// (NOT Initialize, which would re-marshal and rotate the TxID).
|
||||
//
|
||||
// TxID stability across the v0->v1 migration is preserved because:
|
||||
// - v0 genesis blobs decode via the v0 slot map and embedded txs are
|
||||
// re-bound at version 0; their hash(signedBytes) is identical to
|
||||
// what the v1.23.x producer committed.
|
||||
// - v1 genesis blobs decode via the v1 slot map; embedded txs are
|
||||
// re-bound at version 1; bytes are deterministic so the result is
|
||||
// byte-equal to what a v1 producer emits.
|
||||
//
|
||||
// Genesis blobs are typically produced fresh from the JSON config at
|
||||
// each bootstrap (see New + Bytes), so the path that matters for
|
||||
// mainnet/testnet is the v0 fallback: the originally-committed genesis
|
||||
// hash is what subsequent block headers chain back to, and that hash
|
||||
// is hash(v0 genesis bytes).
|
||||
func Parse(genesisBytes []byte) (*Genesis, error) {
|
||||
gen := &Genesis{}
|
||||
if _, err := Codec.Unmarshal(genesisBytes, gen); err != nil {
|
||||
version, err := Codec.Unmarshal(genesisBytes, gen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, tx := range gen.Validators {
|
||||
if err := tx.Initialize(txs.GenesisCodec); err != nil {
|
||||
if err := tx.InitializeFromBytesAtVersion(txs.GenesisCodec, version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, tx := range gen.Chains {
|
||||
if err := tx.Initialize(txs.GenesisCodec); err != nil {
|
||||
if err := tx.InitializeFromBytesAtVersion(txs.GenesisCodec, version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func TestAddPermissionlessPrimaryDelegatorSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleAddPrimaryTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
|
||||
0x00, 0x00, 0x00, 0x1e,
|
||||
// Mainnet network ID
|
||||
@@ -389,7 +389,7 @@ func TestAddPermissionlessPrimaryDelegatorSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexAddPrimaryTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
|
||||
0x00, 0x00, 0x00, 0x1e,
|
||||
// Mainnet network ID
|
||||
@@ -880,7 +880,7 @@ func TestAddPermissionlessNetDelegatorSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleAddNetTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessDelegationTx type ID (post codec-collapse: 30)
|
||||
0x00, 0x00, 0x00, 0x1e,
|
||||
// Mainnet network ID
|
||||
@@ -1157,7 +1157,7 @@ func TestAddPermissionlessNetDelegatorSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexAddNetTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
|
||||
0x00, 0x00, 0x00, 0x1e,
|
||||
// Mainnet network ID
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestAddPermissionlessPrimaryValidator(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleAddPrimaryTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessValidatorTx type ID (post codec-collapse: 29)
|
||||
0x00, 0x00, 0x00, 0x1d,
|
||||
// Mainnet network ID
|
||||
@@ -451,7 +451,7 @@ func TestAddPermissionlessPrimaryValidator(t *testing.T) {
|
||||
|
||||
_ = []byte{ // expectedUnsignedComplexAddPrimaryTxBytes - not used since we skip exact byte comparison
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessValidatorTx type ID (post codec-collapse: 29)
|
||||
0x00, 0x00, 0x00, 0x1d,
|
||||
// Mainnet network ID
|
||||
@@ -842,7 +842,7 @@ func TestAddPermissionlessNetValidator(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleAddNetTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessValidatorTx type ID (post codec-collapse: 29)
|
||||
0x00, 0x00, 0x00, 0x1d,
|
||||
// Mainnet network ID
|
||||
@@ -1144,7 +1144,7 @@ func TestAddPermissionlessNetValidator(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexAddNetTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// AddPermissionlessValidatorTx type ID (post codec-collapse: 29)
|
||||
0x00, 0x00, 0x00, 0x1d,
|
||||
// Mainnet network ID
|
||||
|
||||
@@ -82,7 +82,7 @@ func TestBaseTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleBaseTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// BaseTx Type ID
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
// Mainnet network ID
|
||||
@@ -230,7 +230,7 @@ func TestBaseTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexBaseTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// BaseTx Type ID
|
||||
0x00, 0x00, 0x00, 0x22,
|
||||
// Mainnet network ID
|
||||
|
||||
+155
-21
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
@@ -10,54 +10,99 @@ import (
|
||||
"github.com/luxfi/codec"
|
||||
"github.com/luxfi/codec/linearcodec"
|
||||
"github.com/luxfi/codec/wrappers"
|
||||
"github.com/luxfi/node/vms/platformvm/stakeable"
|
||||
"github.com/luxfi/node/vms/platformvm/signer"
|
||||
"github.com/luxfi/node/vms/platformvm/stakeable"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
)
|
||||
|
||||
const (
|
||||
CodecVersion = 0
|
||||
Version = CodecVersion
|
||||
// CodecVersionV0 is the v1.23.x ("Apricot/Banff") wire layout. It is
|
||||
// retained as a READ-ONLY decoder so that pre-codec-v1 blocks and txs
|
||||
// on disk (mainnet, testnet) continue to deserialize. All write paths
|
||||
// MUST use CodecVersionV1.
|
||||
CodecVersionV0 uint16 = 0
|
||||
|
||||
// CodecVersionV1 is the current canonical wire layout used for every
|
||||
// new tx and every new block. It is the only version produced by the
|
||||
// build/sign paths.
|
||||
CodecVersionV1 uint16 = 1
|
||||
|
||||
// CodecVersion is the canonical write version. All Marshal call sites
|
||||
// in this package use CodecVersion so that any future bump of the
|
||||
// write target updates exactly one symbol.
|
||||
CodecVersion = CodecVersionV1
|
||||
|
||||
// Version is retained as a deprecated alias for code that referenced
|
||||
// the pre-multi-version constant.
|
||||
Version = CodecVersion
|
||||
)
|
||||
|
||||
var (
|
||||
// Codec is the standard-size multi-version codec used for normal txs.
|
||||
Codec codec.Manager
|
||||
|
||||
// GenesisCodec allows txs of larger than usual size to be parsed.
|
||||
// While this gives flexibility in accommodating large genesis txs
|
||||
// it must not be used to parse new, unverified txs which instead
|
||||
// must be processed by Codec.
|
||||
// It registers the same versioned slot layouts as Codec but with an
|
||||
// unbounded maximum size. New, unverified txs MUST be processed by
|
||||
// Codec; GenesisCodec is reserved for genesis decode + state read
|
||||
// fallback paths.
|
||||
GenesisCodec codec.Manager
|
||||
)
|
||||
|
||||
func init() {
|
||||
c := linearcodec.NewDefault()
|
||||
gc := linearcodec.NewDefault()
|
||||
cV0 := linearcodec.NewDefault()
|
||||
cV1 := linearcodec.NewDefault()
|
||||
gcV0 := linearcodec.NewDefault()
|
||||
gcV1 := linearcodec.NewDefault()
|
||||
|
||||
errs := wrappers.Errs{}
|
||||
for _, c := range []linearcodec.Codec{c, gc} {
|
||||
errs.Add(RegisterTypes(c))
|
||||
}
|
||||
errs.Add(
|
||||
registerV0TxTypes(cV0),
|
||||
registerV0TxTypes(gcV0),
|
||||
registerV1TxTypes(cV1),
|
||||
registerV1TxTypes(gcV1),
|
||||
)
|
||||
|
||||
Codec = codec.NewDefaultManager()
|
||||
GenesisCodec = codec.NewManager(math.MaxInt32)
|
||||
errs.Add(
|
||||
Codec.RegisterCodec(CodecVersion, c),
|
||||
GenesisCodec.RegisterCodec(CodecVersion, gc),
|
||||
Codec.RegisterCodec(CodecVersionV0, cV0),
|
||||
Codec.RegisterCodec(CodecVersionV1, cV1),
|
||||
GenesisCodec.RegisterCodec(CodecVersionV0, gcV0),
|
||||
GenesisCodec.RegisterCodec(CodecVersionV1, gcV1),
|
||||
)
|
||||
if errs.Errored() {
|
||||
panic(errs.Err)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTypes registers the canonical platformvm tx codec types in their
|
||||
// canonical order. The ordering is fixed (it determines on-disk type IDs);
|
||||
// new tx types append at the end. SkipRegistrations reserve slots for the
|
||||
// block-level types registered by the block codec so that codec IDs match
|
||||
// up across packages.
|
||||
// RegisterTypes registers the v1 tx-codec types (the only version a
|
||||
// new build path uses) on the given linearcodec. Existing call sites
|
||||
// outside this package continue to compose with RegisterTypes; the v0
|
||||
// decoder is gated to the txs.Codec / txs.GenesisCodec entries.
|
||||
func RegisterTypes(targetCodec linearcodec.Codec) error {
|
||||
// Reserve 5 slots for the four canonical block types + one historical slot
|
||||
// (atomic block ID) so existing tx type IDs remain stable.
|
||||
return registerV1TxTypes(targetCodec)
|
||||
}
|
||||
|
||||
// registerV1TxTypes registers the v1 (current) slot layout. Slot map:
|
||||
//
|
||||
// 0-4 reserved for block codec (Skip(5))
|
||||
// 5-11 secp256k1fx (TransferInput, MintOutput, TransferOutput,
|
||||
// MintOperation, Credential, Input, OutputOwners)
|
||||
// 12-20 Apricot txs
|
||||
// 21-22 stakeable.{LockIn, LockOut}
|
||||
// 23-26 reserved for block codec (Skip(4))
|
||||
// 27-30 Banff txs
|
||||
// 31-32 signer.{Empty, ProofOfPossession}
|
||||
// 33-34 Durango (TransferChainOwnershipTx, BaseTx)
|
||||
// 35-43 Etna + post-Etna (ConvertNetworkToL1Tx,
|
||||
// CreateSovereignL1Tx, RegisterL1ValidatorTx,
|
||||
// SetL1ValidatorWeightTx, IncreaseL1ValidatorBalanceTx,
|
||||
// DisableL1ValidatorTx, SlashValidatorTx, CreateAssetTx,
|
||||
// OperationTx)
|
||||
func registerV1TxTypes(targetCodec linearcodec.Codec) error {
|
||||
// Reserve 5 slots for the four canonical block types + one historical
|
||||
// slot (atomic block ID) so existing tx type IDs remain stable.
|
||||
targetCodec.SkipRegistrations(5)
|
||||
|
||||
errs := wrappers.Errs{}
|
||||
@@ -125,3 +170,92 @@ func RegisterTypes(targetCodec linearcodec.Codec) error {
|
||||
|
||||
return errors.Join(errs.Err)
|
||||
}
|
||||
|
||||
// registerV0TxTypes registers the v1.23.x ("Apricot/Banff") tx-codec
|
||||
// slot layout. Wire-format-identical to the layout produced by
|
||||
// luxfi/node@v1.23.31. v0 is registered as a READ-ONLY decoder; the
|
||||
// write path (Marshal at CodecVersionV0) is never invoked from inside
|
||||
// this package, and downstream code must not Marshal at CodecVersionV0.
|
||||
//
|
||||
// Slot map (tx-only codec):
|
||||
//
|
||||
// 0-4 reserved for block codec (Skip(5))
|
||||
// 5 TransferInput
|
||||
// 6 [skip — was secp256k1fx.MintOutput in XVM; never used on P]
|
||||
// 7 TransferOutput
|
||||
// 8 [skip — was secp256k1fx.MintOperation in XVM; never used on P]
|
||||
// 9-11 Credential, Input, OutputOwners
|
||||
// 12-20 Apricot txs
|
||||
// 21-22 stakeable.{LockIn, LockOut}
|
||||
// 23-26 Banff txs (RemoveChainValidator..AddPermissionlessDelegator)
|
||||
// 27-28 signer.{Empty, ProofOfPossession}
|
||||
// 29-32 reserved for Banff block slots (Skip(4))
|
||||
// 33-34 Durango (TransferChainOwnershipTx, BaseTx)
|
||||
// 35-39 Etna (ConvertChainToL1Tx [decoded as ConvertNetworkToL1Tx;
|
||||
// same struct layout, name-only change],
|
||||
// RegisterL1ValidatorTx, SetL1ValidatorWeightTx,
|
||||
// IncreaseL1ValidatorBalanceTx, DisableL1ValidatorTx)
|
||||
//
|
||||
// Pre-Etna v0 blobs that contain only slots 5..28 decode cleanly
|
||||
// without touching the post-Etna types.
|
||||
func registerV0TxTypes(targetCodec linearcodec.Codec) error {
|
||||
// Slots 0-4: reserved for block-codec block types.
|
||||
targetCodec.SkipRegistrations(5)
|
||||
|
||||
errs := wrappers.Errs{}
|
||||
|
||||
// Slots 5-11: secp256k1fx with the two v0 historical holes.
|
||||
errs.Add(targetCodec.RegisterType(&secp256k1fx.TransferInput{}))
|
||||
targetCodec.SkipRegistrations(1) // slot 6 hole.
|
||||
errs.Add(targetCodec.RegisterType(&secp256k1fx.TransferOutput{}))
|
||||
targetCodec.SkipRegistrations(1) // slot 8 hole.
|
||||
errs.Add(
|
||||
targetCodec.RegisterType(&secp256k1fx.Credential{}),
|
||||
targetCodec.RegisterType(&secp256k1fx.Input{}),
|
||||
targetCodec.RegisterType(&secp256k1fx.OutputOwners{}),
|
||||
)
|
||||
|
||||
// Slots 12-22: Apricot txs + stakeable locks.
|
||||
errs.Add(
|
||||
targetCodec.RegisterType(&AddValidatorTx{}),
|
||||
targetCodec.RegisterType(&AddChainValidatorTx{}),
|
||||
targetCodec.RegisterType(&AddDelegatorTx{}),
|
||||
targetCodec.RegisterType(&CreateNetworkTx{}),
|
||||
targetCodec.RegisterType(&CreateChainTx{}),
|
||||
targetCodec.RegisterType(&ImportTx{}),
|
||||
targetCodec.RegisterType(&ExportTx{}),
|
||||
targetCodec.RegisterType(&AdvanceTimeTx{}),
|
||||
targetCodec.RegisterType(&RewardValidatorTx{}),
|
||||
targetCodec.RegisterType(&stakeable.LockIn{}),
|
||||
targetCodec.RegisterType(&stakeable.LockOut{}),
|
||||
)
|
||||
|
||||
// Slots 23-28: Banff txs + signer.
|
||||
errs.Add(
|
||||
targetCodec.RegisterType(&RemoveChainValidatorTx{}),
|
||||
targetCodec.RegisterType(&TransformChainTx{}),
|
||||
targetCodec.RegisterType(&AddPermissionlessValidatorTx{}),
|
||||
targetCodec.RegisterType(&AddPermissionlessDelegatorTx{}),
|
||||
targetCodec.RegisterType(&signer.Empty{}),
|
||||
targetCodec.RegisterType(&signer.ProofOfPossession{}),
|
||||
)
|
||||
|
||||
// Slots 29-32: reserved for Banff block slots.
|
||||
targetCodec.SkipRegistrations(4)
|
||||
|
||||
// Slots 33-39: Durango + Etna. Slot 35 in v0 was named
|
||||
// ConvertChainToL1Tx; its struct layout matches the post-rename
|
||||
// ConvertNetworkToL1Tx byte-for-byte, so the same Go type is
|
||||
// registered at both v0 slot 35 and v1 slot 35.
|
||||
errs.Add(
|
||||
targetCodec.RegisterType(&TransferChainOwnershipTx{}),
|
||||
targetCodec.RegisterType(&BaseTx{}),
|
||||
targetCodec.RegisterType(&ConvertNetworkToL1Tx{}),
|
||||
targetCodec.RegisterType(&RegisterL1ValidatorTx{}),
|
||||
targetCodec.RegisterType(&SetL1ValidatorWeightTx{}),
|
||||
targetCodec.RegisterType(&IncreaseL1ValidatorBalanceTx{}),
|
||||
targetCodec.RegisterType(&DisableL1ValidatorTx{}),
|
||||
)
|
||||
|
||||
return errors.Join(errs.Err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package txs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
hash "github.com/luxfi/crypto/hash"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// fixtureUnsigned returns a small, deterministic UnsignedTx suitable
|
||||
// for cross-version round-trip tests. AdvanceTimeTx is chosen because
|
||||
// its slot ID (19) is identical under v0 and v1 — so identical input
|
||||
// fields produce structurally similar bytes under both layouts. The
|
||||
// only delta is the leading 2-byte version prefix.
|
||||
func fixtureUnsigned() *AdvanceTimeTx {
|
||||
return &AdvanceTimeTx{Time: 1_700_000_000}
|
||||
}
|
||||
|
||||
// fixtureSignedTx wraps fixtureUnsigned in a *Tx with an empty creds
|
||||
// list. Empty creds keep the bytes minimal and let us isolate the
|
||||
// codec-version prefix as the source of TxID divergence.
|
||||
func fixtureSignedTx(t *testing.T) *Tx {
|
||||
t.Helper()
|
||||
return &Tx{Unsigned: fixtureUnsigned()}
|
||||
}
|
||||
|
||||
// TestCodecVersionV0V1Coexist asserts that the v0 and v1 codec entries
|
||||
// are both registered on Codec and that each can round-trip the
|
||||
// AdvanceTimeTx fixture (whose slot is stable across the migration).
|
||||
func TestCodecVersionV0V1Coexist(t *testing.T) {
|
||||
require := require.New(t)
|
||||
tx := fixtureSignedTx(t)
|
||||
|
||||
v0Bytes, err := Codec.Marshal(CodecVersionV0, tx)
|
||||
require.NoError(err, "v0 marshal")
|
||||
v1Bytes, err := Codec.Marshal(CodecVersionV1, tx)
|
||||
require.NoError(err, "v1 marshal")
|
||||
|
||||
// First 2 bytes carry the version prefix.
|
||||
require.Equal(byte(0x00), v0Bytes[0])
|
||||
require.Equal(byte(0x00), v0Bytes[1])
|
||||
require.Equal(byte(0x00), v1Bytes[0])
|
||||
require.Equal(byte(0x01), v1Bytes[1])
|
||||
|
||||
// The post-prefix payload must be identical for AdvanceTimeTx —
|
||||
// slot 19 is the same in both layouts. This is the structural
|
||||
// invariant we rely on for the Apricot-tx subset of v0 reads.
|
||||
require.True(bytes.Equal(v0Bytes[2:], v1Bytes[2:]),
|
||||
"AdvanceTimeTx body differs across versions; slot 19 is supposed to be stable")
|
||||
}
|
||||
|
||||
// TestParseDispatchesByVersion exercises the version-aware
|
||||
// Parse path with a trivial AdvanceTimeTx (slot 19 stable). The
|
||||
// signature-of-correctness here is that BOTH v0 bytes AND v1 bytes
|
||||
// parse cleanly via txs.Parse and produce txs whose TxID is stable
|
||||
// under their own version.
|
||||
func TestParseDispatchesByVersion(t *testing.T) {
|
||||
require := require.New(t)
|
||||
tx := fixtureSignedTx(t)
|
||||
|
||||
v0Bytes, err := Codec.Marshal(CodecVersionV0, tx)
|
||||
require.NoError(err)
|
||||
v1Bytes, err := Codec.Marshal(CodecVersionV1, tx)
|
||||
require.NoError(err)
|
||||
|
||||
v0Parsed, err := Parse(Codec, v0Bytes)
|
||||
require.NoError(err, "v0 parse")
|
||||
v1Parsed, err := Parse(Codec, v1Bytes)
|
||||
require.NoError(err, "v1 parse")
|
||||
|
||||
// TxID stability: each parsed tx's TxID must equal the hash of its
|
||||
// OWN signedBytes — never re-marshaled at the other version. This
|
||||
// is the byte-preserving invariant.
|
||||
require.Equal(ids.ID(hash.ComputeHash256Array(v0Bytes)), v0Parsed.TxID,
|
||||
"v0 TxID = hash(v0Bytes) — byte-preserving")
|
||||
require.Equal(ids.ID(hash.ComputeHash256Array(v1Bytes)), v1Parsed.TxID,
|
||||
"v1 TxID = hash(v1Bytes) — byte-preserving")
|
||||
|
||||
// v0 and v1 TxIDs MUST differ (the bytes differ in the version
|
||||
// prefix). This is the cross-version non-collision check.
|
||||
require.NotEqual(v0Parsed.TxID, v1Parsed.TxID,
|
||||
"v0 and v1 wire-distinct txs must produce distinct TxIDs")
|
||||
|
||||
// Bytes are preserved verbatim.
|
||||
require.True(bytes.Equal(v0Bytes, v0Parsed.Bytes()),
|
||||
"v0 parsed bytes are the original input")
|
||||
require.True(bytes.Equal(v1Bytes, v1Parsed.Bytes()),
|
||||
"v1 parsed bytes are the original input")
|
||||
}
|
||||
|
||||
// TestTxIDStabilityRoundTrip asserts that the byte-preserving Parse
|
||||
// path is idempotent: re-marshal of the parsed tx under its OWN
|
||||
// version produces byte-equal output, so the TxID does not rotate
|
||||
// across Parse -> Marshal cycles. This is what guarantees that
|
||||
// re-broadcasting a tx loaded from disk keeps its TxID stable.
|
||||
func TestTxIDStabilityRoundTrip(t *testing.T) {
|
||||
require := require.New(t)
|
||||
tx := fixtureSignedTx(t)
|
||||
|
||||
v0Bytes, err := Codec.Marshal(CodecVersionV0, tx)
|
||||
require.NoError(err)
|
||||
v0Parsed, err := Parse(Codec, v0Bytes)
|
||||
require.NoError(err)
|
||||
|
||||
// Re-marshal v0Parsed at v0 — bytes must round-trip exactly.
|
||||
rebuilt, err := Codec.Marshal(CodecVersionV0, v0Parsed)
|
||||
require.NoError(err)
|
||||
require.True(bytes.Equal(v0Bytes, rebuilt),
|
||||
"v0 round-trip: Marshal(Parse(b)) == b")
|
||||
require.Equal(v0Parsed.TxID, ids.ID(hash.ComputeHash256Array(rebuilt)),
|
||||
"v0 TxID stable across Parse -> Marshal")
|
||||
|
||||
// Same for v1.
|
||||
v1Bytes, err := Codec.Marshal(CodecVersionV1, tx)
|
||||
require.NoError(err)
|
||||
v1Parsed, err := Parse(Codec, v1Bytes)
|
||||
require.NoError(err)
|
||||
rebuilt, err = Codec.Marshal(CodecVersionV1, v1Parsed)
|
||||
require.NoError(err)
|
||||
require.True(bytes.Equal(v1Bytes, rebuilt),
|
||||
"v1 round-trip: Marshal(Parse(b)) == b")
|
||||
require.Equal(v1Parsed.TxID, ids.ID(hash.ComputeHash256Array(rebuilt)),
|
||||
"v1 TxID stable across Parse -> Marshal")
|
||||
}
|
||||
|
||||
// TestCrossVersionRefuses asserts that bytes produced at one version
|
||||
// CANNOT be decoded at the other version without going through the
|
||||
// dispatcher. Specifically: v0 bytes' slot map differs from v1 for
|
||||
// post-Apricot types; if we manually swap the version prefix from
|
||||
// 0->1 (or 1->0) without re-encoding, the slot lookup will read the
|
||||
// wrong type and either fail or produce a different TxID. This is
|
||||
// the property the dispatcher protects against.
|
||||
//
|
||||
// For AdvanceTimeTx (slot 19 stable), the post-prefix bytes ARE the
|
||||
// same, so a manual prefix swap would silently succeed but produce a
|
||||
// different TxID (because the prefix bytes are part of the hash). We
|
||||
// assert that mismatch here.
|
||||
func TestCrossVersionRefuses(t *testing.T) {
|
||||
require := require.New(t)
|
||||
tx := fixtureSignedTx(t)
|
||||
v0Bytes, err := Codec.Marshal(CodecVersionV0, tx)
|
||||
require.NoError(err)
|
||||
|
||||
// Build a Frankenbytes: v1 prefix + v0 payload.
|
||||
frank := make([]byte, len(v0Bytes))
|
||||
copy(frank, v0Bytes)
|
||||
frank[0], frank[1] = 0x00, 0x01
|
||||
|
||||
// Parse must NOT yield a tx whose TxID equals the v0 tx's TxID —
|
||||
// the version prefix is part of hash input. AdvanceTimeTx is one
|
||||
// of the stable-slot types so the parse itself may succeed under
|
||||
// v1 dispatcher; the test point is that the TxID differs.
|
||||
frankParsed, err := Parse(Codec, frank)
|
||||
require.NoError(err, "AdvanceTimeTx is slot-stable; v1 parse should succeed structurally")
|
||||
v0Parsed, err := Parse(Codec, v0Bytes)
|
||||
require.NoError(err)
|
||||
require.NotEqual(v0Parsed.TxID, frankParsed.TxID,
|
||||
"manually-relabeled v0 bytes must NOT produce the v0 TxID")
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestConvertNetworkToL1TxSerialization(t *testing.T) {
|
||||
},
|
||||
expectedBytes: []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// ConvertNetworkToL1Tx Type ID
|
||||
0x00, 0x00, 0x00, 0x23,
|
||||
// Mainnet Network ID
|
||||
@@ -324,7 +324,7 @@ func TestConvertNetworkToL1TxSerialization(t *testing.T) {
|
||||
},
|
||||
expectedBytes: []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// ConvertNetworkToL1Tx Type ID
|
||||
0x00, 0x00, 0x00, 0x23,
|
||||
// Mainnet Network ID
|
||||
|
||||
@@ -163,9 +163,9 @@ func TestDisableL1ValidatorTxSerialization(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// DisableL1ValidatorTx Type ID
|
||||
0x00, 0x00, 0x00, 0x27,
|
||||
0x00, 0x00, 0x00, 0x28,
|
||||
// Network ID
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// P-chain blockchain ID (31 zeros + 'P')
|
||||
|
||||
@@ -31,33 +31,33 @@ var (
|
||||
}{
|
||||
{
|
||||
name: "AdvanceTimeTx",
|
||||
tx: "0000000000130000000066a56fe700000000",
|
||||
tx: "0001000000130000000066a56fe700000000",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexityErr: ErrUnsupportedTx,
|
||||
expectedDynamicFeeErr: ErrUnsupportedTx,
|
||||
},
|
||||
{
|
||||
name: "RewardValidatorTx",
|
||||
tx: "0000000000143d0ad12b8ee8928edf248ca91ca55600fb383f07c32bff1d6dec472b25cf59a700000000",
|
||||
tx: "0001000000143d0ad12b8ee8928edf248ca91ca55600fb383f07c32bff1d6dec472b25cf59a700000000",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexityErr: ErrUnsupportedTx,
|
||||
expectedDynamicFeeErr: ErrUnsupportedTx,
|
||||
},
|
||||
{
|
||||
name: "AddValidatorTx",
|
||||
tx: "00000000000c0000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000f4b21e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000015000000006134088000000005000001d1a94a200000000001000000000000000400000000b3da694c70b8bee4478051313621c3f2282088b4000000005f6976d500000000614aaa19000001d1a94a20000000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000016000000006134088000000007000001d1a94a20000000000000000000000000010000000120868ed5ac611711b33d2e4f97085347415db1c40000000b0000000000000000000000010000000120868ed5ac611711b33d2e4f97085347415db1c400009c40000000010000000900000001620513952dd17c8726d52e9e621618cb38f09fd194abb4cd7b4ee35ecd10880a562ad968dc81a89beab4e87d88d5d582aa73d0d265c87892d1ffff1f6e00f0ef00",
|
||||
tx: "00010000000c0000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000f4b21e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000015000000006134088000000005000001d1a94a200000000001000000000000000400000000b3da694c70b8bee4478051313621c3f2282088b4000000005f6976d500000000614aaa19000001d1a94a20000000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000016000000006134088000000007000001d1a94a20000000000000000000000000010000000120868ed5ac611711b33d2e4f97085347415db1c40000000b0000000000000000000000010000000120868ed5ac611711b33d2e4f97085347415db1c400009c40000000010000000900000001620513952dd17c8726d52e9e621618cb38f09fd194abb4cd7b4ee35ecd10880a562ad968dc81a89beab4e87d88d5d582aa73d0d265c87892d1ffff1f6e00f0ef00",
|
||||
expectedComplexityErr: ErrUnsupportedTx,
|
||||
expectedDynamicFeeErr: ErrUnsupportedTx,
|
||||
},
|
||||
{
|
||||
name: "AddDelegatorTx",
|
||||
tx: "00000000000e000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000100000001f887b4c7030e95d2495603ae5d8b14cc0a66781a000000011767be999a49ca24fe705de032fa613b682493110fd6468ae7fb56bde1b9d729000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000012a05f20000000001000000000000000400000000c51c552c49174e2e18b392049d3e4cd48b11490f000000005f692452000000005f73b05200000000ee6b2800000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000ee6b280000000000000000000000000100000001e0cfe8cae22827d032805ded484e393ce51cbedb0000000b00000000000000000000000100000001e0cfe8cae22827d032805ded484e393ce51cbedb00000001000000090000000135cd78758035ed528d230317e5d880083a86a2b68c4a95655571828fe226548f235031c8dabd1fe06366a57613c4370ac26c4c59d1a1c46287a59906ec41b88f00",
|
||||
tx: "00010000000e000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000100000001f887b4c7030e95d2495603ae5d8b14cc0a66781a000000011767be999a49ca24fe705de032fa613b682493110fd6468ae7fb56bde1b9d729000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000012a05f20000000001000000000000000400000000c51c552c49174e2e18b392049d3e4cd48b11490f000000005f692452000000005f73b05200000000ee6b2800000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000ee6b280000000000000000000000000100000001e0cfe8cae22827d032805ded484e393ce51cbedb0000000b00000000000000000000000100000001e0cfe8cae22827d032805ded484e393ce51cbedb00000001000000090000000135cd78758035ed528d230317e5d880083a86a2b68c4a95655571828fe226548f235031c8dabd1fe06366a57613c4370ac26c4c59d1a1c46287a59906ec41b88f00",
|
||||
expectedComplexityErr: ErrUnsupportedTx,
|
||||
expectedDynamicFeeErr: ErrUnsupportedTx,
|
||||
},
|
||||
{
|
||||
name: "AddPermissionlessValidatorTx for primary network",
|
||||
tx: "00000000001d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700238520ba8b1e00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001043c91e9d508169329034e2a68110427a311f945efc53ed3f3493d335b393fd100000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f263d53e00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae35f0000000066b692df000001d1a94a2000000000000000000000000000000000000000000000000000000000000000000000000020a3783a891cb41cadbfcf456da149f30e7af972677a162b984bef0779f254baac51ec042df1781d1295df80fb41c801269731fc6c25e1e5940dc3cb8509e30348fa712742cfdc83678acc9f95908eb98b89b28802fb559b4a2a6ff3216707c07f0ceb0b45a95f4f9a9540bbd3331d8ab4f233bffa4abb97fad9d59a1695f31b92a2b89e365facf7ab8c30de7c4a496d1e00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000001d1a94a2000000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0007a12000000001000000090000000135f122f90bcece0d6c43e07fed1829578a23bc1734f8a4b46203f9f192ea1aec7526f3dca8fddec7418988615e6543012452bae1544275aae435313ec006ec9000",
|
||||
tx: "00010000001d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700238520ba8b1e00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001043c91e9d508169329034e2a68110427a311f945efc53ed3f3493d335b393fd100000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f263d53e00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae35f0000000066b692df000001d1a94a2000000000000000000000000000000000000000000000000000000000000000000000000020a3783a891cb41cadbfcf456da149f30e7af972677a162b984bef0779f254baac51ec042df1781d1295df80fb41c801269731fc6c25e1e5940dc3cb8509e30348fa712742cfdc83678acc9f95908eb98b89b28802fb559b4a2a6ff3216707c07f0ceb0b45a95f4f9a9540bbd3331d8ab4f233bffa4abb97fad9d59a1695f31b92a2b89e365facf7ab8c30de7c4a496d1e00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000001d1a94a2000000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0007a12000000001000000090000000135f122f90bcece0d6c43e07fed1829578a23bc1734f8a4b46203f9f192ea1aec7526f3dca8fddec7418988615e6543012452bae1544275aae435313ec006ec9000",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 691, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicAddPermissionlessValidatorTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -68,7 +68,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "AddPermissionlessValidatorTx for chain",
|
||||
tx: "00000000001d000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000070000000000006091000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700238520ba6c9980000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000002038b42b73d3dc695c76ca12f966e97fe0681b1200f9a5e28d088720a18ea23c9000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000609b0000000100000000a378b74b3293a9d885bd9961f2cc2e1b3364d393c9be875964f2bd614214572c00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500238520ba7bdbc0000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa0000000066a57a160000000066b7ef16000000000000000a97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000001f000000012f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000000a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000000000000000000000b00000000000000000000000000000000000f4240000000020000000900000001593fc20f88a8ce0b3470b0bb103e5f7e09f65023b6515d36660da53f9a15dedc1037ee27a8c4a27c24e20ad3b0ab4bd1ff3a02a6fcc2cbe04282bfe9902c9ae6000000000900000001593fc20f88a8ce0b3470b0bb103e5f7e09f65023b6515d36660da53f9a15dedc1037ee27a8c4a27c24e20ad3b0ab4bd1ff3a02a6fcc2cbe04282bfe9902c9ae600",
|
||||
tx: "00010000001d000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000070000000000006091000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700238520ba6c9980000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000002038b42b73d3dc695c76ca12f966e97fe0681b1200f9a5e28d088720a18ea23c9000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000609b0000000100000000a378b74b3293a9d885bd9961f2cc2e1b3364d393c9be875964f2bd614214572c00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500238520ba7bdbc0000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa0000000066a57a160000000066b7ef16000000000000000a97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000001f000000012f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000000a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000000000000000000000b00000000000000000000000000000000000f4240000000020000000900000001593fc20f88a8ce0b3470b0bb103e5f7e09f65023b6515d36660da53f9a15dedc1037ee27a8c4a27c24e20ad3b0ab4bd1ff3a02a6fcc2cbe04282bfe9902c9ae6000000000900000001593fc20f88a8ce0b3470b0bb103e5f7e09f65023b6515d36660da53f9a15dedc1037ee27a8c4a27c24e20ad3b0ab4bd1ff3a02a6fcc2cbe04282bfe9902c9ae600",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 748, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicAddPermissionlessValidatorTxComplexities[gas.DBRead] + 2*intrinsicInputDBRead,
|
||||
@@ -79,7 +79,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "AddPermissionlessDelegatorTx for primary network",
|
||||
tx: "00000000001e00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834f1140fe00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000017d199179744b3b82d0071c83c2fb7dd6b95a2cdbe9dde295e0ae4f8c2287370300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500238520ba8b1e00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae6080000000066ad5b08000001d1a94a2000000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000001d1a94a2000000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000100000009000000012261556f74a29f02ffc2725a567db2c81f75d0892525dbebaa1cf8650534cc70061123533a9553184cb02d899943ff0bf0b39c77b173c133854bc7c8bc7ab9a400",
|
||||
tx: "00010000001e00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834f1140fe00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000017d199179744b3b82d0071c83c2fb7dd6b95a2cdbe9dde295e0ae4f8c2287370300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500238520ba8b1e00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae6080000000066ad5b08000001d1a94a2000000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000001d1a94a2000000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000100000009000000012261556f74a29f02ffc2725a567db2c81f75d0892525dbebaa1cf8650534cc70061123533a9553184cb02d899943ff0bf0b39c77b173c133854bc7c8bc7ab9a400",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 499, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicAddPermissionlessDelegatorTxComplexities[gas.DBRead] + 1*intrinsicInputDBRead,
|
||||
@@ -90,7 +90,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "AddPermissionlessDelegatorTx for chain",
|
||||
tx: "00000000001e000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000070000000000006087000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700470c1336195b80000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000029494c80361884942e4292c3531e8e790fcf7561e74404ded27eab8634e3fb30f000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000609100000001000000009494c80361884942e4292c3531e8e790fcf7561e74404ded27eab8634e3fb30f00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500470c1336289dc0000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa0000000066a57c1d0000000066b7f11d000000000000000a97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c124000000012f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000000a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b00000000000000000000000000000000000000020000000900000001764190e2405fef72fce0d355e3dcc58a9f5621e583ae718cb2c23b55957995d1206d0b5efcc3cef99815e17a4b2cccd700147a759b7279a131745b237659666a000000000900000001764190e2405fef72fce0d355e3dcc58a9f5621e583ae718cb2c23b55957995d1206d0b5efcc3cef99815e17a4b2cccd700147a759b7279a131745b237659666a00",
|
||||
tx: "00010000001e000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000070000000000006087000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700470c1336195b80000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000029494c80361884942e4292c3531e8e790fcf7561e74404ded27eab8634e3fb30f000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000609100000001000000009494c80361884942e4292c3531e8e790fcf7561e74404ded27eab8634e3fb30f00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500470c1336289dc0000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa0000000066a57c1d0000000066b7f11d000000000000000a97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c124000000012f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000000a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000b00000000000000000000000000000000000000020000000900000001764190e2405fef72fce0d355e3dcc58a9f5621e583ae718cb2c23b55957995d1206d0b5efcc3cef99815e17a4b2cccd700147a759b7279a131745b237659666a000000000900000001764190e2405fef72fce0d355e3dcc58a9f5621e583ae718cb2c23b55957995d1206d0b5efcc3cef99815e17a4b2cccd700147a759b7279a131745b237659666a00",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 720, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicAddPermissionlessDelegatorTxComplexities[gas.DBRead] + 2*intrinsicInputDBRead,
|
||||
@@ -101,7 +101,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "AddChainValidatorTx",
|
||||
tx: "00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834f1131bbc0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000138f94d1a0514eaabdaf4c52cad8d62b26cee61eaa951f5b75a5e57c2ee3793c800000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834f1140fe00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae7c90000000066ad5cc9000000000000c13797ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a00000001000000000000000200000009000000012127130d37877fb1ec4b2374ef72571d49cd7b0319a3769e5da19041a138166c10b1a5c07cf5ccf0419066cbe3bab9827cf29f9fa6213ebdadf19d4849501eb60000000009000000012127130d37877fb1ec4b2374ef72571d49cd7b0319a3769e5da19041a138166c10b1a5c07cf5ccf0419066cbe3bab9827cf29f9fa6213ebdadf19d4849501eb600",
|
||||
tx: "00010000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834f1131bbc0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000138f94d1a0514eaabdaf4c52cad8d62b26cee61eaa951f5b75a5e57c2ee3793c800000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834f1140fe00000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa00000000669ae7c90000000066ad5cc9000000000000c13797ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a00000001000000000000000200000009000000012127130d37877fb1ec4b2374ef72571d49cd7b0319a3769e5da19041a138166c10b1a5c07cf5ccf0419066cbe3bab9827cf29f9fa6213ebdadf19d4849501eb60000000009000000012127130d37877fb1ec4b2374ef72571d49cd7b0319a3769e5da19041a138166c10b1a5c07cf5ccf0419066cbe3bab9827cf29f9fa6213ebdadf19d4849501eb600",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 460, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicAddChainValidatorTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -112,7 +112,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "BaseTx",
|
||||
tx: "00000000002200003039000000000000000000000000000000000000000000000000000000000000000000000002dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b9aca00000000000000000100000002000000024a177205df5c29929d06db9d941f83d5ea985de3e902a9a86640bfdb1cd0e36c0cc982b83e5765fadbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834ed587af80000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001fa4ff39749d44f29563ed9da03193d4a19ef419da4ce326594817ca266fda5ed00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834f1131bbc00000000100000000000000000000000100000009000000014a7b54c63dd25a532b5fe5045b6d0e1db876e067422f12c9c327333c2c792d9273405ac8bbbc2cce549bbd3d0f9274242085ee257adfdb859b0f8d55bdd16fb000",
|
||||
tx: "00010000002200003039000000000000000000000000000000000000000000000000000000000000000000000002dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b9aca00000000000000000100000002000000024a177205df5c29929d06db9d941f83d5ea985de3e902a9a86640bfdb1cd0e36c0cc982b83e5765fadbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834ed587af80000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001fa4ff39749d44f29563ed9da03193d4a19ef419da4ce326594817ca266fda5ed00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834f1131bbc00000000100000000000000000000000100000009000000014a7b54c63dd25a532b5fe5045b6d0e1db876e067422f12c9c327333c2c792d9273405ac8bbbc2cce549bbd3d0f9274242085ee257adfdb859b0f8d55bdd16fb000",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 399, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicBaseTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -123,7 +123,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "CreateChainTx with genesis",
|
||||
tx: "00000000001000003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f263d53e00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000197ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c12400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f269cb1f0000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c12400096c65742074686572657873766d00000000000000000000000000000000000000000000000000000000000000000000002a000000000000669ae21e000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cffffffffffffffff0000000a0000000100000000000000020000000900000001cf8104877b1a59b472f4f34d360c0e4f38e92c5fa334215430d0b99cf78eae8f621b6daf0b0f5c3a58a9497601f978698a1e5545d1873db8f2f38ecb7496c2f8010000000900000001cf8104877b1a59b472f4f34d360c0e4f38e92c5fa334215430d0b99cf78eae8f621b6daf0b0f5c3a58a9497601f978698a1e5545d1873db8f2f38ecb7496c2f801",
|
||||
tx: "00010000001000003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f263d53e00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000197ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c12400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f269cb1f0000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c12400096c65742074686572657873766d00000000000000000000000000000000000000000000000000000000000000000000002a000000000000669ae21e000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cffffffffffffffff0000000a0000000100000000000000020000000900000001cf8104877b1a59b472f4f34d360c0e4f38e92c5fa334215430d0b99cf78eae8f621b6daf0b0f5c3a58a9497601f978698a1e5545d1873db8f2f38ecb7496c2f8010000000900000001cf8104877b1a59b472f4f34d360c0e4f38e92c5fa334215430d0b99cf78eae8f621b6daf0b0f5c3a58a9497601f978698a1e5545d1873db8f2f38ecb7496c2f801",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 509, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicCreateChainTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -134,7 +134,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "CreateNetworkTx",
|
||||
tx: "00000000000f00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f269cb1f00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc100000000000100000000000000000000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000010000000900000001b3c905e7227e619bd6b98c164a8b2b4a8ce89ac5142bbb1c42b139df2d17fd777c4c76eae66cef3de90800e567407945f58d918978f734f8ca4eda6923c78eb201",
|
||||
tx: "00010000000f00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f269cb1f00000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc100000000000100000000000000000000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000010000000900000001b3c905e7227e619bd6b98c164a8b2b4a8ce89ac5142bbb1c42b139df2d17fd777c4c76eae66cef3de90800e567407945f58d918978f734f8ca4eda6923c78eb201",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 339, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicCreateNetworkTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -145,7 +145,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "ExportTx",
|
||||
tx: "00000000001200003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99dda340000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001f62c03574790b6a31a988f90c3e91c50fdd6f5d93baf200057463021ff23ec5c00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834ed587af800000000100000000000000009d0775f450604bd2fbc49ce0c5c1c6dfeb2dc2acb8c92c26eeae6e6df4502b1900000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b9aca00000000000000000100000002000000024a177205df5c29929d06db9d941f83d5ea985de3e902a9a86640bfdb1cd0e36c0cc982b83e5765fa000000010000000900000001129a07c92045e0b9d0a203fcb5b53db7890fabce1397ff6a2ad16c98ef0151891ae72949d240122abf37b1206b95e05ff171df164a98e6bdf2384432eac2c30200",
|
||||
tx: "00010000001200003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99dda340000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001f62c03574790b6a31a988f90c3e91c50fdd6f5d93baf200057463021ff23ec5c00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834ed587af800000000100000000000000009d0775f450604bd2fbc49ce0c5c1c6dfeb2dc2acb8c92c26eeae6e6df4502b1900000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b9aca00000000000000000100000002000000024a177205df5c29929d06db9d941f83d5ea985de3e902a9a86640bfdb1cd0e36c0cc982b83e5765fa000000010000000900000001129a07c92045e0b9d0a203fcb5b53db7890fabce1397ff6a2ad16c98ef0151891ae72949d240122abf37b1206b95e05ff171df164a98e6bdf2384432eac2c30200",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 435, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicExportTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -156,7 +156,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "ImportTx",
|
||||
tx: "00000000001100003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b8b87c0000000000000000100000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000000000000d891ad56056d9c01f18f43f58b5c784ad07a4a49cf3d1f11623804b5cba2c6bf0000000163684415710a7d65f4ccb095edff59f897106b94d38937fc60e3ffc29892833b00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005000000003b9aca00000000010000000000000001000000090000000148ea12cb0950e47d852b99765208f5a811d3c8a47fa7b23fd524bd970019d157029f973abb91c31a146752ef8178434deb331db24c8dca5e61c961e6ac2f3b6700",
|
||||
tx: "00010000001100003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007000000003b8b87c0000000000000000100000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000000000000d891ad56056d9c01f18f43f58b5c784ad07a4a49cf3d1f11623804b5cba2c6bf0000000163684415710a7d65f4ccb095edff59f897106b94d38937fc60e3ffc29892833b00000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005000000003b9aca00000000010000000000000001000000090000000148ea12cb0950e47d852b99765208f5a811d3c8a47fa7b23fd524bd970019d157029f973abb91c31a146752ef8178434deb331db24c8dca5e61c961e6ac2f3b6700",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 335, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicImportTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -167,7 +167,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "RemoveChainValidatorTx",
|
||||
tx: "00000000001b00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99ce6100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001cd4569cfd044d50636fa597c700710403b3b52d3b75c30c542a111cc52c911ec00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834e99dda340000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a0000000100000000000000020000000900000001673ee3e5a3a1221935274e8ff5c45b27ebe570e9731948e393a8ebef6a15391c189a54de7d2396095492ae171103cd4bfccfc2a4dafa001d48c130694c105c2d010000000900000001673ee3e5a3a1221935274e8ff5c45b27ebe570e9731948e393a8ebef6a15391c189a54de7d2396095492ae171103cd4bfccfc2a4dafa001d48c130694c105c2d01",
|
||||
tx: "00010000001b00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99ce6100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001cd4569cfd044d50636fa597c700710403b3b52d3b75c30c542a111cc52c911ec00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834e99dda340000000010000000000000000c582872c37c81efa2c94ea347af49cdc23a830aa97ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a0000000100000000000000020000000900000001673ee3e5a3a1221935274e8ff5c45b27ebe570e9731948e393a8ebef6a15391c189a54de7d2396095492ae171103cd4bfccfc2a4dafa001d48c130694c105c2d010000000900000001673ee3e5a3a1221935274e8ff5c45b27ebe570e9731948e393a8ebef6a15391c189a54de7d2396095492ae171103cd4bfccfc2a4dafa001d48c130694c105c2d01",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 436, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicRemoveChainValidatorTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -178,13 +178,13 @@ var (
|
||||
},
|
||||
{
|
||||
name: "TransformChainTx",
|
||||
tx: "00000000001c000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000609b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f263c5fbc0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000294a113f31a30ee643288277574434f9066e0cdc1d53d6eb2610805c388814134000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000c137000000010000000094a113f31a30ee643288277574434f9066e0cdc1d53d6eb2610805c38881413400000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f269bbdcc000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1242f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000000000609b000000000000c1370000000000000001000000000000000a0000000000000001000000000000006400127500001fa40000000001000000000000000a64000000010000000a00000001000000000000000300000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca820000000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca820000000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca8200",
|
||||
tx: "00010000001c000030390000000000000000000000000000000000000000000000000000000000000000000000022f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000007000000000000609b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29cdbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f263c5fbc0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c0000000294a113f31a30ee643288277574434f9066e0cdc1d53d6eb2610805c388814134000000002f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a00000005000000000000c137000000010000000094a113f31a30ee643288277574434f9066e0cdc1d53d6eb2610805c38881413400000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f269bbdcc000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1242f6399f3e626fe1e75f9daa5e726cb64b7bfec0b6e6d8930eaa9dfa336edca7a000000000000609b000000000000c1370000000000000001000000000000000a0000000000000001000000000000006400127500001fa40000000001000000000000000a64000000010000000a00000001000000000000000300000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca820000000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca820000000009000000015c640ddd6afc7d8059ef54663654d74f0c56cc1ed0b974d401171cdae0b29be67f3223e299d3e5e7c492ef4c7110ddf44d672bd698c42947bfb15ab750f0ca8200",
|
||||
expectedComplexityErr: ErrUnsupportedTx,
|
||||
expectedDynamicFeeErr: ErrUnsupportedTx,
|
||||
},
|
||||
{
|
||||
name: "TransferChainOwnershipTx",
|
||||
tx: "00000000002100003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99bf1ec0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000018f6e5f2840e34f9a375f35627a44bb0b9974285d280dc3220aa9489f97b17ebd00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834e99ce610000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a00000001000000000000000b00000000000000000000000000000000000000020000000900000001e3479034ed8134dd23e154e1ec6e61b25073a20750ebf808e50ec1aae180ef430f8151347afdf6606bc7866f7f068b01719e4dad12e2976af1159fb048f73f7f010000000900000001e3479034ed8134dd23e154e1ec6e61b25073a20750ebf808e50ec1aae180ef430f8151347afdf6606bc7866f7f068b01719e4dad12e2976af1159fb048f73f7f01",
|
||||
tx: "00010000002100003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000070023834e99bf1ec0000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000018f6e5f2840e34f9a375f35627a44bb0b9974285d280dc3220aa9489f97b17ebd00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db000000050023834e99ce610000000001000000000000000097ea88082100491617204ed70c19fc1a2fce4474bee962904359d0b59e84c1240000000a00000001000000000000000b00000000000000000000000000000000000000020000000900000001e3479034ed8134dd23e154e1ec6e61b25073a20750ebf808e50ec1aae180ef430f8151347afdf6606bc7866f7f068b01719e4dad12e2976af1159fb048f73f7f010000000900000001e3479034ed8134dd23e154e1ec6e61b25073a20750ebf808e50ec1aae180ef430f8151347afdf6606bc7866f7f068b01719e4dad12e2976af1159fb048f73f7f01",
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 436, // The length of the tx in bytes
|
||||
gas.DBRead: IntrinsicTransferChainOwnershipTxComplexities[gas.DBRead] + intrinsicInputDBRead,
|
||||
@@ -195,7 +195,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "ConvertNetworkToL1Tx",
|
||||
tx: "00000000002300003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f234262960000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc0f94e000000010000000000000000a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d2000000000000000100000014c582872c37c81efa2c94ea347af49cdc23a830aa000000000000c137000000003b9aca00a3783a891cb41cadbfcf456da149f30e7af972677a162b984bef0779f254baac51ec042df1781d1295df80fb41c801269731fc6c25e1e5940dc3cb8509e30348fa712742cfdc83678acc9f95908eb98b89b28802fb559b4a2a6ff3216707c07f0ceb0b45a95f4f9a9540bbd3331d8ab4f233bffa4abb97fad9d59a1695f31b92a2b89e365facf7ab8c30de7c4a496d1e000000000000000000000000000000000000000a00000001000000000000000200000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd3790100000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd37901",
|
||||
tx: "00010000002300003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f234262960000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc0f94e000000010000000000000000a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d2000000000000000100000014c582872c37c81efa2c94ea347af49cdc23a830aa000000000000c137000000003b9aca00a3783a891cb41cadbfcf456da149f30e7af972677a162b984bef0779f254baac51ec042df1781d1295df80fb41c801269731fc6c25e1e5940dc3cb8509e30348fa712742cfdc83678acc9f95908eb98b89b28802fb559b4a2a6ff3216707c07f0ceb0b45a95f4f9a9540bbd3331d8ab4f233bffa4abb97fad9d59a1695f31b92a2b89e365facf7ab8c30de7c4a496d1e000000000000000000000000000000000000000a00000001000000000000000200000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd3790100000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd37901",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 656, // The length of the tx in bytes
|
||||
@@ -207,7 +207,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "RegisterL1ValidatorTx",
|
||||
tx: "00000000002400003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b552a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001ca44ad45a63381b07074be7f82005c41550c989b967f40020f3bedc4b02191f300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f234262404000000010000000000000000000000003b9aca00ab5cb0516b7afdb13727f766185b2b8da44e2653eef63c85f196701083e649289cce1a23c39eb471b2473bc6872aa3ea190de0fe66296cbdd4132c92c3430ff22f28f0b341b15905a005bbd66cc0f4056bc4be5934e4f3a57151a60060f429190000012f000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d20000009c000000000001000000000000008e000000000001a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b000000145efc86a11c5b12cc95b2cf527c023f9cf6e0e8f6b62034315c5d11cea4190f6ea8997821c02483d29adb5e4567843f7a44c39b2ffa20c8520dc358702fb1ec29f2746dcc000000006705af280000000000000000000000000000000000000000000000010000000000000001018e99dc6ed736089c03b9a1275e0cf801524ed341fb10111f29c0390fa2f96cf6aa78539ec767e5cd523c606c7ede50e60ba6065a3685e770d979b0df74e3541b61ed63f037463776098576e385767a695de59352b44e515831c5ee7a8cc728f9000000010000000900000001a0950b9e6e866130f0d09e2a7bfdd0246513295237258afa942b1850dab79824605c796bbfc9223cf91935fb29c66f8b927690220b9b1c24d6f078054a3e346201",
|
||||
tx: "00010000002500003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b552a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001ca44ad45a63381b07074be7f82005c41550c989b967f40020f3bedc4b02191f300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f234262404000000010000000000000000000000003b9aca00ab5cb0516b7afdb13727f766185b2b8da44e2653eef63c85f196701083e649289cce1a23c39eb471b2473bc6872aa3ea190de0fe66296cbdd4132c92c3430ff22f28f0b341b15905a005bbd66cc0f4056bc4be5934e4f3a57151a60060f429190000012f000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d20000009c000000000001000000000000008e000000000001a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b000000145efc86a11c5b12cc95b2cf527c023f9cf6e0e8f6b62034315c5d11cea4190f6ea8997821c02483d29adb5e4567843f7a44c39b2ffa20c8520dc358702fb1ec29f2746dcc000000006705af280000000000000000000000000000000000000000000000010000000000000001018e99dc6ed736089c03b9a1275e0cf801524ed341fb10111f29c0390fa2f96cf6aa78539ec767e5cd523c606c7ede50e60ba6065a3685e770d979b0df74e3541b61ed63f037463776098576e385767a695de59352b44e515831c5ee7a8cc728f9000000010000000900000001a0950b9e6e866130f0d09e2a7bfdd0246513295237258afa942b1850dab79824605c796bbfc9223cf91935fb29c66f8b927690220b9b1c24d6f078054a3e346201",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 710, // The length of the tx in bytes
|
||||
@@ -219,7 +219,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "SetL1ValidatorWeightTx",
|
||||
tx: "00000000002500003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b5100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001389c41b6ed301e4c118bd23673268fd2054b772efcf25685a117b74bab7ae5e400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b552a000000010000000000000000000000d7000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000044000000000001000000000000003600000000000338e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000001000000000000000500000000000000010187f4bb2c42869c56f023a1ca81045aff034acd490b8f15b5069025f982e605e077007fc588f7d56369a65df7574df3b70ff028ea173739c789525ab7eebfcb5c115b13cca8f02b362104b700c75bc95234109f3f1360ddcb4ec3caf6b0e821cb0000000100000009000000010a29f3c86d52908bf2efbc3f918a363df704c429d66c8d6615712a2a584a2a5f264a9e7b107c07122a06f31cadc2f51285884d36fe8df909a07467417f1d64cf00",
|
||||
tx: "00010000002600003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b5100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001389c41b6ed301e4c118bd23673268fd2054b772efcf25685a117b74bab7ae5e400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b552a000000010000000000000000000000d7000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000044000000000001000000000000003600000000000338e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000001000000000000000500000000000000010187f4bb2c42869c56f023a1ca81045aff034acd490b8f15b5069025f982e605e077007fc588f7d56369a65df7574df3b70ff028ea173739c789525ab7eebfcb5c115b13cca8f02b362104b700c75bc95234109f3f1360ddcb4ec3caf6b0e821cb0000000100000009000000010a29f3c86d52908bf2efbc3f918a363df704c429d66c8d6615712a2a584a2a5f264a9e7b107c07122a06f31cadc2f51285884d36fe8df909a07467417f1d64cf00",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 518, // The length of the tx in bytes
|
||||
@@ -231,7 +231,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "IncreaseL1ValidatorBalanceTx",
|
||||
tx: "00000000002600003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4e52000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001f61ea7e3bb6d33da9901644f3c623e4537b7d1c276e9ef23bcc8e4150e494d6600000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b510000000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000002000000010000000900000001cb56b56387be9186d86430fad5418db4d13e991b6805b6ba178b719e3f47ce001da52d6ed3173bfdd8b69940a135432abce493a10332e881f6c34cea3617595e00",
|
||||
tx: "00010000002700003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4e52000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001f61ea7e3bb6d33da9901644f3c623e4537b7d1c276e9ef23bcc8e4150e494d6600000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b510000000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000002000000010000000900000001cb56b56387be9186d86430fad5418db4d13e991b6805b6ba178b719e3f47ce001da52d6ed3173bfdd8b69940a135432abce493a10332e881f6c34cea3617595e00",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 339, // The length of the tx in bytes
|
||||
@@ -243,7 +243,7 @@ var (
|
||||
},
|
||||
{
|
||||
name: "DisableL1ValidatorTx",
|
||||
tx: "00000000002700003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4b9e000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001fd91c5c421468b13b09dda413bdbe1316c7c9417f2468b893071d4cb608a01da00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b4e5200000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000a00000000000000020000000900000001ff99bb626d898907a660701e2febaa311b4e644fe71add2d1a3f71748102c73f54d73c8370a9ae33e09c984bb8c03da4922bf208af836ec2daaa31cb42788bee010000000900000000",
|
||||
tx: "00010000002800003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4b9e000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001fd91c5c421468b13b09dda413bdbe1316c7c9417f2468b893071d4cb608a01da00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b4e5200000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000a00000000000000020000000900000001ff99bb626d898907a660701e2febaa311b4e644fe71add2d1a3f71748102c73f54d73c8370a9ae33e09c984bb8c03da4922bf208af836ec2daaa31cb42788bee010000000900000000",
|
||||
expectedStaticFeeErr: ErrUnsupportedTx,
|
||||
expectedComplexity: gas.Dimensions{
|
||||
gas.Bandwidth: 347, // The length of the tx in bytes
|
||||
|
||||
@@ -161,9 +161,9 @@ func TestIncreaseL1ValidatorBalanceTxSerialization(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// IncreaseL1ValidatorBalanceTx Type ID
|
||||
0x00, 0x00, 0x00, 0x26,
|
||||
0x00, 0x00, 0x00, 0x27,
|
||||
// Network ID
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// P-chain blockchain ID (31 zeros + 'P')
|
||||
|
||||
@@ -169,9 +169,9 @@ func TestRegisterL1ValidatorTxSerialization(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// RegisterL1ValidatorTx Type ID
|
||||
0x00, 0x00, 0x00, 0x24,
|
||||
0x00, 0x00, 0x00, 0x25,
|
||||
// Network ID
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// P-chain blockchain ID (31 zeros + 'P')
|
||||
|
||||
@@ -110,7 +110,7 @@ func TestRemoveChainValidatorTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleRemoveValidatorTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// RemoveChainValidatorTx Type ID (post codec-collapse: 27)
|
||||
0x00, 0x00, 0x00, 0x1b,
|
||||
// Mainnet network ID
|
||||
@@ -280,7 +280,7 @@ func TestRemoveChainValidatorTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexRemoveValidatorTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// RemoveChainValidatorTx Type ID (post codec-collapse: 27)
|
||||
0x00, 0x00, 0x00, 0x1b,
|
||||
// Mainnet network ID
|
||||
|
||||
@@ -154,9 +154,9 @@ func TestSetL1ValidatorWeightTxSerialization(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// SetL1ValidatorWeightTx Type ID
|
||||
0x00, 0x00, 0x00, 0x25,
|
||||
0x00, 0x00, 0x00, 0x26,
|
||||
// Network ID
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
// P-chain blockchain ID (31 zeros + 'P')
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestTransferChainOwnershipTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleTransferChainOwnershipTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// TransferChainOwnershipTx Type ID
|
||||
0x00, 0x00, 0x00, 0x21,
|
||||
// Mainnet network ID
|
||||
@@ -288,7 +288,7 @@ func TestTransferChainOwnershipTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexTransferChainOwnershipTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// TransferChainOwnershipTx Type ID
|
||||
0x00, 0x00, 0x00, 0x21,
|
||||
// Mainnet network ID
|
||||
|
||||
@@ -125,7 +125,7 @@ func TestTransformChainTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedSimpleTransformTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// TransformChainTx type ID (post codec-collapse: 28)
|
||||
0x00, 0x00, 0x00, 0x1c,
|
||||
// Mainnet network ID
|
||||
@@ -353,7 +353,7 @@ func TestTransformChainTxSerialization(t *testing.T) {
|
||||
|
||||
expectedUnsignedComplexTransformTxBytes := []byte{
|
||||
// Codec version
|
||||
0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
// TransformChainTx type ID (post codec-collapse: 28)
|
||||
0x00, 0x00, 0x00, 0x1c,
|
||||
// Mainnet network ID
|
||||
|
||||
+82
-11
@@ -48,6 +48,12 @@ func NewSigned(
|
||||
return res, res.Sign(c, signers)
|
||||
}
|
||||
|
||||
// Initialize marshals tx with the canonical write codec (v1) and
|
||||
// derives TxID = hash(signedBytes). This is appropriate ONLY for txs
|
||||
// built fresh in this process (e.g. by the wallet or by the block
|
||||
// builder). For txs loaded from disk or received from the wire, use
|
||||
// InitializeFromBytes — Initialize would re-encode at v1, changing the
|
||||
// TxID of any tx that was originally serialized at v0.
|
||||
func (tx *Tx) Initialize(c codec.Manager) error {
|
||||
signedBytes, err := c.Marshal(CodecVersion, tx)
|
||||
if err != nil {
|
||||
@@ -64,28 +70,93 @@ func (tx *Tx) Initialize(c codec.Manager) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeFromBytes binds the tx to the EXACT signedBytes it was
|
||||
// decoded from, deriving TxID = hash(signedBytes) under the codec
|
||||
// version the bytes were originally written at. It never re-marshals,
|
||||
// so a tx whose bytes were written at v0 keeps its v0-derived TxID
|
||||
// forever — the chain commitment is therefore stable across the v0->v1
|
||||
// codec migration.
|
||||
//
|
||||
// version is the codec version the bytes were decoded under (returned
|
||||
// by codec.Manager.Unmarshal). It selects the c.Size(...) call used to
|
||||
// split signedBytes into unsignedBytes (the prefix the signature
|
||||
// covers) and the credentials suffix.
|
||||
func (tx *Tx) InitializeFromBytes(c codec.Manager, version uint16, signedBytes []byte) error {
|
||||
unsignedBytesLen, err := c.Size(version, &tx.Unsigned)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't calculate UnsignedTx marshal length: %w", err)
|
||||
}
|
||||
if unsignedBytesLen > len(signedBytes) {
|
||||
return fmt.Errorf("unsigned length %d exceeds signed length %d", unsignedBytesLen, len(signedBytes))
|
||||
}
|
||||
unsignedBytes := signedBytes[:unsignedBytesLen]
|
||||
tx.SetBytes(unsignedBytes, signedBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeFromBytesAtVersion is the byte-preserving init used after
|
||||
// a struct-level Unmarshal where the outer container (a genesis blob,
|
||||
// a stored block) decoded the tx field directly — in that case we
|
||||
// only have the struct value and the codec version it was decoded
|
||||
// under, not the raw signed bytes (which were never sliced out of the
|
||||
// outer stream by the codec).
|
||||
//
|
||||
// We re-derive signedBytes by Marshal'ing the just-decoded tx at the
|
||||
// SAME version, then bind via SetBytes. The wire format is
|
||||
// deterministic and the codec is closed at v0, so the re-marshal
|
||||
// produces the exact bytes the original v0 producer emitted; therefore
|
||||
// TxID = hash(re-marshaled v0 bytes) = TxID the chain committed.
|
||||
//
|
||||
// This single bounded re-marshal is the ONLY place we allow
|
||||
// c.Marshal(v0, ...) inside the read path. It is justified because:
|
||||
// 1. Linearcodec marshal is a pure function of the struct fields
|
||||
// under a fixed slot map, so the output is byte-equal to the
|
||||
// original v0 producer's output.
|
||||
// 2. The struct fields were just populated by c.Unmarshal at the
|
||||
// same version, so no information has been lost or rotated.
|
||||
// 3. The c.Size(v0, ...) inverse used by InitializeFromBytes is the
|
||||
// same path, just in the opposite direction — the size of an
|
||||
// Unsigned in v0 layout is a fixed function of its fields.
|
||||
//
|
||||
// Once every genesis blob and every stored block on disk has been
|
||||
// re-encoded at v1 (a future migration), this method becomes dead
|
||||
// code and the v0 codec entry can be removed.
|
||||
func (tx *Tx) InitializeFromBytesAtVersion(c codec.Manager, version uint16) error {
|
||||
signedBytes, err := c.Marshal(version, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't re-marshal tx at v%d: %w", version, err)
|
||||
}
|
||||
return tx.InitializeFromBytes(c, version, signedBytes)
|
||||
}
|
||||
|
||||
func (tx *Tx) SetBytes(unsignedBytes, signedBytes []byte) {
|
||||
tx.Unsigned.SetBytes(unsignedBytes)
|
||||
tx.bytes = signedBytes
|
||||
tx.TxID = hash.ComputeHash256Array(signedBytes)
|
||||
}
|
||||
|
||||
// Parse signed tx starting from its byte representation.
|
||||
// Note: We explicitly pass the codec in Parse since we may need to parse
|
||||
// P-Chain genesis txs whose length exceed the max length of txs.Codec.
|
||||
// Parse signed tx starting from its byte representation. The wire
|
||||
// version is taken from the 2-byte prefix that c.Unmarshal reads;
|
||||
// c.Size(...) is then called under THAT version so the split point
|
||||
// between unsignedBytes and the credentials suffix is correct for
|
||||
// either v0 or v1 layouts.
|
||||
//
|
||||
// Parse never re-marshals: TxID = hash(signedBytes) verbatim. This is
|
||||
// the byte-preserving path that all from-disk and from-wire reads must
|
||||
// go through to keep TxIDs stable across the v0→v1 migration.
|
||||
//
|
||||
// We explicitly pass the codec in Parse since some call sites (genesis,
|
||||
// state fallback) must use GenesisCodec to admit txs larger than the
|
||||
// max length of Codec.
|
||||
func Parse(c codec.Manager, signedBytes []byte) (*Tx, error) {
|
||||
tx := &Tx{}
|
||||
if _, err := c.Unmarshal(signedBytes, tx); err != nil {
|
||||
version, err := c.Unmarshal(signedBytes, tx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse tx: %w", err)
|
||||
}
|
||||
|
||||
unsignedBytesLen, err := c.Size(CodecVersion, &tx.Unsigned)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't calculate UnsignedTx marshal length: %w", err)
|
||||
if err := tx.InitializeFromBytes(c, version, signedBytes); err != nil {
|
||||
return nil, fmt.Errorf("couldn't initialize tx from bytes: %w", err)
|
||||
}
|
||||
|
||||
unsignedBytes := signedBytes[:unsignedBytesLen]
|
||||
tx.SetBytes(unsignedBytes, signedBytes)
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user