dcrd/internal/blockchain/error_test.go
Dave Collins e22c50a2a5
blockchain: Reject params with blank choice id.
This adds an additional validation check to the chain initialization
process to validate that none of the IDs of the choices in the given
chain params are blank.

It also adds a test for the new validation logic.
2023-03-13 13:17:10 -05:00

352 lines
13 KiB
Go

// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2015-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"errors"
"io"
"testing"
)
// TestErrorKindStringer tests the stringized output for the ErrorKind type.
func TestErrorKindStringer(t *testing.T) {
tests := []struct {
in ErrorKind
want string
}{
{ErrDuplicateBlock, "ErrDuplicateBlock"},
{ErrMissingParent, "ErrMissingParent"},
{ErrNoBlockData, "ErrNoBlockData"},
{ErrBlockTooBig, "ErrBlockTooBig"},
{ErrWrongBlockSize, "ErrWrongBlockSize"},
{ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
{ErrBadStakeVersion, "ErrBadStakeVersion"},
{ErrInvalidTime, "ErrInvalidTime"},
{ErrTimeTooOld, "ErrTimeTooOld"},
{ErrTimeTooNew, "ErrTimeTooNew"},
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"},
{ErrHighHash, "ErrHighHash"},
{ErrBadMerkleRoot, "ErrBadMerkleRoot"},
{ErrBadCommitmentRoot, "ErrBadCommitmentRoot"},
{ErrForkTooOld, "ErrForkTooOld"},
{ErrBadMaxDiffCheckpoint, "ErrBadMaxDiffCheckpoint"},
{ErrNoTransactions, "ErrNoTransactions"},
{ErrNoTxInputs, "ErrNoTxInputs"},
{ErrNoTxOutputs, "ErrNoTxOutputs"},
{ErrTxTooBig, "ErrTxTooBig"},
{ErrBadTxOutValue, "ErrBadTxOutValue"},
{ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
{ErrTxVersionTooHigh, "ErrTxVersionTooHigh"},
{ErrBadTxInput, "ErrBadTxInput"},
{ErrScriptVersionTooHigh, "ErrScriptVersionTooHigh"},
{ErrMissingTxOut, "ErrMissingTxOut"},
{ErrUnfinalizedTx, "ErrUnfinalizedTx"},
{ErrDuplicateTx, "ErrDuplicateTx"},
{ErrOverwriteTx, "ErrOverwriteTx"},
{ErrImmatureSpend, "ErrImmatureSpend"},
{ErrSpendTooHigh, "ErrSpendTooHigh"},
{ErrBadFees, "ErrBadFees"},
{ErrTooManySigOps, "ErrTooManySigOps"},
{ErrFirstTxNotCoinbase, "ErrFirstTxNotCoinbase"},
{ErrCoinbaseHeight, "ErrCoinbaseHeight"},
{ErrMultipleCoinbases, "ErrMultipleCoinbases"},
{ErrStakeTxInRegularTree, "ErrStakeTxInRegularTree"},
{ErrRegTxInStakeTree, "ErrRegTxInStakeTree"},
{ErrBadCoinbaseScriptLen, "ErrBadCoinbaseScriptLen"},
{ErrBadCoinbaseValue, "ErrBadCoinbaseValue"},
{ErrBadCoinbaseOutpoint, "ErrBadCoinbaseOutpoint"},
{ErrBadCoinbaseFraudProof, "ErrBadCoinbaseFraudProof"},
{ErrBadCoinbaseAmountIn, "ErrBadCoinbaseAmountIn"},
{ErrBadStakebaseAmountIn, "ErrBadStakebaseAmountIn"},
{ErrBadStakebaseScriptLen, "ErrBadStakebaseScriptLen"},
{ErrBadStakebaseScrVal, "ErrBadStakebaseScrVal"},
{ErrScriptMalformed, "ErrScriptMalformed"},
{ErrScriptValidation, "ErrScriptValidation"},
{ErrNotEnoughStake, "ErrNotEnoughStake"},
{ErrStakeBelowMinimum, "ErrStakeBelowMinimum"},
{ErrNonstandardStakeTx, "ErrNonstandardStakeTx"},
{ErrNotEnoughVotes, "ErrNotEnoughVotes"},
{ErrTooManyVotes, "ErrTooManyVotes"},
{ErrFreshStakeMismatch, "ErrFreshStakeMismatch"},
{ErrTooManySStxs, "ErrTooManySStxs"},
{ErrInvalidEarlyStakeTx, "ErrInvalidEarlyStakeTx"},
{ErrTicketUnavailable, "ErrTicketUnavailable"},
{ErrVotesOnWrongBlock, "ErrVotesOnWrongBlock"},
{ErrVotesMismatch, "ErrVotesMismatch"},
{ErrIncongruentVotebit, "ErrIncongruentVotebit"},
{ErrInvalidSSRtx, "ErrInvalidSSRtx"},
{ErrRevocationsMismatch, "ErrRevocationsMismatch"},
{ErrTooManyRevocations, "ErrTooManyRevocations"},
{ErrTicketCommitment, "ErrTicketCommitment"},
{ErrInvalidVoteInput, "ErrInvalidVoteInput"},
{ErrBadNumPayees, "ErrBadNumPayees"},
{ErrBadPayeeScriptVersion, "ErrBadPayeeScriptVersion"},
{ErrBadPayeeScriptType, "ErrBadPayeeScriptType"},
{ErrMismatchedPayeeHash, "ErrMismatchedPayeeHash"},
{ErrBadPayeeValue, "ErrBadPayeeValue"},
{ErrSSGenSubsidy, "ErrSSGenSubsidy"},
{ErrImmatureTicketSpend, "ErrImmatureTicketSpend"},
{ErrTicketInputScript, "ErrTicketInputScript"},
{ErrInvalidRevokeInput, "ErrInvalidRevokeInput"},
{ErrTxSStxOutSpend, "ErrTxSStxOutSpend"},
{ErrRegTxCreateStakeOut, "ErrRegTxCreateStakeOut"},
{ErrInvalidFinalState, "ErrInvalidFinalState"},
{ErrPoolSize, "ErrPoolSize"},
{ErrForceReorgSameBlock, "ErrForceReorgSameBlock"},
{ErrForceReorgWrongChain, "ErrForceReorgWrongChain"},
{ErrForceReorgMissingChild, "ErrForceReorgMissingChild"},
{ErrBadStakebaseValue, "ErrBadStakebaseValue"},
{ErrStakeFees, "ErrStakeFees"},
{ErrNoStakeTx, "ErrNoStakeTx"},
{ErrBadBlockHeight, "ErrBadBlockHeight"},
{ErrBlockOneTx, "ErrBlockOneTx"},
{ErrBlockOneInputs, "ErrBlockOneInputs"},
{ErrBlockOneOutputs, "ErrBlockOneOutputs"},
{ErrNoTreasury, "ErrNoTreasury"},
{ErrExpiredTx, "ErrExpiredTx"},
{ErrExpiryTxSpentEarly, "ErrExpiryTxSpentEarly"},
{ErrFraudAmountIn, "ErrFraudAmountIn"},
{ErrFraudBlockHeight, "ErrFraudBlockHeight"},
{ErrFraudBlockIndex, "ErrFraudBlockIndex"},
{ErrZeroValueOutputSpend, "ErrZeroValueOutputSpend"},
{ErrInvalidEarlyVoteBits, "ErrInvalidEarlyVoteBits"},
{ErrInvalidEarlyFinalState, "ErrInvalidEarlyFinalState"},
{ErrKnownInvalidBlock, "ErrKnownInvalidBlock"},
{ErrInvalidAncestorBlock, "ErrInvalidAncestorBlock"},
{ErrInvalidTemplateParent, "ErrInvalidTemplateParent"},
{ErrUnknownPiKey, "ErrUnknownPiKey"},
{ErrInvalidPiSignature, "ErrInvalidPiSignature"},
{ErrInvalidTVoteWindow, "ErrInvalidTVoteWindow"},
{ErrNotTVI, "ErrNotTVI"},
{ErrInvalidTSpendWindow, "ErrInvalidTSpendWindow"},
{ErrNotEnoughTSpendVotes, "ErrNotEnoughTSpendVotes"},
{ErrInvalidTSpendValueIn, "ErrInvalidTSpendValueIn"},
{ErrTSpendExists, "ErrTSpendExists"},
{ErrInvalidExpenditure, "ErrInvalidExpenditure"},
{ErrFirstTxNotTreasurybase, "ErrFirstTxNotTreasurybase"},
{ErrBadTreasurybaseOutpoint, "ErrBadTreasurybaseOutpoint"},
{ErrBadTreasurybaseFraudProof, "ErrBadTreasurybaseFraudProof"},
{ErrBadTreasurybaseScriptLen, "ErrBadTreasurybaseScriptLen"},
{ErrTreasurybaseTxNotOpReturn, "ErrTreasurybaseTxNotOpReturn"},
{ErrTreasurybaseHeight, "ErrTreasurybaseHeight"},
{ErrMultipleTreasurybases, "ErrMultipleTreasurybases"},
{ErrInvalidTreasurybaseTxOutputs, "ErrInvalidTreasurybaseTxOutputs"},
{ErrInvalidTreasurybaseVersion, "ErrInvalidTreasurybaseVersion"},
{ErrInvalidTreasurybaseScript, "ErrInvalidTreasurybaseScript"},
{ErrMultipleTreasurybases, "ErrMultipleTreasurybases"},
{ErrBadTreasurybaseAmountIn, "ErrBadTreasurybaseAmountIn"},
{ErrBadTSpendOutpoint, "ErrBadTSpendOutpoint"},
{ErrBadTSpendFraudProof, "ErrBadTSpendFraudProof"},
{ErrBadTSpendScriptLen, "ErrBadTSpendScriptLen"},
{ErrInvalidTAddChange, "ErrInvalidTAddChange"},
{ErrTooManyTAdds, "ErrTooManyTAdds"},
{ErrTicketExhaustion, "ErrTicketExhaustion"},
{ErrDBTooOldToUpgrade, "ErrDBTooOldToUpgrade"},
{ErrUnknownBlock, "ErrUnknownBlock"},
{ErrNoFilter, "ErrNoFilter"},
{ErrNoTreasuryBalance, "ErrNoTreasuryBalance"},
{ErrInvalidateGenesisBlock, "ErrInvalidateGenesisBlock"},
{ErrSerializeHeader, "ErrSerializeHeader"},
{ErrUtxoBackend, "ErrUtxoBackend"},
{ErrUtxoBackendCorruption, "ErrUtxoBackendCorruption"},
{ErrUtxoBackendNotOpen, "ErrUtxoBackendNotOpen"},
{ErrUtxoBackendTxClosed, "ErrUtxoBackendTxClosed"},
{ErrInvalidRevocationTxVersion, "ErrInvalidRevocationTxVersion"},
{ErrNoExpiredTicketRevocation, "ErrNoExpiredTicketRevocation"},
{ErrNoMissedTicketRevocation, "ErrNoMissedTicketRevocation"},
{ErrUnknownDeploymentID, "ErrUnknownDeploymentID"},
{ErrUnknownDeploymentVersion, "ErrUnknownDeploymentVersion"},
{ErrDuplicateDeployment, "ErrDuplicateDeployment"},
{ErrUnknownDeploymentChoice, "ErrUnknownDeploymentChoice"},
{ErrDeploymentBadMask, "ErrDeploymentBadMask"},
{ErrDeploymentTooManyChoices, "ErrDeploymentTooManyChoices"},
{ErrDeploymentMissingChoiceID, "ErrDeploymentMissingChoiceID"},
{ErrDeploymentBadChoiceBits, "ErrDeploymentBadChoiceBits"},
{ErrDeploymentNonExclusiveFlags, "ErrDeploymentNonExclusiveFlags"},
{ErrDeploymentDuplicateChoice, "ErrDeploymentDuplicateChoice"},
{ErrDeploymentMissingAbstain, "ErrDeploymentMissingAbstain"},
{ErrDeploymentTooManyAbstain, "ErrDeploymentTooManyAbstain"},
{ErrDeploymentMissingNo, "ErrDeploymentMissingNo"},
{ErrDeploymentTooManyNo, "ErrDeploymentTooManyNo"},
{ErrDeploymentChoiceAbstain, "ErrDeploymentChoiceAbstain"},
{ErrForcedMainNetChoice, "ErrForcedMainNetChoice"},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("#%d: got: %s want: %s", i, result, test.want)
continue
}
}
}
// TestRuleError tests the error output for the RuleError type.
func TestRuleError(t *testing.T) {
tests := []struct {
in RuleError
want string
}{{
RuleError{Description: "duplicate block"},
"duplicate block",
}, {
RuleError{Description: "human-readable error"},
"human-readable error",
}}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("#%d: got: %s want: %s", i, result, test.want)
continue
}
}
}
// TestContextError tests the error output for the ContextError type.
func TestContextError(t *testing.T) {
tests := []struct {
in ContextError
want string
}{{
ContextError{Description: "duplicate block"},
"duplicate block",
}, {
ContextError{Description: "human-readable error"},
"human-readable error",
}}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
result := test.in.Error()
if result != test.want {
t.Errorf("#%d: got: %s want: %s", i, result, test.want)
continue
}
}
}
// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being
// a specific error kind via errors.Is and unwrapped via errors.As.
func TestErrorKindIsAs(t *testing.T) {
tests := []struct {
name string
err error
target error
wantMatch bool
wantAs ErrorKind
}{{
name: "ErrDuplicateBlock == ErrDuplicateBlock",
err: ErrDuplicateBlock,
target: ErrDuplicateBlock,
wantMatch: true,
wantAs: ErrDuplicateBlock,
}, {
name: "RuleError.ErrDuplicateBlock == ErrDuplicateBlock",
err: ruleError(ErrDuplicateBlock, ""),
target: ErrDuplicateBlock,
wantMatch: true,
wantAs: ErrDuplicateBlock,
}, {
name: "RuleError.ErrDuplicateBlock == RuleError.ErrDuplicateBlock",
err: ruleError(ErrDuplicateBlock, ""),
target: ruleError(ErrDuplicateBlock, ""),
wantMatch: true,
wantAs: ErrDuplicateBlock,
}, {
name: "ErrDuplicateBlock != ErrMissingParent",
err: ErrDuplicateBlock,
target: ErrMissingParent,
wantMatch: false,
wantAs: ErrDuplicateBlock,
}, {
name: "RuleError.ErrDuplicateBlock != ErrMissingParent",
err: ruleError(ErrDuplicateBlock, ""),
target: ErrMissingParent,
wantMatch: false,
wantAs: ErrDuplicateBlock,
}, {
name: "ErrDuplicateBlock != RuleError.ErrMissingParent",
err: ErrDuplicateBlock,
target: ruleError(ErrMissingParent, ""),
wantMatch: false,
wantAs: ErrDuplicateBlock,
}, {
name: "RuleError.ErrDuplicateBlock != RuleError.ErrMissingParent",
err: ruleError(ErrDuplicateBlock, ""),
target: ruleError(ErrMissingParent, ""),
wantMatch: false,
wantAs: ErrDuplicateBlock,
}, {
name: "RuleError.ErrDuplicateBlock != io.EOF",
err: ruleError(ErrDuplicateBlock, ""),
target: io.EOF,
wantMatch: false,
wantAs: ErrDuplicateBlock,
}, {
name: "ContextError.ErrDBTooOldToUpgrade == ErrDBTooOldToUpgrade",
err: contextError(ErrDBTooOldToUpgrade, ""),
target: ErrDBTooOldToUpgrade,
wantMatch: true,
wantAs: ErrDBTooOldToUpgrade,
}, {
name: "ContextError.ErrDBTooOldToUpgrade == ContextError.ErrDBTooOldToUpgrade",
err: contextError(ErrDBTooOldToUpgrade, ""),
target: contextError(ErrDBTooOldToUpgrade, ""),
wantMatch: true,
wantAs: ErrDBTooOldToUpgrade,
}, {
name: "ContextError.ErrDBTooOldToUpgrade != ErrMissingParent",
err: contextError(ErrDBTooOldToUpgrade, ""),
target: ErrMissingParent,
wantMatch: false,
wantAs: ErrDBTooOldToUpgrade,
}, {
name: "ErrDBTooOldToUpgrade != ContextError.ErrMissingParent",
err: ErrDBTooOldToUpgrade,
target: contextError(ErrMissingParent, ""),
wantMatch: false,
wantAs: ErrDBTooOldToUpgrade,
}, {
name: "ContextError.ErrDBTooOldToUpgrade != ContextError.ErrMissingParent",
err: contextError(ErrDBTooOldToUpgrade, ""),
target: contextError(ErrMissingParent, ""),
wantMatch: false,
wantAs: ErrDBTooOldToUpgrade,
}, {
name: "ContextError.ErrDBTooOldToUpgrade != io.EOF",
err: contextError(ErrDBTooOldToUpgrade, ""),
target: io.EOF,
wantMatch: false,
wantAs: ErrDBTooOldToUpgrade,
}}
for _, test := range tests {
// Ensure the error matches or not depending on the expected result.
result := errors.Is(test.err, test.target)
if result != test.wantMatch {
t.Errorf("%s: incorrect error identification -- got %v, want %v",
test.name, result, test.wantMatch)
continue
}
// Ensure the underlying error kind can be unwrapped and is the
// expected kind.
var kind ErrorKind
if !errors.As(test.err, &kind) {
t.Errorf("%s: unable to unwrap to error kind", test.name)
continue
}
if kind != test.wantAs {
t.Errorf("%s: unexpected unwrapped error kind -- got %v, want %v",
test.name, kind, test.wantAs)
continue
}
}
}