Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions services/validator/TxValidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,16 +319,24 @@ func isStandardInputScript(script *bscript.Script, blockHeight uint32, uahfHeigh
func (tv *TxValidator) checkOutputs(tx *bt.Tx, blockHeight uint32, validationOptions *Options) error {
total := uint64(0)

// Note: We use > instead of >= to exclude the Genesis activation block itself
// because transactions in block 620538 were created before Genesis rules existed
isGenesisActivated := blockHeight > tv.settings.ChainCfgParams.GenesisActivationHeight

for index, output := range tx.Outputs {
// Check P2SH output after genesis activation
if !validationOptions.SkipPolicyChecks && isGenesisActivated && output.LockingScript.IsP2SH() {
// See https://github.com/bitcoin-sv/teranode/issues/4333
return errors.NewTxInvalidError("transaction output %d is p2sh after genesis activation", index)
}

if output.Satoshis > MaxSatoshis {
return errors.NewTxInvalidError("transaction output %d satoshis is invalid", index)
}

// Check dust limit after genesis activation
// Note: We use > instead of >= to exclude the Genesis activation block itself
// because transactions in block 620538 were created before Genesis rules existed
// Dust checks are policy rules, not consensus rules - they only apply to mempool/relay
if !validationOptions.SkipPolicyChecks && blockHeight > tv.settings.ChainCfgParams.GenesisActivationHeight {
if !validationOptions.SkipPolicyChecks && isGenesisActivated {
// Only enforce dust limit for spendable outputs when RequireStandard is true
if tv.settings.ChainCfgParams.RequireStandard && output.Satoshis < DustLimit && !isUnspendableOutput(output.LockingScript) {
return errors.NewTxInvalidError("zero-satoshi outputs require 'OP_FALSE OP_RETURN' prefix")
Expand Down
32 changes: 32 additions & 0 deletions services/validator/TxValidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,38 @@ func Test_MinFeePolicy(t *testing.T) {
}
}

func TestCheckP2SHOutput(t *testing.T) {
tSettings := test.CreateBaseTestSettings(t)
tSettings.ChainCfgParams.RequireStandard = true

txValidator := NewTxValidator(ulogger.TestLogger{}, tSettings)

// See https://github.com/bitcoin-sv/teranode/issues/4333
txP2SH, err := bt.NewTxFromString("020000000000000000ef01e0d8bc7aae870d67eaf3021492735637ddae403feb7914fb739a53872a82d301000000006a473044022041215b9ac965ce93684340d86d74df5ccf2d0910f36173a9d691e8405b37fd400220300ab0376d9d75542eaaffb4fe1eead267f0ac537ae13a4349506274978066f7412103afe4a8eb7f3f69757235bb8db804a01156af9d1cace07af534ca9be7f4928a5effffffffacc88203000000001976a9140533653ad7e12be8ee8151bc586f04bf859ae4d788ac0267307e03000000001976a9140533653ad7e12be8ee8151bc586f04bf859ae4d788ace09304000000000017a914496164f9f2e373628c5cc0a5895d995aaf3bec658700000000")
require.NoError(t, err)

// At Genesis activation height, p2sh should not be rejected
err = txValidator.ValidateTransaction(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight-1, nil, &Options{})
require.NoError(t, err)

err = txValidator.checkOutputs(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight-1, &Options{})
require.NoError(t, err)

// At Genesis activation height, p2sh should be rejected
err = txValidator.ValidateTransaction(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight, nil, &Options{})
require.Error(t, err)

err = txValidator.checkOutputs(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight, &Options{})
require.Error(t, err)

// At Genesis activation height, with skip policy check, p2sh should be accepted
err = txValidator.ValidateTransaction(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight, nil, &Options{SkipPolicyChecks: true})
require.NoError(t, err)

err = txValidator.checkOutputs(txP2SH, tSettings.ChainCfgParams.GenesisActivationHeight, &Options{SkipPolicyChecks: true})
require.NoError(t, err)
}

func TestCheckFees(t *testing.T) {
tSettings := test.CreateBaseTestSettings(t)

Expand Down
Loading