fix(consensus): enforce tx versions across fork eras
Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
be99c5e93a
commit
c1b68523c6
2 changed files with 43 additions and 13 deletions
|
|
@ -75,26 +75,32 @@ func ValidateTransaction(tx *types.Transaction, txBlob []byte, forks []config.Ha
|
||||||
// checkTxVersion validates that the transaction version is appropriate for the
|
// checkTxVersion validates that the transaction version is appropriate for the
|
||||||
// current hardfork era.
|
// current hardfork era.
|
||||||
//
|
//
|
||||||
// After HF5: transaction version must be exactly VersionPostHF5 (3) and the
|
// Pre-HF4: regular transactions must use version 1.
|
||||||
// embedded hardfork_id must match the active hardfork version.
|
// HF4 era: regular transactions must use version 2.
|
||||||
// Before HF5: transaction version 3 is rejected (too early).
|
// 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 {
|
func checkTxVersion(tx *types.Transaction, forks []config.HardFork, height uint64) error {
|
||||||
hf5Active := config.IsHardForkActive(forks, config.HF5, height)
|
hf5Active := config.IsHardForkActive(forks, config.HF5, height)
|
||||||
|
hf4Active := config.IsHardForkActive(forks, config.HF4Zarcanum, height)
|
||||||
currentFork := config.VersionAtHeight(forks, 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",
|
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)
|
ErrTxVersionInvalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hf5Active && tx.Version >= types.VersionPostHF5 {
|
if tx.Version >= types.VersionPostHF5 && tx.HardforkID != currentFork {
|
||||||
return coreerr.E("checkTxVersion",
|
|
||||||
fmt.Sprintf("version %d not allowed before HF5 at height %d", tx.Version, height),
|
|
||||||
ErrTxVersionInvalid)
|
|
||||||
}
|
|
||||||
|
|
||||||
if hf5Active && tx.HardforkID != currentFork {
|
|
||||||
return coreerr.E("checkTxVersion",
|
return coreerr.E("checkTxVersion",
|
||||||
fmt.Sprintf("hardfork id %d does not match active fork %d at height %d", tx.HardforkID, currentFork, height),
|
fmt.Sprintf("hardfork id %d does not match active fork %d at height %d", tx.HardforkID, currentFork, height),
|
||||||
ErrTxVersionInvalid)
|
ErrTxVersionInvalid)
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,18 @@ func TestCheckTxVersion_Bad(t *testing.T) {
|
||||||
forks []config.HardFork
|
forks []config.HardFork
|
||||||
height uint64
|
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 transaction after HF5 — must be v3.
|
||||||
{"v2_after_hf5", validV2Tx(), config.TestnetForks, 250},
|
{"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 transaction after HF5 with wrong hardfork id.
|
||||||
{"v3_after_hf5_wrong_hardfork", func() *types.Transaction {
|
{"v3_after_hf5_wrong_hardfork", func() *types.Transaction {
|
||||||
tx := validV3Tx()
|
tx := validV3Tx()
|
||||||
|
|
@ -108,9 +118,23 @@ func TestCheckTxVersion_Bad(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckTxVersion_Ugly(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).
|
// v3 at exact HF5 activation boundary (height 201 on testnet, HF5.Height=200).
|
||||||
tx := validV3Tx()
|
tx := validV3Tx()
|
||||||
err := checkTxVersion(tx, config.TestnetForks, 201)
|
err = checkTxVersion(tx, config.TestnetForks, 201)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("v3 at HF5 activation boundary should be valid: %v", err)
|
t.Errorf("v3 at HF5 activation boundary should be valid: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue