dcrd/blockchain/error.go
Dave Collins b6d426241d blockchain: Rework to use new db interface.
This commit is the first stage of several that are planned to convert
the blockchain package into a concurrent safe package that will
ultimately allow support for multi-peer download and concurrent chain
processing.  The goal is to update btcd proper after each step so it can
take advantage of the enhancements as they are developed.

In addition to the aforementioned benefit, this staged approach has been
chosen since it is absolutely critical to maintain consensus.
Separating the changes into several stages makes it easier for reviewers
to logically follow what is happening and therefore helps prevent
consensus bugs.  Naturally there are significant automated tests to help
prevent consensus issues as well.

The main focus of this stage is to convert the blockchain package to use
the new database interface and implement the chain-related functionality
which it no longer handles.  It also aims to improve efficiency in
various areas by making use of the new database and chain capabilities.

The following is an overview of the chain changes:

- Update to use the new database interface
- Add chain-related functionality that the old database used to handle
  - Main chain structure and state
  - Transaction spend tracking
- Implement a new pruned unspent transaction output (utxo) set
  - Provides efficient direct access to the unspent transaction outputs
  - Uses a domain specific compression algorithm that understands the
    standard transaction scripts in order to significantly compress them
  - Removes reliance on the transaction index and paves the way toward
    eventually enabling block pruning
- Modify the New function to accept a Config struct instead of
  inidividual parameters
- Replace the old TxStore type with a new UtxoViewpoint type that makes
  use of the new pruned utxo set
- Convert code to treat the new UtxoViewpoint as a rolling view that is
  used between connects and disconnects to improve efficiency
- Make best chain state always set when the chain instance is created
  - Remove now unnecessary logic for dealing with unset best state
- Make all exported functions concurrent safe
  - Currently using a single chain state lock as it provides a straight
    forward and easy to review path forward however this can be improved
    with more fine grained locking
- Optimize various cases where full blocks were being loaded when only
  the header is needed to help reduce the I/O load
- Add the ability for callers to get a snapshot of the current best
  chain stats in a concurrent safe fashion
  - Does not block callers while new blocks are being processed
- Make error messages that reference transaction outputs consistently
  use <transaction hash>:<output index>
- Introduce a new AssertError type an convert internal consistency
  checks to use it
- Update tests and examples to reflect the changes
- Add a full suite of tests to ensure correct functionality of the new
  code

The following is an overview of the btcd changes:

- Update to use the new database and chain interfaces
- Temporarily remove all code related to the transaction index
- Temporarily remove all code related to the address index
- Convert all code that uses transaction stores to use the new utxo
  view
- Rework several calls that required the block manager for safe
  concurrency to use the chain package directly now that it is
  concurrent safe
- Change all calls to obtain the best hash to use the new best state
  snapshot capability from the chain package
- Remove workaround for limits on fetching height ranges since the new
  database interface no longer imposes them
- Correct the gettxout RPC handler to return the best chain hash as
  opposed the hash the txout was found in
- Optimize various RPC handlers:
  - Change several of the RPC handlers to use the new chain snapshot
    capability to avoid needlessly loading data
  - Update several handlers to use new functionality to avoid accessing
    the block manager so they are able to return the data without
    blocking when the server is busy processing blocks
  - Update non-verbose getblock to avoid deserialization and
    serialization overhead
  - Update getblockheader to request the block height directly from
    chain and only load the header
  - Update getdifficulty to use the new cached data from chain
  - Update getmininginfo to use the new cached data from chain
  - Update non-verbose getrawtransaction to avoid deserialization and
    serialization overhead
  - Update gettxout to use the new utxo store versus loading
    full transactions using the transaction index

The following is an overview of the utility changes:
- Update addblock to use the new database and chain interfaces
- Update findcheckpoint to use the new database and chain interfaces
- Remove the dropafter utility which is no longer supported

NOTE: The transaction index and address index will be reimplemented in
another commit.
2016-08-18 15:42:18 -04:00

552 lines
20 KiB
Go

// Copyright (c) 2014-2016 The btcsuite developers
// Copyright (c) 2015-2016 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 (
"fmt"
)
// AssertError identifies an error that indicates an internal code consistency
// issue and should be treated as a critical and unrecoverable error.
type AssertError string
// Error returns the assertion error as a huma-readable string and satisfies
// the error interface.
func (e AssertError) Error() string {
return "assertion failed: " + string(e)
}
// ErrorCode identifies a kind of error.
type ErrorCode int
// These constants are used to identify a specific RuleError.
const (
// ErrDuplicateBlock indicates a block with the same hash already
// exists.
ErrDuplicateBlock ErrorCode = iota
// ErrMissingParent indicates that the block was an orphan.
ErrMissingParent
// ErrBlockTooBig indicates the serialized block size exceeds the
// maximum allowed size.
ErrBlockTooBig
// ErrWrongBlockSize indicates that the block size from the header was
// not the actual serialized size of the block.
ErrWrongBlockSize
// ErrBlockVersionTooOld indicates the block version is too old and is
// no longer accepted since the majority of the network has upgraded
// to a newer version.
ErrBlockVersionTooOld
// ErrInvalidTime indicates the time in the passed block has a precision
// that is more than one second. The chain consensus rules require
// timestamps to have a maximum precision of one second.
ErrInvalidTime
// ErrTimeTooOld indicates the time is either before the median time of
// the last several blocks per the chain consensus rules or prior to the
// most recent checkpoint.
ErrTimeTooOld
// ErrTimeTooNew indicates the time is too far in the future as compared
// the current time.
ErrTimeTooNew
// ErrDifficultyTooLow indicates the difficulty for the block is lower
// than the difficulty required by the most recent checkpoint.
ErrDifficultyTooLow
// ErrUnexpectedDifficulty indicates specified bits do not align with
// the expected value either because it doesn't match the calculated
// valued based on difficulty regarted rules or it is out of the valid
// range.
ErrUnexpectedDifficulty
// ErrHighHash indicates the block does not hash to a value which is
// lower than the required target difficultly.
ErrHighHash
// ErrBadMerkleRoot indicates the calculated merkle root does not match
// the expected value.
ErrBadMerkleRoot
// ErrBadCheckpoint indicates a block that is expected to be at a
// checkpoint height does not match the expected one.
ErrBadCheckpoint
// ErrForkTooOld indicates a block is attempting to fork the block chain
// before the most recent checkpoint.
ErrForkTooOld
// ErrCheckpointTimeTooOld indicates a block has a timestamp before the
// most recent checkpoint.
ErrCheckpointTimeTooOld
// ErrNoTransactions indicates the block does not have a least one
// transaction. A valid block must have at least the coinbase
// transaction.
ErrNoTransactions
// ErrTooManyTransactions indicates the block has more transactions than
// are allowed.
ErrTooManyTransactions
// ErrNoTxInputs indicates a transaction does not have any inputs. A
// valid transaction must have at least one input.
ErrNoTxInputs
// ErrNoTxOutputs indicates a transaction does not have any outputs. A
// valid transaction must have at least one output.
ErrNoTxOutputs
// ErrTxTooBig indicates a transaction exceeds the maximum allowed size
// when serialized.
ErrTxTooBig
// ErrBadTxOutValue indicates an output value for a transaction is
// invalid in some way such as being out of range.
ErrBadTxOutValue
// ErrDuplicateTxInputs indicates a transaction references the same
// input more than once.
ErrDuplicateTxInputs
// ErrBadTxInput indicates a transaction input is invalid in some way
// such as referencing a previous transaction outpoint which is out of
// range or not referencing one at all.
ErrBadTxInput
// ErrMissingTx indicates a transaction referenced by an input is
// missing.
ErrMissingTx
// ErrUnfinalizedTx indicates a transaction has not been finalized.
// A valid block may only contain finalized transactions.
ErrUnfinalizedTx
// ErrDuplicateTx indicates a block contains an identical transaction
// (or at least two transactions which hash to the same value). A
// valid block may only contain unique transactions.
ErrDuplicateTx
// ErrOverwriteTx indicates a block contains a transaction that has
// the same hash as a previous transaction which has not been fully
// spent.
ErrOverwriteTx
// ErrImmatureSpend indicates a transaction is attempting to spend a
// coinbase that has not yet reached the required maturity.
ErrImmatureSpend
// ErrDoubleSpend indicates a transaction is attempting to spend coins
// that have already been spent.
ErrDoubleSpend
// ErrSpendTooHigh indicates a transaction is attempting to spend more
// value than the sum of all of its inputs.
ErrSpendTooHigh
// ErrBadFees indicates the total fees for a block are invalid due to
// exceeding the maximum possible value.
ErrBadFees
// ErrTooManySigOps indicates the total number of signature operations
// for a transaction or block exceed the maximum allowed limits.
ErrTooManySigOps
// ErrFirstTxNotCoinbase indicates the first transaction in a block
// is not a coinbase transaction.
ErrFirstTxNotCoinbase
// ErrCoinbaseHeight indicates that the encoded height in the coinbase
// is incorrect.
ErrCoinbaseHeight
// ErrMultipleCoinbases indicates a block contains more than one
// coinbase transaction.
ErrMultipleCoinbases
// ErrStakeTxInRegularTree indicates a stake transaction was found in
// the regular transaction tree.
ErrStakeTxInRegularTree
// ErrRegTxInStakeTree indicates that a regular transaction was found in
// the stake transaction tree.
ErrRegTxInStakeTree
// ErrBadCoinbaseScriptLen indicates the length of the signature script
// for a coinbase transaction is not within the valid range.
ErrBadCoinbaseScriptLen
// ErrBadCoinbaseValue indicates the amount of a coinbase value does
// not match the expected value of the subsidy plus the sum of all fees.
ErrBadCoinbaseValue
// ErrBadCoinbaseOutpoint indicates that the outpoint used by a coinbase
// as input was non-null.
ErrBadCoinbaseOutpoint
// ErrBadCoinbaseFraudProof indicates that the fraud proof for a coinbase
// input was non-null.
ErrBadCoinbaseFraudProof
// ErrBadCoinbaseAmountIn indicates that the AmountIn (=subsidy) for a
// coinbase input was incorrect.
ErrBadCoinbaseAmountIn
// ErrBadStakebaseAmountIn indicates that the AmountIn (=subsidy) for a
// stakebase input was incorrect.
ErrBadStakebaseAmountIn
// ErrBadStakebaseScriptLen indicates the length of the signature script
// for a stakebase transaction is not within the valid range.
ErrBadStakebaseScriptLen
// ErrBadStakevaseScrVal indicates the signature script for a stakebase
// transaction was not set to the network consensus value.
ErrBadStakevaseScrVal
// ErrScriptMalformed indicates a transaction script is malformed in
// some way. For example, it might be longer than the maximum allowed
// length or fail to parse.
ErrScriptMalformed
// ErrScriptValidation indicates the result of executing transaction
// script failed. The error covers any failure when executing scripts
// such signature verification failures and execution past the end of
// the stack.
ErrScriptValidation
// ErrNotEnoughStake indicates that there was for some SStx in a given block,
// the given SStx did not have enough stake to meet the network target.
ErrNotEnoughStake
// ErrStakeBelowMinimum indicates that for some SStx in a given block,
// the given SStx had an amount of stake below the minimum network target.
ErrStakeBelowMinimum
// ErrNonstandardStakeTx indicates that a block contained a stake tx that
// was not one of the allowed types of a stake transactions.
ErrNonstandardStakeTx
// ErrNotEnoughVotes indicates that a block contained less than a majority
// of voters.
ErrNotEnoughVotes
// ErrTooManyVotes indicates that a block contained more than the maximum
// allowable number of votes.
ErrTooManyVotes
// ErrFreshStakeMismatch indicates that a block's header contained a different
// number of SStx as compared to what was found in the block.
ErrFreshStakeMismatch
// ErrTooManySStxs indicates that more than the allowed number of SStx was
// found in a block.
ErrTooManySStxs
// ErrInvalidEarlyStakeTx indicates that a tx type other than SStx was found
// in the stake tx tree before the period when stake validation begins, or
// before the stake tx type could possibly be included in the block.
ErrInvalidEarlyStakeTx
// ErrTicketUnavailable indicates that a vote in the block spent a ticket
// that could not be found.
ErrTicketUnavailable
// ErrVotesOnWrongBlock indicates that an SSGen voted on a block not the
// block's parent, and so was ineligible for inclusion into that block.
ErrVotesOnWrongBlock
// ErrVotesMismatch indicates that the number of SSGen in the block was not
// equivalent to the number of votes provided in the block header.
ErrVotesMismatch
// ErrIncongruentVotebit indicates that the first votebit in votebits was not
// the same as that determined by the majority of voters in the SSGen tx
// included in the block.
ErrIncongruentVotebit
// ErrInvalidSSRtx indicates than an SSRtx in a block could not be found to
// have a valid missed sstx input as per the stake ticket database.
ErrInvalidSSRtx
// ErrInvalidRevNum indicates that the number of revocations from the
// header was not the same as the number of SSRtx included in the block.
ErrRevocationsMismatch
// ErrTooManyRevocations indicates more revocations were found in a block
// than were allowed.
ErrTooManyRevocations
// ErrSStxCommitment indicates that the propotional amounts from the inputs
// of an SStx did not match those found in the commitment outputs.
ErrSStxCommitment
// ErrUnparseableSSGen indicates that the SSGen block vote or votebits data
// was unparseable from the null data outputs.
ErrUnparseableSSGen
// ErrInvalidSSGenInput indicates that the input SStx to the SSGen tx was
// invalid because it was not an SStx.
ErrInvalidSSGenInput
// ErrSSGenPayeeNum indicates that the number of payees from the referenced
// SSGen's SStx was not the same as the number of the payees in the outputs
// of the SSGen tx.
ErrSSGenPayeeNum
// ErrSSGenPayeeOuts indicates that the SSGen payee outputs were either not
// the values that would be expected given the rewards and input amounts of
// the original SStx, or that the SSGen addresses did not correctly correspond
// to the null data outputs given in the originating SStx.
ErrSSGenPayeeOuts
// ErrSSGenSubsidy indicates that there was an error in the amount of subsidy
// generated in the vote.
ErrSSGenSubsidy
// ErrSStxInImmature indicates that the OP_SSTX tagged output used as input
// was not yet TicketMaturity many blocks old.
ErrSStxInImmature
// ErrSStxInScrType indicates that the input used in an sstx was not
// pay-to-pubkeyhash or pay-to-script-hash, which is required. It can
// be OP_SS* tagged, but it must be P2PKH or P2SH.
ErrSStxInScrType
// ErrInvalidSSRtxInput indicates that the input for the SSRtx was not from
// an SStx.
ErrInvalidSSRtxInput
// ErrSSRtxPayeesMismatch means that the number of payees in an SSRtx was
// not the same as the number of payees in the outputs of the input SStx.
ErrSSRtxPayeesMismatch
// ErrSSRtxPayees indicates that the SSRtx failed to pay out to the committed
// addresses or amounts from the originating SStx.
ErrSSRtxPayees
// ErrTxSStxOutSpend indicates that a non SSGen or SSRtx tx attempted to spend
// an OP_SSTX tagged output from an SStx.
ErrTxSStxOutSpend
// ErrRegTxSpendStakeOut indicates that a regular tx attempted to spend to
// outputs tagged with stake tags, e.g. OP_SSTX.
ErrRegTxSpendStakeOut
// ErrBIP0030 indicates that a block failed to pass BIP0030.
ErrBIP0030
// ErrInvalidFinalState indicates that the final state of the PRNG included
// in the the block differed from the calculated final state.
ErrInvalidFinalState
// ErrPoolSize indicates an error in the ticket pool size for this block.
ErrPoolSize
// ErrForceReorgWrongChain indicates that a reroganization was attempted
// to be forced, but the chain indicated was not mirrored by b.bestChain.
ErrForceReorgWrongChain
// ErrForceReorgMissingChild indicates that a reroganization was attempted
// to be forced, but the child node to reorganize to could not be found.
ErrForceReorgMissingChild
// ErrBadStakebaseValue indicates that a block's stake tx tree has spent
// more than it is allowed.
ErrBadStakebaseValue
// ErrDiscordantTxTree specifies that a given origin tx's content
// indicated that it should exist in a different tx tree than the
// one given in the TxIn outpoint.
ErrDiscordantTxTree
// ErrStakeFees indicates an error with the fees found in the stake
// transaction tree.
ErrStakeFees
// ErrNoStakeTx indicates there were no stake transactions found in a
// block after stake validation height.
ErrNoStakeTx
// ErrBadBlockHeight indicates that a block header's embedded block height
// was different from where it was actually embedded in the block chain.
ErrBadBlockHeight
// ErrBlockOneTx indicates that block height 1 failed to correct generate
// the block one premine transaction.
ErrBlockOneTx
// ErrBlockOneTx indicates that block height 1 coinbase transaction in
// zero was incorrect in some way.
ErrBlockOneInputs
// ErrBlockOneOutputs indicates that block height 1 failed to incorporate
// the ledger addresses correctly into the transaction's outputs.
ErrBlockOneOutputs
// ErrNoTax indicates that there was no tax present in the coinbase of a
// block after height 1.
ErrNoTax
// ErrExpiredTx indicates that the transaction is currently expired.
ErrExpiredTx
// ErrExpiryTxSpentEarly indicates that an output from a transaction
// that included an expiry field was spent before coinbase maturity
// many blocks had passed in the blockchain.
ErrExpiryTxSpentEarly
// ErrFraudAmountIn indicates the witness amount given was fraudulent.
ErrFraudAmountIn
// ErrFraudBlockHeight indicates the witness block height given was fraudulent.
ErrFraudBlockHeight
// ErrFraudBlockIndex indicates the witness block index given was fraudulent.
ErrFraudBlockIndex
// ErrZeroValueOutputSpend indicates that a transaction attempted to spend a
// zero value output.
ErrZeroValueOutputSpend
// ErrInvalidEarlyVoteBits indicates that a block before stake validation
// height had an unallowed vote bits value.
ErrInvalidEarlyVoteBits
)
// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrDuplicateBlock: "ErrDuplicateBlock",
ErrMissingParent: "ErrMissingParent",
ErrBlockTooBig: "ErrBlockTooBig",
ErrWrongBlockSize: "ErrWrongBlockSize",
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
ErrInvalidTime: "ErrInvalidTime",
ErrTimeTooOld: "ErrTimeTooOld",
ErrTimeTooNew: "ErrTimeTooNew",
ErrDifficultyTooLow: "ErrDifficultyTooLow",
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
ErrHighHash: "ErrHighHash",
ErrBadMerkleRoot: "ErrBadMerkleRoot",
ErrBadCheckpoint: "ErrBadCheckpoint",
ErrForkTooOld: "ErrForkTooOld",
ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld",
ErrNoTransactions: "ErrNoTransactions",
ErrTooManyTransactions: "ErrTooManyTransactions",
ErrNoTxInputs: "ErrNoTxInputs",
ErrNoTxOutputs: "ErrNoTxOutputs",
ErrTxTooBig: "ErrTxTooBig",
ErrBadTxOutValue: "ErrBadTxOutValue",
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
ErrBadTxInput: "ErrBadTxInput",
ErrMissingTx: "ErrMissingTx",
ErrUnfinalizedTx: "ErrUnfinalizedTx",
ErrDuplicateTx: "ErrDuplicateTx",
ErrOverwriteTx: "ErrOverwriteTx",
ErrImmatureSpend: "ErrImmatureSpend",
ErrDoubleSpend: "ErrDoubleSpend",
ErrSpendTooHigh: "ErrSpendTooHigh",
ErrBadFees: "ErrBadFees",
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrStakeTxInRegularTree: "ErrStakeTxInRegularTree",
ErrRegTxInStakeTree: "ErrRegTxInStakeTree",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
ErrBadCoinbaseOutpoint: "ErrBadCoinbaseOutpoint",
ErrBadCoinbaseFraudProof: "ErrBadCoinbaseFraudProof",
ErrBadCoinbaseAmountIn: "ErrBadCoinbaseAmountIn",
ErrBadStakebaseAmountIn: "ErrBadStakebaseAmountIn",
ErrBadStakebaseScriptLen: "ErrBadStakebaseScriptLen",
ErrBadStakevaseScrVal: "ErrBadStakevaseScrVal",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrNotEnoughStake: "ErrNotEnoughStake",
ErrStakeBelowMinimum: "ErrStakeBelowMinimum",
ErrNotEnoughVotes: "ErrNotEnoughVotes",
ErrTooManyVotes: "ErrTooManyVotes",
ErrFreshStakeMismatch: "ErrFreshStakeMismatch",
ErrTooManySStxs: "ErrTooManySStxs",
ErrInvalidEarlyStakeTx: "ErrInvalidEarlyStakeTx",
ErrTicketUnavailable: "ErrTicketUnavailable",
ErrVotesOnWrongBlock: "ErrVotesOnWrongBlock",
ErrVotesMismatch: "ErrVotesMismatch",
ErrIncongruentVotebit: "ErrIncongruentVotebit",
ErrInvalidSSRtx: "ErrInvalidSSRtx",
ErrRevocationsMismatch: "ErrRevocationsMismatch",
ErrTooManyRevocations: "ErrTooManyRevocations",
ErrSStxCommitment: "ErrSStxCommitment",
ErrUnparseableSSGen: "ErrUnparseableSSGen",
ErrInvalidSSGenInput: "ErrInvalidSSGenInput",
ErrSSGenPayeeOuts: "ErrSSGenPayeeOuts",
ErrSSGenSubsidy: "ErrSSGenSubsidy",
ErrSStxInImmature: "ErrSStxInImmature",
ErrSStxInScrType: "ErrSStxInScrType",
ErrInvalidSSRtxInput: "ErrInvalidSSRtxInput",
ErrSSRtxPayeesMismatch: "ErrSSRtxPayeesMismatch",
ErrSSRtxPayees: "ErrSSRtxPayees",
ErrTxSStxOutSpend: "ErrTxSStxOutSpend",
ErrRegTxSpendStakeOut: "ErrRegTxSpendStakeOut",
ErrInvalidFinalState: "ErrInvalidFinalState",
ErrPoolSize: "ErrPoolSize",
ErrForceReorgWrongChain: "ErrForceReorgWrongChain",
ErrForceReorgMissingChild: "ErrForceReorgMissingChild",
ErrBadStakebaseValue: "ErrBadStakebaseValue",
ErrDiscordantTxTree: "ErrDiscordantTxTree",
ErrStakeFees: "ErrStakeFees",
ErrBadBlockHeight: "ErrBadBlockHeight",
ErrBlockOneTx: "ErrBlockOneTx",
ErrBlockOneInputs: "ErrBlockOneInputs",
ErrBlockOneOutputs: "ErrBlockOneOutputs",
ErrNoTax: "ErrNoTax",
ErrExpiredTx: "ErrExpiredTx",
ErrExpiryTxSpentEarly: "ErrExpiryTxSpentEarly",
ErrFraudAmountIn: "ErrFraudAmountIn",
ErrFraudBlockHeight: "ErrFraudBlockHeight",
ErrFraudBlockIndex: "ErrFraudBlockIndex",
ErrZeroValueOutputSpend: "ErrZeroValueOutputSpend",
ErrInvalidEarlyVoteBits: "ErrInvalidEarlyVoteBits",
}
// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
}
// RuleError identifies a rule violation. It is used to indicate that
// processing of a block or transaction failed due to one of the many validation
// rules. The caller can use type assertions to determine if a failure was
// specifically due to a rule violation and access the ErrorCode field to
// ascertain the specific reason for the rule violation.
type RuleError struct {
ErrorCode ErrorCode // Describes the kind of error
Description string // Human readable description of the issue
}
// Error satisfies the error interface and prints human-readable errors.
func (e RuleError) Error() string {
return e.Description
}
// GetCode satisfies the error interface and prints human-readable errors.
func (e RuleError) GetCode() ErrorCode {
return e.ErrorCode
}
// ruleError creates an RuleError given a set of arguments.
func ruleError(c ErrorCode, desc string) RuleError {
return RuleError{ErrorCode: c, Description: desc}
}