// Copyright (c) 2017-2026 Lethean (https://lt.hn) // // Licensed under the European Union Public Licence (EUPL) version 1.2. // SPDX-License-Identifier: EUPL-1.2 //go:build !integration package consensus import ( "testing" "dappco.re/go/core/blockchain/config" "dappco.re/go/core/blockchain/types" ) func TestIsPreHardforkFreeze_Good(t *testing.T) { // Testnet HF5 activates at heights > 200. // Freeze window: heights 141..200 (activation_height - period + 1 .. activation_height). // Note: HF5 activation height is 200, meaning HF5 is active at height > 200 = 201+. // The freeze applies for 60 blocks *before* the fork activates, so heights 141..200. tests := []struct { name string height uint64 want bool }{ {"well_before_freeze", 100, false}, {"just_before_freeze", 140, false}, {"first_freeze_block", 141, true}, {"mid_freeze", 170, true}, {"last_freeze_block", 200, true}, {"after_hf5_active", 201, false}, {"well_after_hf5", 300, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := IsPreHardforkFreeze(config.TestnetForks, config.HF5, tt.height) if got != tt.want { t.Errorf("IsPreHardforkFreeze(testnet, HF5, %d) = %v, want %v", tt.height, got, tt.want) } }) } } func TestIsPreHardforkFreeze_Bad(t *testing.T) { // Mainnet HF5 is at 999999999 — freeze window starts at 999999940. // At typical mainnet heights, no freeze. if IsPreHardforkFreeze(config.MainnetForks, config.HF5, 50000) { t.Error("should not be in freeze period at mainnet height 50000") } } func TestIsPreHardforkFreeze_Ugly(t *testing.T) { // Unknown fork version — never frozen. if IsPreHardforkFreeze(config.TestnetForks, 99, 150) { t.Error("unknown fork version should never trigger freeze") } // Fork at height 0 (HF0) — freeze period would be negative/underflow, // should return false. if IsPreHardforkFreeze(config.TestnetForks, config.HF0Initial, 0) { t.Error("fork at genesis should not trigger freeze") } } func TestValidateBlockFreeze_Good(t *testing.T) { // During freeze, coinbase transactions should still be accepted. // This test verifies that ValidateBlock does not reject a block // that only contains its miner transaction during the freeze window. // (ValidateBlock validates the miner tx; regular tx validation is // done separately per tx.) // // The freeze check applies to regular transactions via // ValidateTransactionInBlock, not to the miner tx itself. coinbaseTx := &types.Transaction{ Version: types.VersionPostHF4, Vin: []types.TxInput{types.TxInputGenesis{Height: 150}}, } _ = coinbaseTx // structural test — actual block validation needs more fields } func TestValidateTransactionInBlock_Good(t *testing.T) { // Outside freeze window — regular transaction accepted. tx := validV2Tx() blob := make([]byte, 100) err := ValidateTransactionInBlock(tx, blob, config.TestnetForks, 130) if err != nil { t.Errorf("expected no error outside freeze, got: %v", err) } } func TestValidateTransactionInBlock_Bad(t *testing.T) { // Inside freeze window — regular transaction rejected. tx := validV2Tx() blob := make([]byte, 100) err := ValidateTransactionInBlock(tx, blob, config.TestnetForks, 150) if err == nil { t.Error("expected ErrPreHardforkFreeze during freeze window") } } func TestValidateTransactionInBlock_Ugly(t *testing.T) { // Coinbase transaction during freeze — the freeze check itself should // not reject it (coinbase is exempt). The isCoinbase guard must pass. // Note: ValidateTransaction separately rejects txin_gen in regular txs, // but that is the expected path — coinbase txs are validated via // ValidateMinerTx, not ValidateTransaction. This test verifies the // freeze guard specifically exempts coinbase inputs. tx := &types.Transaction{ Version: types.VersionPostHF4, Vin: []types.TxInput{types.TxInputGenesis{Height: 150}}, Vout: []types.TxOutput{ types.TxOutputZarcanum{StealthAddress: types.PublicKey{1}}, types.TxOutputZarcanum{StealthAddress: types.PublicKey{2}}, }, } // Directly verify the freeze exemption — isCoinbase should return true, // and the freeze check should not trigger. if !isCoinbase(tx) { t.Fatal("expected coinbase transaction to be identified as coinbase") } if IsPreHardforkFreeze(config.TestnetForks, config.HF5, 150) { // Good — we are in the freeze window. Coinbase should still bypass. // The freeze check in ValidateTransactionInBlock gates on !isCoinbase, // so coinbase txs never hit ErrPreHardforkFreeze. } else { t.Fatal("expected height 150 to be in freeze window") } }