btcd/rpcclient/errors.go
Yong cc26860b40
rpcclient: update error str to match both versions (#2205)
This commit updates the error str to match the same error returned from
`btcd` for both pre-0.24.2 and post-0.24.2.
2024-06-25 10:27:44 -04:00

519 lines
16 KiB
Go

package rpcclient
import (
"errors"
"fmt"
"strings"
)
var (
// ErrBackendVersion is returned when running against a bitcoind or
// btcd that is older than the minimum version supported by the
// rpcclient.
ErrBackendVersion = errors.New("backend version too low")
// ErrInvalidParam is returned when the caller provides an invalid
// parameter to an RPC method.
ErrInvalidParam = errors.New("invalid param")
// ErrUndefined is used when an error returned is not recognized. We
// should gradually increase our error types to avoid returning this
// error.
ErrUndefined = errors.New("undefined")
)
// BitcoindRPCErr represents an error returned by bitcoind's RPC server.
type BitcoindRPCErr uint32
// This section defines all possible errors or reject reasons returned from
// bitcoind's `sendrawtransaction` or `testmempoolaccept` RPC.
const (
// ErrMissingInputsOrSpent is returned when calling
// `sendrawtransaction` with missing inputs.
ErrMissingInputsOrSpent BitcoindRPCErr = iota
// ErrMaxBurnExceeded is returned when calling `sendrawtransaction`
// with exceeding, falling short of, and equaling maxburnamount.
ErrMaxBurnExceeded
// ErrMaxFeeExceeded can happen when passing a signed tx to
// `testmempoolaccept`, but the tx pays more fees than specified.
ErrMaxFeeExceeded
// ErrTxAlreadyKnown is used in the `reject-reason` field of
// `testmempoolaccept` when a transaction is already in the blockchain.
ErrTxAlreadyKnown
// ErrTxAlreadyConfirmed is returned as an error from
// `sendrawtransaction` when a transaction is already in the
// blockchain.
ErrTxAlreadyConfirmed
// ErrMempoolConflict happens when RBF is not enabled yet the
// transaction conflicts with an unconfirmed tx. .
//
// NOTE: RBF rule 1.
ErrMempoolConflict
// ErrReplacementAddsUnconfirmed is returned when a transaction adds
// new unconfirmed inputs.
//
// NOTE: RBF rule 2.
ErrReplacementAddsUnconfirmed
// ErrInsufficientFee is returned when fee rate used or fees paid
// doesn't meet the requirements.
//
// NOTE: RBF rule 3 or 4.
ErrInsufficientFee
// ErrTooManyReplacements is returned when a transaction causes too
// many transactions being replaced. This is set by
// `MAX_REPLACEMENT_CANDIDATES` in `bitcoind` and defaults to 100.
//
// NOTE: RBF rule 5.
ErrTooManyReplacements
// ErrMempoolMinFeeNotMet is returned when the transaction doesn't meet
// the minimum relay fee.
ErrMempoolMinFeeNotMet
// ErrConflictingTx is returned when a transaction that spends
// conflicting tx outputs that are rejected.
ErrConflictingTx
// ErrEmptyOutput is returned when a transaction has no outputs.
ErrEmptyOutput
// ErrEmptyInput is returned when a transaction has no inputs.
ErrEmptyInput
// ErrTxTooSmall is returned when spending a tiny transaction(in
// non-witness bytes) that is disallowed.
//
// NOTE: ErrTxTooLarge must be put after ErrTxTooSmall because it's a
// subset of ErrTxTooSmall. Otherwise, if bitcoind returns
// `tx-size-small`, it will be matched to ErrTxTooLarge.
ErrTxTooSmall
// ErrDuplicateInput is returned when a transaction has duplicate
// inputs.
ErrDuplicateInput
// ErrEmptyPrevOut is returned when a non-coinbase transaction has
// coinbase-like outpoint.
ErrEmptyPrevOut
// ErrBelowOutValue is returned when a transaction's output value is
// greater than its input value.
ErrBelowOutValue
// ErrNegativeOutput is returned when a transaction has negative output
// value.
ErrNegativeOutput
// ErrLargeOutput is returned when a transaction has too large output
// value.
ErrLargeOutput
// ErrLargeTotalOutput is returned when a transaction has too large sum
// of output values.
ErrLargeTotalOutput
// ErrScriptVerifyFlag is returned when there is invalid OP_IF
// construction.
ErrScriptVerifyFlag
// ErrTooManySigOps is returned when a transaction has too many sigops.
ErrTooManySigOps
// ErrInvalidOpcode is returned when a transaction has invalid OP
// codes.
ErrInvalidOpcode
// ErrTxAlreadyInMempool is returned when a transaction is in the
// mempool.
ErrTxAlreadyInMempool
// ErrMissingInputs is returned when a transaction has missing inputs,
// that never existed or only existed once in the past.
ErrMissingInputs
// ErrOversizeTx is returned when a transaction is too large.
ErrOversizeTx
// ErrCoinbaseTx is returned when the transaction is coinbase tx.
ErrCoinbaseTx
// ErrNonStandardVersion is returned when the transactions are not
// standard - a version currently non-standard.
ErrNonStandardVersion
// ErrNonStandardScript is returned when the transactions are not
// standard - non-standard script.
ErrNonStandardScript
// ErrBareMultiSig is returned when the transactions are not standard -
// bare multisig script (2-of-3).
ErrBareMultiSig
// ErrScriptSigNotPushOnly is returned when the transactions are not
// standard - not-pushonly scriptSig.
ErrScriptSigNotPushOnly
// ErrScriptSigSize is returned when the transactions are not standard
// - too large scriptSig (>1650 bytes).
ErrScriptSigSize
// ErrTxTooLarge is returned when the transactions are not standard -
// too large tx size.
ErrTxTooLarge
// ErrDust is returned when the transactions are not standard - output
// too small.
ErrDust
// ErrMultiOpReturn is returned when the transactions are not standard
// - muiltiple OP_RETURNs.
ErrMultiOpReturn
// ErrNonFinal is returned when spending a timelocked transaction that
// hasn't expired yet.
ErrNonFinal
// ErrNonBIP68Final is returned when a transaction that is locked by
// BIP68 sequence logic and not expired yet.
ErrNonBIP68Final
// ErrSameNonWitnessData is returned when another tx with the same
// non-witness data is already in the mempool. For instance, these two
// txns share the same `txid` but different `wtxid`.
ErrSameNonWitnessData
// ErrNonMandatoryScriptVerifyFlag is returned when passing a raw tx to
// `testmempoolaccept`, which gives the error followed by (Witness
// program hash mismatch).
ErrNonMandatoryScriptVerifyFlag
// errSentinel is used to indicate the end of the error list. This
// should always be the last error code.
errSentinel
)
// Error implements the error interface. It returns the error message defined
// in `bitcoind`.
// Some of the dashes used in the original error string is removed, e.g.
// "missing-inputs" is now "missing inputs". This is ok since we will normalize
// the errors before matching.
//
// references:
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/rpc_rawtransaction.py#L342
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/data/invalid_txs.py
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/mempool_accept.py
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/mempool_accept_wtxid.py
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/mempool_dust.py
// - https://github.com/bitcoin/bitcoin/blob/master/test/functional/mempool_limit.py
// - https://github.com/bitcoin/bitcoin/blob/master/src/validation.cpp
func (r BitcoindRPCErr) Error() string {
switch r {
case ErrMissingInputsOrSpent:
return "bad-txns-inputs-missingorspent"
case ErrMaxBurnExceeded:
return "Unspendable output exceeds maximum configured by user (maxburnamount)"
case ErrMaxFeeExceeded:
return "max-fee-exceeded"
case ErrTxAlreadyKnown:
return "txn-already-known"
case ErrTxAlreadyConfirmed:
return "Transaction already in block chain"
case ErrMempoolConflict:
return "txn mempool conflict"
case ErrReplacementAddsUnconfirmed:
return "replacement adds unconfirmed"
case ErrInsufficientFee:
return "insufficient fee"
case ErrTooManyReplacements:
return "too many potential replacements"
case ErrMempoolMinFeeNotMet:
return "mempool min fee not met"
case ErrConflictingTx:
return "bad txns spends conflicting tx"
case ErrEmptyOutput:
return "bad txns vout empty"
case ErrEmptyInput:
return "bad txns vin empty"
case ErrTxTooSmall:
return "tx size small"
case ErrDuplicateInput:
return "bad txns inputs duplicate"
case ErrEmptyPrevOut:
return "bad txns prevout null"
case ErrBelowOutValue:
return "bad txns in belowout"
case ErrNegativeOutput:
return "bad txns vout negative"
case ErrLargeOutput:
return "bad txns vout toolarge"
case ErrLargeTotalOutput:
return "bad txns txouttotal toolarge"
case ErrScriptVerifyFlag:
return "mandatory script verify flag failed"
case ErrTooManySigOps:
return "bad txns too many sigops"
case ErrInvalidOpcode:
return "disabled opcode"
case ErrTxAlreadyInMempool:
return "txn already in mempool"
case ErrMissingInputs:
return "missing inputs"
case ErrOversizeTx:
return "bad txns oversize"
case ErrCoinbaseTx:
return "coinbase"
case ErrNonStandardVersion:
return "version"
case ErrNonStandardScript:
return "scriptpubkey"
case ErrBareMultiSig:
return "bare multisig"
case ErrScriptSigNotPushOnly:
return "scriptsig not pushonly"
case ErrScriptSigSize:
return "scriptsig size"
case ErrTxTooLarge:
return "tx size"
case ErrDust:
return "dust"
case ErrMultiOpReturn:
return "multi op return"
case ErrNonFinal:
return "non final"
case ErrNonBIP68Final:
return "non BIP68 final"
case ErrSameNonWitnessData:
return "txn-same-nonwitness-data-in-mempool"
case ErrNonMandatoryScriptVerifyFlag:
return "non-mandatory-script-verify-flag"
}
return "unknown error"
}
// BtcdErrMap takes the errors returned from btcd's `testmempoolaccept` and
// `sendrawtransaction` RPCs and map them to the errors defined above, which
// are results from calling either `testmempoolaccept` or `sendrawtransaction`
// in `bitcoind`.
//
// Errors not mapped in `btcd`:
// - deployment error from `validateSegWitDeployment`.
// - the error when total inputs is higher than max allowed value from
// `CheckTransactionInputs`.
// - the error when total outputs is higher than total inputs from
// `CheckTransactionInputs`.
// - errors from `CalcSequenceLock`.
//
// NOTE: This is not an exhaustive list of errors, but it covers the
// usage case of LND.
//
//nolint:lll
var BtcdErrMap = map[string]error{
// BIP125 related errors.
//
// When fee rate used or fees paid doesn't meet the requirements.
"replacement transaction has an insufficient fee rate": ErrInsufficientFee,
"replacement transaction has an insufficient absolute fee": ErrInsufficientFee,
// When a transaction causes too many transactions being replaced. This
// is set by `MAX_REPLACEMENT_CANDIDATES` in `bitcoind` and defaults to
// 100.
"replacement transaction evicts more transactions than permitted": ErrTooManyReplacements,
// When a transaction adds new unconfirmed inputs.
"replacement transaction spends new unconfirmed input": ErrReplacementAddsUnconfirmed,
// A transaction that spends conflicting tx outputs that are rejected.
"replacement transaction spends parent transaction": ErrConflictingTx,
// A transaction that conflicts with an unconfirmed tx. Happens when
// RBF is not enabled.
"output already spent in mempool": ErrMempoolConflict,
// A transaction with no outputs.
"transaction has no outputs": ErrEmptyOutput,
// A transaction with no inputs.
"transaction has no inputs": ErrEmptyInput,
// A transaction with duplicate inputs.
"transaction contains duplicate inputs": ErrDuplicateInput,
// A non-coinbase transaction with coinbase-like outpoint.
"transaction input refers to previous output that is null": ErrEmptyPrevOut,
// A transaction pays too little fee.
"fees which is under the required amount": ErrMempoolMinFeeNotMet,
"has insufficient priority": ErrInsufficientFee,
"has been rejected by the rate limiter due to low fees": ErrInsufficientFee,
// A transaction with negative output value.
"transaction output has negative value": ErrNegativeOutput,
// A transaction with too large output value.
"transaction output value is higher than max allowed value": ErrLargeOutput,
// A transaction with too large sum of output values.
"total value of all transaction outputs exceeds max allowed value": ErrLargeTotalOutput,
// A transaction with too many sigops.
"sigop cost is too hight": ErrTooManySigOps,
// A transaction already in the blockchain.
"database contains entry for spent tx output": ErrTxAlreadyKnown,
"transaction already exists in blockchain": ErrTxAlreadyConfirmed,
// A transaction in the mempool.
//
// NOTE: For btcd v0.24.2 and beyond, the error message is "already
// have transaction in mempool".
"already have transaction": ErrTxAlreadyInMempool,
// A transaction with missing inputs, that never existed or only
// existed once in the past.
"either does not exist or has already been spent": ErrMissingInputs,
"orphan transaction": ErrMissingInputs,
// A really large transaction.
"serialized transaction is too big": ErrOversizeTx,
// A coinbase transaction.
"transaction is an invalid coinbase": ErrCoinbaseTx,
// Some nonstandard transactions - a version currently non-standard.
"transaction version": ErrNonStandardVersion,
// Some nonstandard transactions - non-standard script.
"non-standard script form": ErrNonStandardScript,
"has a non-standard input": ErrNonStandardScript,
// Some nonstandard transactions - bare multisig script
// (2-of-3).
"milti-signature script": ErrBareMultiSig,
// Some nonstandard transactions - not-pushonly scriptSig.
"signature script is not push only": ErrScriptSigNotPushOnly,
// Some nonstandard transactions - too large scriptSig (>1650
// bytes).
"signature script size is larger than max allowed": ErrScriptSigSize,
// Some nonstandard transactions - too large tx size.
"weight of transaction is larger than max allowed": ErrTxTooLarge,
// Some nonstandard transactions - output too small.
"payment is dust": ErrDust,
// Some nonstandard transactions - muiltiple OP_RETURNs.
"more than one transaction output in a nulldata script": ErrMultiOpReturn,
// A timelocked transaction.
"transaction is not finalized": ErrNonFinal,
"tried to spend coinbase transaction output": ErrNonFinal,
// A transaction that is locked by BIP68 sequence logic.
"transaction's sequence locks on inputs not met": ErrNonBIP68Final,
// TODO(yy): find/return the following errors in `btcd`.
//
// A tiny transaction(in non-witness bytes) that is disallowed.
// "unmatched btcd error 1": ErrTxTooSmall,
// "unmatched btcd error 2": ErrScriptVerifyFlag,
// // A transaction with invalid OP codes.
// "unmatched btcd error 3": ErrInvalidOpcode,
// // Minimally-small transaction(in non-witness bytes) that is
// // allowed.
// "unmatched btcd error 4": ErrSameNonWitnessData,
}
// MapRPCErr takes an error returned from calling RPC methods from various
// chain backend and map it to an defined error here. It uses the `BtcdErrMap`
// defined above, whose keys are btcd error strings and values are errors made
// from bitcoind error strings.
//
// NOTE: we assume neutrino shares the same error strings as btcd.
func MapRPCErr(rpcErr error) error {
// Iterate the map and find the matching error.
for btcdErr, err := range BtcdErrMap {
// Match it against btcd's error first.
if matchErrStr(rpcErr, btcdErr) {
return err
}
}
// If not found, try to match it against bitcoind's error.
for i := uint32(0); i < uint32(errSentinel); i++ {
err := BitcoindRPCErr(i)
if matchErrStr(rpcErr, err.Error()) {
return err
}
}
// If not matched, return the original error wrapped.
return fmt.Errorf("%w: %v", ErrUndefined, rpcErr)
}
// matchErrStr takes an error returned from RPC client and matches it against
// the specified string. If the expected string pattern is found in the error
// passed, return true. Both the error strings are normalized before matching.
func matchErrStr(err error, s string) bool {
// Replace all dashes found in the error string with spaces.
strippedErrStr := strings.ReplaceAll(err.Error(), "-", " ")
// Replace all dashes found in the error string with spaces.
strippedMatchStr := strings.ReplaceAll(s, "-", " ")
// Match against the lowercase.
return strings.Contains(
strings.ToLower(strippedErrStr),
strings.ToLower(strippedMatchStr),
)
}