diff --git a/consensus/tx.go b/consensus/tx.go index 1584666..f7af120 100644 --- a/consensus/tx.go +++ b/consensus/tx.go @@ -75,26 +75,32 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha // checkTxVersion validates that the transaction version is appropriate for the // current hardfork era. // -// After HF5: transaction version must be exactly VersionPostHF5 (3) and the -// embedded hardfork_id must match the active hardfork version. -// Before HF5: transaction version 3 is rejected (too early). +// Pre-HF4: regular transactions must use version 1. +// HF4 era: regular transactions must use version 2. +// HF5+: transaction version must be exactly version 3 and the embedded +// hardfork_id must match the active hardfork version. func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint64) error { hf5Active := config.IsHardForkActive(forks, config.HF5, height) + hf4Active := config.IsHardForkActive(forks, config.HF4Zarcanum, height) currentFork := config.VersionAtHeight(forks, height) - if hf5Active && tx.Version != types.VersionPostHF5 { + var expectedVersion uint64 + switch { + case hf5Active: + expectedVersion = types.VersionPostHF5 + case hf4Active: + expectedVersion = types.VersionPostHF4 + default: + expectedVersion = types.VersionPreHF4 + } + + if tx.Version != expectedVersion { return coreerr.E("checkTxVersion", - fmt.Sprintf("version %d invalid after HF5 at height %d", tx.Version, height), + fmt.Sprintf("version %d invalid at height %d (expected %d)", tx.Version, height, expectedVersion), ErrTxVersionInvalid) } - if !hf5Active && tx.Version >= types.VersionPostHF5 { - return coreerr.E("checkTxVersion", - fmt.Sprintf("version %d not allowed before HF5 at height %d", tx.Version, height), - ErrTxVersionInvalid) - } - - if hf5Active && tx.HardforkID != currentFork { + if tx.Version >= types.VersionPostHF5 && tx.HardforkID != currentFork { return coreerr.E("checkTxVersion", fmt.Sprintf("hardfork id %d does not match active fork %d at height %d", tx.HardforkID, currentFork, height), ErrTxVersionInvalid) diff --git a/consensus/tx_version_test.go b/consensus/tx_version_test.go index b7678b9..e923abd 100644 --- a/consensus/tx_version_test.go +++ b/consensus/tx_version_test.go @@ -79,8 +79,18 @@ func TestCheckTxVersion_Bad(t *testing.T) { forks []config.HardFork height uint64 }{ + // v0 regular transaction before HF4 — must still be v1. + {"v0_before_hf4", func() *types.Transaction { + tx := validV1Tx() + tx.Version = types.VersionInitial + return tx + }(), config.MainnetForks, 5000}, + // v1 transaction after HF4 — must be v2. + {"v1_after_hf4", validV1Tx(), config.TestnetForks, 150}, // v2 transaction after HF5 — must be v3. {"v2_after_hf5", validV2Tx(), config.TestnetForks, 250}, + // v3 transaction after HF4 but before HF5 — too early. + {"v3_after_hf4_before_hf5", validV3Tx(), config.TestnetForks, 150}, // v3 transaction after HF5 with wrong hardfork id. {"v3_after_hf5_wrong_hardfork", func() *types.Transaction { tx := validV3Tx() @@ -108,9 +118,23 @@ func TestCheckTxVersion_Bad(t *testing.T) { } func TestCheckTxVersion_Ugly(t *testing.T) { + // v2 at exact HF4 activation boundary (height 101 on testnet, HF4.Height=100). + txHF4 := validV2Tx() + err := checkTxVersion(txHF4, config.TestnetForks, 101) + if err != nil { + t.Errorf("v2 at HF4 activation boundary should be valid: %v", err) + } + + // v1 at exact HF4 activation boundary should be rejected. + txPreHF4 := validV1Tx() + err = checkTxVersion(txPreHF4, config.TestnetForks, 101) + if err == nil { + t.Error("v1 at HF4 activation boundary should be rejected") + } + // v3 at exact HF5 activation boundary (height 201 on testnet, HF5.Height=200). tx := validV3Tx() - err := checkTxVersion(tx, config.TestnetForks, 201) + err = checkTxVersion(tx, config.TestnetForks, 201) if err != nil { t.Errorf("v3 at HF5 activation boundary should be valid: %v", err) }