mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Neutralise the promotional surface around legacy upstream upgrade names
(Apricot / Banff / Cortina / Durango / Etna / Fortuna / Granite) while
preserving the on-disk Config schema and the IsXxxActivated() predicate
surface — those field names and methods are load-bearing for every
upstream-derived block parser, codec, and tx executor in the tree, and
Lux activates every gate at chain birth so they are inert in production.
Changes:
- upgrade/upgrade.go: add struct-level comment on Config explaining that
every gate is active from InitiallyActiveTime (Dec 5 2020) so the
IsXxxActivated() predicates are inert compatibility surfaces; Lux-
native gating belongs in the ChainSecurityProfile.
- upgrade/upgradetest/fork.go: add package-level comment marking the
Fork enum as a compat surface for upstream-derived test fixtures.
- vms/platformvm/docs/{block_formation_logic,chain_time_update}.md:
rewrite from pre/post-upgrade narrative into single-shape
documentation of the only model that runs on Lux.
- vms/platformvm/docs/validators_versioning.md, indexer/service.md,
config/config.md, vms/platformvm/warp/README.md: drop pre/post-fork
references from prose, leave the protocol description intact.
- vms/proposervm/proposer/windower.go, pre_fork_block.go,
vms/proposervm/block_test.go, vms/platformvm/warp/validator.go,
vms/platformvm/txs/executor/proposal_tx_executor.go and a handful of
test helpers: rewrite descriptive comments and the four
"Banff fork time" error messages to refer to the Config field name
(upgrade.Config.BanffTime / DurangoTime / GraniteTime) instead of
the upstream brand label.
- tests/granite_integration_test.go -> tests/lp181_integration_test.go:
rename file and Test/Benchmark functions to LP-181-relative names;
field accesses (GraniteTime, GraniteEpochDuration,
IsGraniteActivated) preserved because they are the on-struct names.
- network/network.go, network/peer/peer.go, chains/manager.go,
scripts/tests.upgrade.sh, .github/labels.yml: scrub stray
upstream-brand mentions from comments / labels.
What is intentionally preserved (compat shim policy, same logic as the
geth-config preservation rule for ~/work/lux/coreth /geth /genesis):
- upgrade.Config field names (ApricotPhaseNTime, BanffTime, ..., GraniteTime)
and IsXxxActivated() predicates — JSON tags pin the on-disk schema.
- upgradetest.Fork enum values (NoUpgrades..Granite) — referenced by
hundreds of upstream-derived test fixtures.
- Block-type identifiers (BanffStandardBlock, ApricotProposalBlock, ...)
— wire-format-load-bearing.
- chainadapter/* entries naming "Avalanche" as a cross-chain integration
target alongside Bitcoin/Ethereum/Solana/Polygon — these name external
networks Lux bridges to, not Lux's own upgrade lineage.
- RELEASES.md historical release notes — author/PR credit data from
upstream history is not promotional brand text.
Build verified: GOWORK=off go build ./... -> exit 0.
410 lines
11 KiB
Go
410 lines
11 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package builder
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/luxfi/metric"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/consensus/core/coremock"
|
|
"github.com/luxfi/runtime"
|
|
consensustest "github.com/luxfi/consensus/test/helpers"
|
|
"github.com/luxfi/validators/uptime"
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
"github.com/luxfi/database/memdb"
|
|
"github.com/luxfi/database/prefixdb"
|
|
"github.com/luxfi/database/versiondb"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/log"
|
|
"github.com/luxfi/p2p"
|
|
|
|
"github.com/luxfi/codec"
|
|
"github.com/luxfi/codec/linearcodec"
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/node/chains"
|
|
"github.com/luxfi/node/upgrade/upgradetest"
|
|
"github.com/luxfi/node/vms/platformvm/config"
|
|
"github.com/luxfi/node/vms/platformvm/fx"
|
|
"github.com/luxfi/node/vms/platformvm/genesis/genesistest"
|
|
"github.com/luxfi/node/vms/platformvm/metrics"
|
|
"github.com/luxfi/node/vms/platformvm/network"
|
|
"github.com/luxfi/node/vms/platformvm/reward"
|
|
"github.com/luxfi/node/vms/platformvm/state"
|
|
"github.com/luxfi/node/vms/platformvm/state/statetest"
|
|
"github.com/luxfi/node/vms/platformvm/status"
|
|
"github.com/luxfi/node/vms/platformvm/txs"
|
|
"github.com/luxfi/node/vms/platformvm/txs/mempool"
|
|
"github.com/luxfi/node/vms/platformvm/txs/txstest"
|
|
"github.com/luxfi/node/vms/platformvm/utxo"
|
|
"github.com/luxfi/node/vms/platformvm/validators/validatorstest"
|
|
"github.com/luxfi/node/wallet/chain/p/wallet"
|
|
"github.com/luxfi/timer/mockable"
|
|
"github.com/luxfi/utils"
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
"github.com/luxfi/vm/chains/atomic"
|
|
|
|
blockexecutor "github.com/luxfi/node/vms/platformvm/block/executor"
|
|
txexecutor "github.com/luxfi/node/vms/platformvm/txs/executor"
|
|
"github.com/luxfi/node/vms/platformvm/warp"
|
|
txmempool "github.com/luxfi/node/vms/txs/mempool"
|
|
|
|
validators "github.com/luxfi/validators"
|
|
)
|
|
|
|
const (
|
|
defaultMinStakingDuration = 24 * time.Hour
|
|
defaultMaxStakingDuration = 365 * 24 * time.Hour
|
|
)
|
|
|
|
var testNet1 *txs.Tx
|
|
|
|
// mockValidatorState implements validators.State for testing
|
|
type mockValidatorState struct{}
|
|
|
|
func (m *mockValidatorState) GetChainID(netID ids.ID) (ids.ID, error) {
|
|
return ids.Empty, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetNetworkID(chainID ids.ID) (ids.ID, error) {
|
|
return constants.PrimaryNetworkID, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetValidatorSet(ctx context.Context, height uint64, netID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetCurrentValidators(ctx context.Context, height uint64, netID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetCurrentHeight(ctx context.Context) (uint64, error) {
|
|
return 100, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetMinimumHeight(ctx context.Context) (uint64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetWarpValidatorSets(ctx context.Context, heights []uint64, netIDs []ids.ID) (map[ids.ID]map[uint64]*validators.WarpSet, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockValidatorState) GetWarpValidatorSet(ctx context.Context, height uint64, netID ids.ID) (*validators.WarpSet, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type mutableSharedMemory struct {
|
|
atomic.SharedMemory
|
|
}
|
|
|
|
type environment struct {
|
|
Builder
|
|
blkManager blockexecutor.Manager
|
|
mempool txmempool.Mempool[*txs.Tx]
|
|
network *network.Network
|
|
sender *coremock.MockSender
|
|
|
|
isBootstrapped *utils.Atomic[bool]
|
|
config *config.Internal
|
|
clk *mockable.Clock
|
|
baseDB *versiondb.Database
|
|
rt *runtime.Runtime
|
|
msm *mutableSharedMemory
|
|
fx fx.Fx
|
|
state state.State
|
|
utxosVerifier utxo.Verifier
|
|
backend txexecutor.Backend
|
|
}
|
|
|
|
func newEnvironment(t *testing.T, f upgradetest.Fork) *environment { //nolint:unparam
|
|
require := require.New(t)
|
|
|
|
res := &environment{
|
|
isBootstrapped: &utils.Atomic[bool]{},
|
|
config: defaultConfig(f),
|
|
clk: defaultClock(),
|
|
}
|
|
res.isBootstrapped.Set(true)
|
|
|
|
res.baseDB = versiondb.New(memdb.New())
|
|
atomicDB := prefixdb.New([]byte{1}, res.baseDB)
|
|
m := atomic.NewMemory(atomicDB)
|
|
|
|
// Use PlatformChainID to match genesis transactions
|
|
res.rt = consensustest.Runtime(t, constants.PlatformChainID)
|
|
logger := log.NoLog{}
|
|
res.rt.Log = logger
|
|
res.msm = &mutableSharedMemory{
|
|
SharedMemory: m.NewSharedMemory(res.rt.ChainID),
|
|
}
|
|
res.rt.SharedMemory = res.msm
|
|
|
|
// Create a mock ValidatorState that implements runtime.ValidatorState
|
|
res.rt.ValidatorState = &mockValidatorState{}
|
|
|
|
res.rt.Lock.Lock()
|
|
defer res.rt.Lock.Unlock()
|
|
|
|
res.fx = defaultFx(t, res.clk, logger, res.isBootstrapped.Get())
|
|
|
|
rewardsCalc := reward.NewCalculator(res.config.RewardConfig)
|
|
// Convert test runtime to runtime.Runtime for state.
|
|
stateConsensusCtx := &runtime.Runtime{
|
|
NetworkID: res.rt.NetworkID,
|
|
ChainID: res.rt.ChainID,
|
|
NodeID: res.rt.NodeID,
|
|
XAssetID: res.rt.XAssetID,
|
|
Log: logger,
|
|
}
|
|
res.state = statetest.New(t, statetest.Config{
|
|
DB: res.baseDB,
|
|
Genesis: genesistest.NewBytes(t, genesistest.Config{}),
|
|
Validators: res.config.Validators,
|
|
Runtime: stateConsensusCtx,
|
|
Rewards: rewardsCalc,
|
|
})
|
|
|
|
// Uptime calculator is set to NoOp in backend
|
|
res.utxosVerifier = utxo.NewVerifier(res.clk, res.fx)
|
|
|
|
genesisID := res.state.GetLastAccepted()
|
|
// Convert test runtime to runtime.Runtime.
|
|
backendConsensusCtx := &runtime.Runtime{
|
|
NetworkID: res.rt.NetworkID,
|
|
ChainID: res.rt.ChainID,
|
|
NodeID: res.rt.NodeID,
|
|
XAssetID: res.rt.XAssetID,
|
|
Log: logger,
|
|
ValidatorState: res.rt.ValidatorState,
|
|
SharedMemory: res.rt.SharedMemory,
|
|
}
|
|
|
|
res.backend = txexecutor.Backend{
|
|
Config: res.config,
|
|
Runtime: backendConsensusCtx,
|
|
Clk: res.clk,
|
|
Bootstrapped: res.isBootstrapped,
|
|
Fx: res.fx,
|
|
FlowChecker: res.utxosVerifier,
|
|
Uptimes: &uptime.NoOpCalculator{},
|
|
Rewards: rewardsCalc,
|
|
}
|
|
|
|
registerer := metric.NewRegistry()
|
|
res.sender = &coremock.MockSender{
|
|
SendGossipF: func(context.Context, p2p.SendConfig, []byte) error {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
platformMetrics, err := metrics.New(registerer)
|
|
require.NoError(err)
|
|
|
|
res.mempool, err = mempool.New("mempool", registerer)
|
|
require.NoError(err)
|
|
|
|
res.blkManager = blockexecutor.NewManager(
|
|
res.mempool,
|
|
platformMetrics,
|
|
res.state,
|
|
&res.backend,
|
|
validatorstest.Manager,
|
|
)
|
|
|
|
// Use validatorstest.Manager for validator state
|
|
txVerifier := network.NewLockedTxVerifier(&res.rt.Lock, res.blkManager)
|
|
|
|
// Tests don't need warp signing, so we pass nil
|
|
var warpSigner warp.Signer
|
|
|
|
res.network, err = network.New(
|
|
logger,
|
|
res.rt.NodeID,
|
|
res.rt.ChainID,
|
|
validatorstest.Manager,
|
|
txVerifier,
|
|
res.mempool,
|
|
res.backend.Config.PartialSyncPrimaryNetwork,
|
|
res.sender,
|
|
&res.rt.Lock,
|
|
res.state,
|
|
warpSigner,
|
|
registerer,
|
|
config.DefaultNetwork,
|
|
)
|
|
require.NoError(err)
|
|
|
|
res.Builder = New(
|
|
res.mempool,
|
|
&res.backend,
|
|
res.blkManager,
|
|
)
|
|
|
|
res.blkManager.SetPreference(genesisID)
|
|
addNet(t, res)
|
|
|
|
t.Cleanup(func() {
|
|
// Note: We need to be careful about the cleanup order.
|
|
// The lock should already be released before cleanup runs.
|
|
// State and DB should be closed only after all operations complete.
|
|
if res.state != nil {
|
|
_ = res.state.Close()
|
|
}
|
|
if res.baseDB != nil {
|
|
_ = res.baseDB.Close()
|
|
}
|
|
})
|
|
|
|
return res
|
|
}
|
|
|
|
type walletConfig struct {
|
|
keys []*secp256k1.PrivateKey
|
|
netIDs []ids.ID
|
|
}
|
|
|
|
func newWallet(t testing.TB, e *environment, c walletConfig) wallet.Wallet {
|
|
if len(c.keys) == 0 {
|
|
c.keys = genesistest.DefaultFundedKeys
|
|
}
|
|
// Convert test runtime to runtime.Runtime for wallet.
|
|
walletCtx := &runtime.Runtime{
|
|
NetworkID: e.rt.NetworkID,
|
|
ChainID: e.rt.ChainID,
|
|
NodeID: e.rt.NodeID,
|
|
XAssetID: e.rt.XAssetID,
|
|
SharedMemory: e.rt.SharedMemory,
|
|
}
|
|
// Create a minimal Config for the wallet
|
|
walletCfg := &config.Config{
|
|
TxFee: constants.MilliLux,
|
|
CreateAssetTxFee: constants.MilliLux,
|
|
CreateNetworkTxFee: constants.Lux,
|
|
CreateChainTxFee: constants.Lux,
|
|
AddNetworkValidatorFee: 0,
|
|
AddNetworkDelegatorFee: 0,
|
|
}
|
|
return txstest.NewWallet(
|
|
t,
|
|
walletCtx,
|
|
walletCfg,
|
|
e.state,
|
|
secp256k1fx.NewKeychain(c.keys...),
|
|
c.netIDs,
|
|
nil, // validationIDs
|
|
[]ids.ID{e.rt.CChainID, e.rt.XChainID},
|
|
)
|
|
}
|
|
|
|
func addNet(t *testing.T, env *environment) {
|
|
require := require.New(t)
|
|
|
|
wallet := newWallet(t, env, walletConfig{
|
|
keys: genesistest.DefaultFundedKeys[:1],
|
|
})
|
|
|
|
var err error
|
|
testNet1, err = wallet.IssueCreateNetworkTx(
|
|
&secp256k1fx.OutputOwners{
|
|
Threshold: 2,
|
|
Addrs: []ids.ShortID{
|
|
genesistest.DefaultFundedKeys[0].Address(),
|
|
genesistest.DefaultFundedKeys[1].Address(),
|
|
genesistest.DefaultFundedKeys[2].Address(),
|
|
},
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
|
|
genesisID := env.state.GetLastAccepted()
|
|
stateDiff, err := state.NewDiff(genesisID, env.blkManager)
|
|
require.NoError(err)
|
|
|
|
feeCalculator := state.PickFeeCalculator(env.config, stateDiff)
|
|
_, _, _, err = txexecutor.StandardTx(
|
|
&env.backend,
|
|
feeCalculator,
|
|
testNet1,
|
|
stateDiff,
|
|
)
|
|
require.NoError(err)
|
|
|
|
stateDiff.AddTx(testNet1, status.Committed)
|
|
require.NoError(stateDiff.Apply(env.state))
|
|
require.NoError(env.state.Commit())
|
|
}
|
|
|
|
func defaultConfig(f upgradetest.Fork) *config.Internal {
|
|
upgrades := upgradetest.GetConfigWithUpgradeTime(f, time.Time{})
|
|
// This package neglects fork ordering
|
|
upgradetest.SetTimesTo(
|
|
&upgrades,
|
|
min(f, upgradetest.ApricotPhase5),
|
|
genesistest.DefaultValidatorEndTime,
|
|
)
|
|
|
|
return &config.Internal{
|
|
Chains: chains.TestManager,
|
|
UptimeLockedCalculator: uptime.NewLockedCalculator(),
|
|
Validators: validators.NewManager(),
|
|
MinValidatorStake: 5 * constants.MilliLux,
|
|
MaxValidatorStake: 500 * constants.MilliLux,
|
|
MinDelegatorStake: 1 * constants.MilliLux,
|
|
MinStakeDuration: defaultMinStakingDuration,
|
|
MaxStakeDuration: defaultMaxStakingDuration,
|
|
RewardConfig: reward.Config{
|
|
MaxConsumptionRate: .12 * reward.PercentDenominator,
|
|
MinConsumptionRate: .10 * reward.PercentDenominator,
|
|
MintingPeriod: 365 * 24 * time.Hour,
|
|
SupplyCap: 720 * constants.MegaLux,
|
|
},
|
|
UpgradeConfig: upgrades,
|
|
}
|
|
}
|
|
|
|
func defaultClock() *mockable.Clock {
|
|
// set time after upgrade.Default.BanffTime (and before nextStakerTime)
|
|
clk := &mockable.Clock{}
|
|
clk.Set(genesistest.DefaultValidatorStartTime)
|
|
return clk
|
|
}
|
|
|
|
type fxVMInt struct {
|
|
registry codec.Registry
|
|
clk *mockable.Clock
|
|
log log.Logger
|
|
}
|
|
|
|
func (fvi *fxVMInt) CodecRegistry() codec.Registry {
|
|
return fvi.registry
|
|
}
|
|
|
|
func (fvi *fxVMInt) Clock() *mockable.Clock {
|
|
return fvi.clk
|
|
}
|
|
|
|
func (fvi *fxVMInt) Logger() log.Logger {
|
|
return fvi.log
|
|
}
|
|
|
|
func defaultFx(t *testing.T, clk *mockable.Clock, log log.Logger, isBootstrapped bool) fx.Fx {
|
|
require := require.New(t)
|
|
|
|
fxVMInt := &fxVMInt{
|
|
registry: linearcodec.NewDefault(),
|
|
clk: clk,
|
|
log: log,
|
|
}
|
|
res := &secp256k1fx.Fx{}
|
|
require.NoError(res.Initialize(fxVMInt))
|
|
if isBootstrapped {
|
|
require.NoError(res.Bootstrapped())
|
|
}
|
|
return res
|
|
}
|