fix(consensus): tighten fork-era type gating
Reject Zarcanum inputs and outputs before HF4 instead of letting unsupported combinations pass semantic validation. Also restrict PoS miner stake inputs to txin_to_key pre-HF4 and txin_zc post-HF4, with regression coverage for the affected paths. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
95edac1d15
commit
bb941ebcc5
4 changed files with 86 additions and 5 deletions
|
|
@ -112,12 +112,13 @@ func ValidateMinerTx(tx *types.Transaction, height uint64, forks []config.HardFo
|
|||
switch tx.Vin[1].(type) {
|
||||
case types.TxInputToKey:
|
||||
// Pre-HF4 PoS.
|
||||
default:
|
||||
case types.TxInputZC:
|
||||
hf4Active := config.IsHardForkActive(forks, config.HF4Zarcanum, height)
|
||||
if !hf4Active {
|
||||
return coreerr.E("ValidateMinerTx", "invalid PoS stake input type", ErrMinerTxInputs)
|
||||
}
|
||||
// Post-HF4: accept ZC inputs.
|
||||
default:
|
||||
return coreerr.E("ValidateMinerTx", "invalid PoS stake input type", ErrMinerTxInputs)
|
||||
}
|
||||
} else {
|
||||
return coreerr.E("ValidateMinerTx", fmt.Sprintf("%d inputs (expected 1 or 2)", len(tx.Vin)), ErrMinerTxInputs)
|
||||
|
|
|
|||
|
|
@ -126,6 +126,36 @@ func TestValidateMinerTx_Good_PoS(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateMinerTx_Good_PoS_ZCAfterHF4(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPostHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputGenesis{Height: 101},
|
||||
types.TxInputZC{KeyImage: types.KeyImage{1}},
|
||||
},
|
||||
Vout: []types.TxOutput{
|
||||
types.TxOutputBare{Amount: config.BlockReward, Target: types.TxOutToKey{Key: types.PublicKey{1}}},
|
||||
},
|
||||
}
|
||||
err := ValidateMinerTx(tx, 101, config.TestnetForks)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateMinerTx_Bad_PoS_UnsupportedStakeInput(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPostHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputGenesis{Height: 101},
|
||||
types.TxInputHTLC{Amount: 1, KeyImage: types.KeyImage{1}},
|
||||
},
|
||||
Vout: []types.TxOutput{
|
||||
types.TxOutputBare{Amount: config.BlockReward, Target: types.TxOutToKey{Key: types.PublicKey{1}}},
|
||||
},
|
||||
}
|
||||
err := ValidateMinerTx(tx, 101, config.TestnetForks)
|
||||
assert.ErrorIs(t, err, ErrMinerTxInputs)
|
||||
}
|
||||
|
||||
func TestValidateMinerTx_Version_Good(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -128,11 +128,12 @@ func checkInputTypes(tx *types.Transaction, hf1Active, hf4Active bool) error {
|
|||
if !hf1Active {
|
||||
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF1", vin.InputType()), ErrInvalidInputType)
|
||||
}
|
||||
default:
|
||||
// Future types (ZC) — accept if HF4+.
|
||||
case types.TxInputZC:
|
||||
if !hf4Active {
|
||||
return coreerr.E("checkInputTypes", fmt.Sprintf("tag %d pre-HF4", vin.InputType()), ErrInvalidInputType)
|
||||
}
|
||||
default:
|
||||
return coreerr.E("checkInputTypes", fmt.Sprintf("unsupported input type %T", vin), ErrInvalidInputType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -170,7 +171,9 @@ func checkOutputs(tx *types.Transaction, hf1Active, hf4Active bool) error {
|
|||
return coreerr.E("checkOutputs", fmt.Sprintf("output %d: unsupported target %T", i, o.Target), ErrInvalidOutput)
|
||||
}
|
||||
case types.TxOutputZarcanum:
|
||||
// Validated by proof verification.
|
||||
if !hf4Active {
|
||||
return coreerr.E("checkOutputs", fmt.Sprintf("output %d: Zarcanum output pre-HF4", i), ErrInvalidOutput)
|
||||
}
|
||||
default:
|
||||
return coreerr.E("checkOutputs", fmt.Sprintf("output %d: unsupported output type %T", i, vout), ErrInvalidOutput)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,6 +244,53 @@ func TestCheckOutputs_MultisigTargetPostHF1_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCheckInputTypes_ZCPreHF4_Bad(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputZC{KeyImage: types.KeyImage{1}},
|
||||
},
|
||||
Vout: []types.TxOutput{
|
||||
types.TxOutputBare{Amount: 90, Target: types.TxOutToKey{Key: types.PublicKey{1}}},
|
||||
types.TxOutputBare{Amount: 1, Target: types.TxOutToKey{Key: types.PublicKey{2}}},
|
||||
},
|
||||
}
|
||||
blob := make([]byte, 100)
|
||||
err := ValidateTransaction(tx, blob, config.MainnetForks, 5000)
|
||||
assert.ErrorIs(t, err, ErrInvalidInputType)
|
||||
}
|
||||
|
||||
func TestCheckOutputs_ZarcanumPreHF4_Bad(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputToKey{Amount: 100, KeyImage: types.KeyImage{1}},
|
||||
},
|
||||
Vout: []types.TxOutput{
|
||||
types.TxOutputZarcanum{StealthAddress: types.PublicKey{1}},
|
||||
},
|
||||
}
|
||||
blob := make([]byte, 100)
|
||||
err := ValidateTransaction(tx, blob, config.MainnetForks, 5000)
|
||||
assert.ErrorIs(t, err, ErrInvalidOutput)
|
||||
}
|
||||
|
||||
func TestCheckOutputs_ZarcanumPostHF4_Good(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPostHF4,
|
||||
Vin: []types.TxInput{
|
||||
types.TxInputZC{KeyImage: types.KeyImage{1}},
|
||||
},
|
||||
Vout: []types.TxOutput{
|
||||
types.TxOutputZarcanum{StealthAddress: types.PublicKey{1}},
|
||||
types.TxOutputZarcanum{StealthAddress: types.PublicKey{2}},
|
||||
},
|
||||
}
|
||||
blob := make([]byte, 100)
|
||||
err := ValidateTransaction(tx, blob, config.TestnetForks, 150)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCheckOutputs_MissingTarget_Bad(t *testing.T) {
|
||||
tx := &types.Transaction{
|
||||
Version: types.VersionPreHF4,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue