Files
node/vms/platformvm/network/gossip.go
T
zeekayandHanzo Dev c44cc2289f platformvm builds GREEN off the codec: executor fold + full consumer flip
vms/platformvm/... build + vet PASS with pcodecs/txs.Codec gone from the VM.

Executor semantics (standard_tx_executor):
- registerOwnSet(): shared primitive — per-validator state.L1Validator with
  native owner blobs (txs.MarshalOwner/UnmarshalOwner, no codec), active-set
  capacity check, EndAccumulatedFee=balance+accruedFees, SetNetToL1Conversion
  manager-authority recording (byte-for-byte legacy tail).
- ConvertNetworkTx: promote endomorphism (owner-authorized, folds old
  ConvertNetworkToL1Tx), gated by security.Mode.Manager.
- CreateNetworkTx: base (AddNet/SetNetOwner) + sovereign path registers own set.
- Deleted CreateSovereignL1Tx + ConvertNetworkToL1Tx.
- txs.UnmarshalOwner added: canonical owner marshal/unmarshal pair, no codec.

All ~13 txs.Visitor impls carry ConvertNetworkTx; gossip/block Parse(b) codec-free;
wallet/network/primary off txs.Codec.

KNOWN GAP (pending design decision, NOT silently green): CreateNetworkTx reads
tx.Chains() only to derive managerChainID — it does NOT create the genesis
chains (no AddChain). Atomic-spawn-with-chains vs decomplect-to-CreateChainTx is
the open call. 17 codec-era _test.go parked as .bak for follow-up rewrite.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-10 14:25:08 -07:00

154 lines
3.4 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package network
import (
"go.uber.org/zap"
"context"
"fmt"
"sync"
"time"
"github.com/luxfi/log"
"github.com/luxfi/ids"
"github.com/luxfi/metric"
"github.com/luxfi/node/vms/platformvm/txs"
"github.com/luxfi/node/vms/txs/mempool"
"github.com/luxfi/p2p"
"github.com/luxfi/p2p/gossip"
"github.com/luxfi/warp"
)
var (
_ p2p.Handler = (*txGossipHandler)(nil)
_ gossip.Marshaller[*txs.Tx] = (*txMarshaller)(nil)
_ gossip.Gossipable = (*txs.Tx)(nil)
)
// bloomChurnMultiplier is the number used to multiply the size of the mempool
// to determine how large of a bloom filter to create.
const bloomChurnMultiplier = 3
// txGossipHandler is the handler called when serving gossip messages
type txGossipHandler struct {
p2p.NoOpHandler
appGossipHandler p2p.Handler
appRequestHandler p2p.Handler
}
func (t txGossipHandler) Gossip(
ctx context.Context,
nodeID ids.NodeID,
gossipBytes []byte,
) {
t.appGossipHandler.Gossip(ctx, nodeID, gossipBytes)
}
func (t txGossipHandler) Request(
ctx context.Context,
nodeID ids.NodeID,
deadline time.Time,
requestBytes []byte,
) ([]byte, *warp.Error) {
return t.appRequestHandler.Request(ctx, nodeID, deadline, requestBytes)
}
type txMarshaller struct{}
func (txMarshaller) MarshalGossip(tx *txs.Tx) ([]byte, error) {
return tx.Bytes(), nil
}
func (txMarshaller) UnmarshalGossip(bytes []byte) (*txs.Tx, error) {
return txs.Parse(bytes)
}
func newGossipMempool(
mempool mempool.Mempool[*txs.Tx],
registerer metric.Registerer,
log log.Logger,
txVerifier TxVerifier,
minTargetElements int,
targetFalsePositiveProbability,
resetFalsePositiveProbability float64,
) (*gossipMempool, error) {
bloom, err := gossip.NewBloomFilter(registerer, "mempool_bloom_filter", minTargetElements, targetFalsePositiveProbability, resetFalsePositiveProbability)
return &gossipMempool{
Mempool: mempool,
log: log,
txVerifier: txVerifier,
bloom: bloom,
}, err
}
type gossipMempool struct {
mempool.Mempool[*txs.Tx]
log log.Logger
txVerifier TxVerifier
lock sync.RWMutex
bloom *gossip.BloomFilter
}
func (g *gossipMempool) Add(tx *txs.Tx) error {
txID := tx.ID()
if _, ok := g.Mempool.Get(txID); ok {
return fmt.Errorf("tx %s dropped: %w", txID, mempool.ErrDuplicateTx)
}
if reason := g.Mempool.GetDropReason(txID); reason != nil {
// If the tx is being dropped - just ignore it
//
// failed previously?
return reason
}
if err := g.txVerifier.VerifyTx(tx); err != nil {
g.log.Debug("transaction failed verification",
log.Stringer("txID", txID),
zap.Error(err),
)
g.Mempool.MarkDropped(txID, err)
return fmt.Errorf("failed verification: %w", err)
}
if err := g.Mempool.Add(tx); err != nil {
g.Mempool.MarkDropped(txID, err)
return err
}
g.lock.Lock()
defer g.lock.Unlock()
g.bloom.Add(tx)
reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, g.Mempool.Len()*bloomChurnMultiplier)
if err != nil {
return err
}
if reset {
g.log.Debug("resetting bloom filter")
g.Mempool.Iterate(func(tx *txs.Tx) bool {
g.bloom.Add(tx)
return true
})
}
return nil
}
func (g *gossipMempool) Has(txID ids.ID) bool {
_, ok := g.Mempool.Get(txID)
return ok
}
func (g *gossipMempool) GetFilter() (bloom []byte, salt []byte) {
g.lock.RLock()
defer g.lock.RUnlock()
return g.bloom.Marshal()
}