Files
coreth/sync/atomic/summary.go
zeekay 0bfbe1a491 coreth/atomic-tx: complete codec rip Wave 2B
Drop github.com/luxfi/codec from the atomic-tx subsystem and replace
the reflection-based polymorphic registry with a hand-rolled binary
encoder. Type IDs preserved from the legacy linearcodec registration
order (UnsignedImportTx=0, UnsignedExportTx=1, TransferInput=5,
TransferOutput=7, secp256k1fx.Credential=9) so on-disk blobs and
p2p gossip framing stay byte-equal to legacy.

New atomic.Manager interface mirrors the message.Manager pattern
shipped in Wave 1C — Marshal/Unmarshal only, no reflection registry.
The atomic.Codec singleton is the only Manager exported from the
package; downstream code (state/, vm/, sync/atomic, tests/warp,
plugin/evm/vm.go, plugin/evm/network_handler.go) holds an
atomic.Manager (or message.Manager for the wire codec) instead of
the legacy codec.Manager.

Tx.Sign drops its codec.Manager argument and uses the package-local
atomic.Codec singleton. A new Tx.initializeBytes(c Manager) helper
re-marshals a freshly-unmarshaled tx through whatever Manager
produced the bytes — keeps the atomictest harness disjoint from
production Codec while round-tripping correctly.

sync/atomic.Summary picks up its own marshal/unmarshal (4 fixed
fields, no codec needed) so plugin/evm/message doesn't have to
know about syncable summary shape.

The secp256k1fx.VM contract still requires codec.Registry — extracted
to a new plugin/evm/secpfx/ adapter so the only remaining codec
dependency in the atomic-tx universe lives outside plugin/evm/atomic/.

Tests pass: plugin/evm/atomic/..., sync/atomic/..., tests/warp/...
2026-06-06 00:42:16 -07:00

130 lines
3.7 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package atomic
import (
"context"
"errors"
"fmt"
"github.com/luxfi/coreth/plugin/evm/message"
"github.com/luxfi/ids"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/crypto"
"github.com/luxfi/geth/common"
"github.com/luxfi/utils/wrappers"
)
var (
_ message.Syncable = (*Summary)(nil)
errSummaryShort = errors.New("syncable atomic summary truncated")
)
// Summary provides the information necessary to sync a node starting at
// the given block. The wire shape mirrors message.BlockSyncSummary with
// an AtomicRoot suffix:
//
// [u16 version=0]
// [u64 BlockNumber]
// [32 BlockHash]
// [32 BlockRoot]
// [32 AtomicRoot]
//
// The summary wire format is owned here (not by plugin/evm/message)
// because plugin/evm/message must not depend on sync/atomic. Marshal /
// Unmarshal are local; the message codec is only used to spell the
// version constant via message.Version.
type Summary struct {
*message.BlockSyncSummary `serialize:"true"`
AtomicRoot common.Hash `serialize:"true"`
summaryID ids.ID
bytes []byte
acceptImpl message.AcceptImplFn
}
func NewSummary(blockHash common.Hash, blockNumber uint64, blockRoot common.Hash, atomicRoot common.Hash) (*Summary, error) {
summary := Summary{
BlockSyncSummary: &message.BlockSyncSummary{
BlockNumber: blockNumber,
BlockHash: blockHash,
BlockRoot: blockRoot,
},
AtomicRoot: atomicRoot,
}
bytes, err := summary.marshal()
if err != nil {
return nil, fmt.Errorf("failed to marshal syncable summary: %w", err)
}
summary.bytes = bytes
summaryID, err := ids.ToID(crypto.Keccak256(bytes))
if err != nil {
return nil, fmt.Errorf("failed to compute summary ID: %w", err)
}
summary.summaryID = summaryID
return &summary, nil
}
func (a *Summary) Bytes() []byte {
return a.bytes
}
func (a *Summary) ID() ids.ID {
return a.summaryID
}
func (a *Summary) String() string {
return fmt.Sprintf("Summary(BlockHash=%s, BlockNumber=%d, BlockRoot=%s, AtomicRoot=%s)", a.BlockHash, a.BlockNumber, a.BlockRoot, a.AtomicRoot)
}
func (a *Summary) Accept(context.Context) (block.StateSyncMode, error) {
if a.acceptImpl == nil {
return block.StateSyncSkipped, fmt.Errorf("accept implementation not specified for summary: %s", a)
}
return a.acceptImpl(a)
}
// marshal emits the [u16 version][BlockSyncSummary body][AtomicRoot]
// shape. Big-endian, byte-equal to legacy reflectcodec.
func (a *Summary) marshal() ([]byte, error) {
const summarySize = 2 + 8 + 32 + 32 + 32
p := &wrappers.Packer{MaxSize: summarySize, Bytes: make([]byte, 0, summarySize)}
p.PackShort(message.Version)
p.PackLong(a.BlockNumber)
p.PackFixedBytes(a.BlockHash[:])
p.PackFixedBytes(a.BlockRoot[:])
p.PackFixedBytes(a.AtomicRoot[:])
if p.Errored() {
return nil, p.Err
}
return p.Bytes, nil
}
// unmarshalSummary parses a wire blob in the format produced by
// [Summary.marshal] and populates [a]. Returns ErrUnknownVersion if the
// leading version is anything other than [message.Version].
func unmarshalSummary(blob []byte, a *Summary) error {
const summarySize = 2 + 8 + 32 + 32 + 32
if len(blob) != summarySize {
return fmt.Errorf("%w: got %d bytes, want %d", errSummaryShort, len(blob), summarySize)
}
p := &wrappers.Packer{Bytes: blob, MaxSize: summarySize}
v := p.UnpackShort()
if v != message.Version {
return fmt.Errorf("%w: %d", message.ErrUnknownVersion, v)
}
a.BlockSyncSummary = &message.BlockSyncSummary{}
a.BlockNumber = p.UnpackLong()
copy(a.BlockHash[:], p.UnpackFixedBytes(common.HashLength))
copy(a.BlockRoot[:], p.UnpackFixedBytes(common.HashLength))
copy(a.AtomicRoot[:], p.UnpackFixedBytes(common.HashLength))
if p.Errored() {
return p.Err
}
return nil
}