Files
node/vms/platformvm/block/executor/options.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

149 lines
4.1 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package executor
import (
"errors"
"fmt"
"github.com/luxfi/constants"
"github.com/luxfi/log"
"github.com/luxfi/node/vms/platformvm/block"
"github.com/luxfi/node/vms/platformvm/reward"
"github.com/luxfi/node/vms/platformvm/state"
"github.com/luxfi/node/vms/platformvm/txs"
txexecutor "github.com/luxfi/node/vms/platformvm/txs/executor"
"github.com/luxfi/validators/uptime"
)
var (
_ block.Visitor = (*options)(nil)
ErrNotOracle = errors.New("block doesn't have options")
errUnexpectedProposalTxType = errors.New("unexpected proposal transaction type")
errFailedFetchingStakerTx = errors.New("failed fetching staker transaction")
errUnexpectedStakerTxType = errors.New("unexpected staker transaction type")
errFailedFetchingPrimaryStaker = errors.New("failed fetching primary staker")
errFailedFetchingNetTransformation = errors.New("failed fetching net transformation")
errFailedCalculatingUptime = errors.New("failed calculating uptime")
)
// options supports build new option blocks
type options struct {
// inputs populated before calling this struct's methods:
log log.Logger
primaryUptimePercentage float64
uptimes uptime.Calculator
state state.Chain
// outputs populated by this struct's methods:
preferredBlock block.Block
alternateBlock block.Block
}
func (*options) AbortBlock(*block.AbortBlock) error {
return ErrNotOracle
}
func (*options) CommitBlock(*block.CommitBlock) error {
return ErrNotOracle
}
func (o *options) ProposalBlock(b *block.ProposalBlock) error {
timestamp := b.Timestamp()
blkID := b.ID()
nextHeight := b.Height() + 1
commitBlock, err := block.NewCommitBlock(timestamp, blkID, nextHeight)
if err != nil {
return fmt.Errorf(
"failed to create commit block: %w",
err,
)
}
abortBlock, err := block.NewAbortBlock(timestamp, blkID, nextHeight)
if err != nil {
return fmt.Errorf(
"failed to create abort block: %w",
err,
)
}
prefersCommit, err := o.prefersCommit(b.Tx())
if err != nil {
o.log.Debug("falling back to prefer commit",
"error", err,
)
// We fall back to commit here to err on the side of over-rewarding
// rather than under-rewarding.
//
// Invariant: We must not return the error here, because the error would
// be treated as fatal. Errors can occur here due to a malicious block
// proposer or even in unusual virtuous cases.
prefersCommit = true
}
if prefersCommit {
o.preferredBlock = commitBlock
o.alternateBlock = abortBlock
} else {
o.preferredBlock = abortBlock
o.alternateBlock = commitBlock
}
return nil
}
func (*options) StandardBlock(*block.StandardBlock) error {
return ErrNotOracle
}
func (o *options) prefersCommit(tx *txs.Tx) (bool, error) {
unsignedTx, ok := tx.Unsigned.(*txs.RewardValidatorTx)
if !ok {
return false, fmt.Errorf("%w: %T", errUnexpectedProposalTxType, tx.Unsigned)
}
stakerTx, _, err := o.state.GetTx(unsignedTx.TxID())
if err != nil {
return false, fmt.Errorf("%w: %w", errFailedFetchingStakerTx, err)
}
staker, ok := stakerTx.Unsigned.(txs.Staker)
if !ok {
return false, fmt.Errorf("%w: %T", errUnexpectedStakerTxType, stakerTx.Unsigned)
}
nodeID := staker.NodeID()
primaryNetworkValidator, err := o.state.GetCurrentValidator(
constants.PrimaryNetworkID,
nodeID,
)
if err != nil {
return false, fmt.Errorf("%w: %w", errFailedFetchingPrimaryStaker, err)
}
netID := staker.ChainID()
expectedUptimePercentage := o.primaryUptimePercentage
if netID != constants.PrimaryNetworkID {
transformNet, err := txexecutor.GetTransformChainTx(o.state, netID)
if err != nil {
return false, fmt.Errorf("%w: %w", errFailedFetchingNetTransformation, err)
}
expectedUptimePercentage = float64(transformNet.UptimeRequirement()) / reward.PercentDenominator
}
uptime, err := o.uptimes.CalculateUptimePercentFrom(
nodeID,
netID,
primaryNetworkValidator.StartTime,
)
if err != nil {
return false, fmt.Errorf("%w: %w", errFailedCalculatingUptime, err)
}
return uptime >= expectedUptimePercentage, nil
}