Update go.mod module line, all require/replace directives, and every .go import path from forge.lthn.ai/core/go-blockchain to dappco.re/go/core/blockchain. Add replace directives to bridge dappco.re paths to existing forge.lthn.ai registry during migration. Update CLAUDE.md, README, and docs to reflect the new module path. Co-Authored-By: Virgil <virgil@lethean.io>
133 lines
4.5 KiB
Go
133 lines
4.5 KiB
Go
// 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")
|
|
}
|
|
}
|