Implement BIP0061 reject handling (pver 70002).
This commit implements reject handling as defined by BIP0061 and bumps the maximum supported protocol version to 70002 accordingly. As a part of supporting this a new error type named RuleError has been introduced which encapsulates and underlying error which could be one of the existing TxRuleError or btcchain.RuleError types. This allows a single high level type assertion to be used to determine if the block or transaction was rejected due to a rule error or due to an unexpected error. Meanwhile, an appropriate reject error can be created from the error by pulling the underlying error out and using it. Also, a check for minimum protocol version of 209 has been added. Closes #133.
This commit is contained in:
parent
2f0cab1a48
commit
000691dc9e
6 changed files with 379 additions and 62 deletions
|
@ -499,12 +499,19 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) {
|
||||||
// simply rejected as opposed to something actually going wrong,
|
// simply rejected as opposed to something actually going wrong,
|
||||||
// so log it as such. Otherwise, something really did go wrong,
|
// so log it as such. Otherwise, something really did go wrong,
|
||||||
// so log it as an actual error.
|
// so log it as an actual error.
|
||||||
if _, ok := err.(TxRuleError); ok {
|
if _, ok := err.(RuleError); ok {
|
||||||
bmgrLog.Debugf("Rejected transaction %v from %s: %v", txHash,
|
bmgrLog.Debugf("Rejected transaction %v from %s: %v",
|
||||||
tmsg.peer, err)
|
txHash, tmsg.peer, err)
|
||||||
} else {
|
} else {
|
||||||
bmgrLog.Errorf("Failed to process transaction %v: %v", txHash, err)
|
bmgrLog.Errorf("Failed to process transaction %v: %v",
|
||||||
|
txHash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the error into an appropriate reject message and
|
||||||
|
// send it.
|
||||||
|
code, reason := errToRejectErr(err)
|
||||||
|
tmsg.peer.PushRejectMsg(btcwire.CmdBlock, code, reason, txHash,
|
||||||
|
false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,8 +601,15 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
|
||||||
bmgrLog.Infof("Rejected block %v from %s: %v", blockSha,
|
bmgrLog.Infof("Rejected block %v from %s: %v", blockSha,
|
||||||
bmsg.peer, err)
|
bmsg.peer, err)
|
||||||
} else {
|
} else {
|
||||||
bmgrLog.Errorf("Failed to process block %v: %v", blockSha, err)
|
bmgrLog.Errorf("Failed to process block %v: %v",
|
||||||
|
blockSha, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the error into an appropriate reject message and
|
||||||
|
// send it.
|
||||||
|
code, reason := errToRejectErr(err)
|
||||||
|
bmsg.peer.PushRejectMsg(btcwire.CmdBlock, code, reason,
|
||||||
|
blockSha, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
log.go
43
log.go
|
@ -7,6 +7,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/conformal/btcd/addrmgr"
|
"github.com/conformal/btcd/addrmgr"
|
||||||
|
@ -26,6 +27,10 @@ const (
|
||||||
// years. However, if the field is interpreted as a timestamp, given
|
// years. However, if the field is interpreted as a timestamp, given
|
||||||
// the lock time is a uint32, the max is sometime around 2106.
|
// the lock time is a uint32, the max is sometime around 2106.
|
||||||
lockTimeThreshold uint32 = 5e8 // Tue Nov 5 00:53:20 1985 UTC
|
lockTimeThreshold uint32 = 5e8 // Tue Nov 5 00:53:20 1985 UTC
|
||||||
|
|
||||||
|
// maxRejectReasonLen is the maximum length of a sanitized reject reason
|
||||||
|
// that will be logged.
|
||||||
|
maxRejectReasonLen = 200
|
||||||
)
|
)
|
||||||
|
|
||||||
// Loggers per subsytem. Note that backendLog is a seelog logger that all of
|
// Loggers per subsytem. Note that backendLog is a seelog logger that all of
|
||||||
|
@ -250,6 +255,30 @@ func locatorSummary(locator []*btcwire.ShaHash, stopHash *btcwire.ShaHash) strin
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitizeString strips any characters which are even remotely dangerous, such
|
||||||
|
// as html control characters, from the passed string. It also limits it to
|
||||||
|
// the passed maximum size, which can be 0 for unlimited. When the string is
|
||||||
|
// limited, it will also add "..." to the string to indicate it was truncated.
|
||||||
|
func sanitizeString(str string, maxLength uint) string {
|
||||||
|
const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" +
|
||||||
|
"Z01234567890 .,;_/:?@"
|
||||||
|
|
||||||
|
// Strip any characters not in the safeChars string removed.
|
||||||
|
str = strings.Map(func(r rune) rune {
|
||||||
|
if strings.IndexRune(safeChars, r) >= 0 {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}, str)
|
||||||
|
|
||||||
|
// Limit the string to the max allowed length.
|
||||||
|
if maxLength > 0 && uint(len(str)) > maxLength {
|
||||||
|
str = str[:maxLength]
|
||||||
|
str = str + "..."
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
// messageSummary returns a human-readable string which summarizes a message.
|
// messageSummary returns a human-readable string which summarizes a message.
|
||||||
// Not all messages have or need a summary. This is used for debug logging.
|
// Not all messages have or need a summary. This is used for debug logging.
|
||||||
func messageSummary(msg btcwire.Message) string {
|
func messageSummary(msg btcwire.Message) string {
|
||||||
|
@ -308,6 +337,20 @@ func messageSummary(msg btcwire.Message) string {
|
||||||
|
|
||||||
case *btcwire.MsgHeaders:
|
case *btcwire.MsgHeaders:
|
||||||
return fmt.Sprintf("num %d", len(msg.Headers))
|
return fmt.Sprintf("num %d", len(msg.Headers))
|
||||||
|
|
||||||
|
case *btcwire.MsgReject:
|
||||||
|
// Ensure the variable length strings don't contain any
|
||||||
|
// characters which are even remotely dangerous such as HTML
|
||||||
|
// control characters, etc. Also limit them to sane length for
|
||||||
|
// logging.
|
||||||
|
rejCommand := sanitizeString(msg.Cmd, btcwire.CommandSize)
|
||||||
|
rejReason := sanitizeString(msg.Reason, maxRejectReasonLen)
|
||||||
|
summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand,
|
||||||
|
msg.Code, rejReason)
|
||||||
|
if rejCommand == btcwire.CmdBlock || rejCommand == btcwire.CmdTx {
|
||||||
|
summary += fmt.Sprintf(", hash %v", msg.Hash)
|
||||||
|
}
|
||||||
|
return summary
|
||||||
}
|
}
|
||||||
|
|
||||||
// No summary for other messages.
|
// No summary for other messages.
|
||||||
|
|
128
mempool.go
128
mempool.go
|
@ -20,17 +20,6 @@ import (
|
||||||
"github.com/conformal/btcwire"
|
"github.com/conformal/btcwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxRuleError identifies a rule violation. It is used to indicate that
|
|
||||||
// processing of a 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.
|
|
||||||
type TxRuleError string
|
|
||||||
|
|
||||||
// Error satisfies the error interface to print human-readable errors.
|
|
||||||
func (e TxRuleError) Error() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// mempoolHeight is the height used for the "block" height field of the
|
// mempoolHeight is the height used for the "block" height field of the
|
||||||
// contextual transaction information provided in a transaction store.
|
// contextual transaction information provided in a transaction store.
|
||||||
|
@ -182,36 +171,41 @@ func checkPkScriptStandard(pkScript []byte, scriptClass btcscript.ScriptClass) e
|
||||||
case btcscript.MultiSigTy:
|
case btcscript.MultiSigTy:
|
||||||
numPubKeys, numSigs, err := btcscript.CalcMultiSigStats(pkScript)
|
numPubKeys, numSigs, err := btcscript.CalcMultiSigStats(pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
str := fmt.Sprintf("multi-signature script parse "+
|
||||||
|
"failure: %v", err)
|
||||||
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A standard multi-signature public key script must contain
|
// A standard multi-signature public key script must contain
|
||||||
// from 1 to maxStandardMultiSigKeys public keys.
|
// from 1 to maxStandardMultiSigKeys public keys.
|
||||||
if numPubKeys < 1 {
|
if numPubKeys < 1 {
|
||||||
return TxRuleError("multi-signature script with no pubkeys")
|
str := "multi-signature script with no pubkeys"
|
||||||
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
if numPubKeys > maxStandardMultiSigKeys {
|
if numPubKeys > maxStandardMultiSigKeys {
|
||||||
str := fmt.Sprintf("multi-signature script with %d "+
|
str := fmt.Sprintf("multi-signature script with %d "+
|
||||||
"public keys which is more than the allowed "+
|
"public keys which is more than the allowed "+
|
||||||
"max of %d", numPubKeys, maxStandardMultiSigKeys)
|
"max of %d", numPubKeys, maxStandardMultiSigKeys)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A standard multi-signature public key script must have at
|
// A standard multi-signature public key script must have at
|
||||||
// least 1 signature and no more signatures than available
|
// least 1 signature and no more signatures than available
|
||||||
// public keys.
|
// public keys.
|
||||||
if numSigs < 1 {
|
if numSigs < 1 {
|
||||||
return TxRuleError("multi-signature script with no signatures")
|
return txRuleError(btcwire.RejectNonstandard,
|
||||||
|
"multi-signature script with no signatures")
|
||||||
}
|
}
|
||||||
if numSigs > numPubKeys {
|
if numSigs > numPubKeys {
|
||||||
str := fmt.Sprintf("multi-signature script with %d "+
|
str := fmt.Sprintf("multi-signature script with %d "+
|
||||||
"signatures which is more than the available "+
|
"signatures which is more than the available "+
|
||||||
"%d public keys", numSigs, numPubKeys)
|
"%d public keys", numSigs, numPubKeys)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
case btcscript.NonStandardTy:
|
case btcscript.NonStandardTy:
|
||||||
return TxRuleError("non-standard script form")
|
return txRuleError(btcwire.RejectNonstandard,
|
||||||
|
"non-standard script form")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -232,13 +226,14 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
str := fmt.Sprintf("transaction version %d is not in the "+
|
str := fmt.Sprintf("transaction version %d is not in the "+
|
||||||
"valid range of %d-%d", msgTx.Version, 1,
|
"valid range of %d-%d", msgTx.Version, 1,
|
||||||
btcwire.TxVersion)
|
btcwire.TxVersion)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The transaction must be finalized to be standard and therefore
|
// The transaction must be finalized to be standard and therefore
|
||||||
// considered for inclusion in a block.
|
// considered for inclusion in a block.
|
||||||
if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) {
|
if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) {
|
||||||
return TxRuleError("transaction is not finalized")
|
return txRuleError(btcwire.RejectNonstandard,
|
||||||
|
"transaction is not finalized")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since extremely large transactions with a lot of inputs can cost
|
// Since extremely large transactions with a lot of inputs can cost
|
||||||
|
@ -249,7 +244,7 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
if serializedLen > maxStandardTxSize {
|
if serializedLen > maxStandardTxSize {
|
||||||
str := fmt.Sprintf("transaction size of %v is larger than max "+
|
str := fmt.Sprintf("transaction size of %v is larger than max "+
|
||||||
"allowed size of %v", serializedLen, maxStandardTxSize)
|
"allowed size of %v", serializedLen, maxStandardTxSize)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, txIn := range msgTx.TxIn {
|
for i, txIn := range msgTx.TxIn {
|
||||||
|
@ -262,7 +257,7 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
"script size of %d bytes is large than max "+
|
"script size of %d bytes is large than max "+
|
||||||
"allowed size of %d bytes", i, sigScriptLen,
|
"allowed size of %d bytes", i, sigScriptLen,
|
||||||
maxStandardSigScriptSize)
|
maxStandardSigScriptSize)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each transaction input signature script must only contain
|
// Each transaction input signature script must only contain
|
||||||
|
@ -270,7 +265,7 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
if !btcscript.IsPushOnlyScript(txIn.SignatureScript) {
|
if !btcscript.IsPushOnlyScript(txIn.SignatureScript) {
|
||||||
str := fmt.Sprintf("transaction input %d: signature "+
|
str := fmt.Sprintf("transaction input %d: signature "+
|
||||||
"script is not push only", i)
|
"script is not push only", i)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each transaction input signature script must only contain
|
// Each transaction input signature script must only contain
|
||||||
|
@ -280,7 +275,7 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
if !btcscript.HasCanonicalPushes(txIn.SignatureScript) {
|
if !btcscript.HasCanonicalPushes(txIn.SignatureScript) {
|
||||||
str := fmt.Sprintf("transaction input %d: signature "+
|
str := fmt.Sprintf("transaction input %d: signature "+
|
||||||
"script has a non-canonical data push", i)
|
"script has a non-canonical data push", i)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,8 +286,15 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
scriptClass := btcscript.GetScriptClass(txOut.PkScript)
|
scriptClass := btcscript.GetScriptClass(txOut.PkScript)
|
||||||
err := checkPkScriptStandard(txOut.PkScript, scriptClass)
|
err := checkPkScriptStandard(txOut.PkScript, scriptClass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Attempt to extract a reject code from the error so
|
||||||
|
// it can be retained. When not possible, fall back to
|
||||||
|
// a non standard error.
|
||||||
|
rejectCode, found := extractRejectCode(err)
|
||||||
|
if !found {
|
||||||
|
rejectCode = btcwire.RejectNonstandard
|
||||||
|
}
|
||||||
str := fmt.Sprintf("transaction output %d: %v", i, err)
|
str := fmt.Sprintf("transaction output %d: %v", i, err)
|
||||||
return TxRuleError(str)
|
return txRuleError(rejectCode, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate the number of outputs which only carry data.
|
// Accumulate the number of outputs which only carry data.
|
||||||
|
@ -303,15 +305,15 @@ func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
|
||||||
if isDust(txOut) {
|
if isDust(txOut) {
|
||||||
str := fmt.Sprintf("transaction output %d: payment "+
|
str := fmt.Sprintf("transaction output %d: payment "+
|
||||||
"of %d is dust", i, txOut.Value)
|
"of %d is dust", i, txOut.Value)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectDust, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A standard transaction must not have more than one output script that
|
// A standard transaction must not have more than one output script that
|
||||||
// only carries data.
|
// only carries data.
|
||||||
if numNullDataOutputs > 1 {
|
if numNullDataOutputs > 1 {
|
||||||
return TxRuleError("more than one transaction output is a " +
|
str := "more than one transaction output in a nulldata script"
|
||||||
"nulldata script")
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -341,7 +343,9 @@ func checkInputsStandard(tx *btcutil.Tx, txStore btcchain.TxStore) error {
|
||||||
scriptInfo, err := btcscript.CalcScriptInfo(txIn.SignatureScript,
|
scriptInfo, err := btcscript.CalcScriptInfo(txIn.SignatureScript,
|
||||||
originPkScript, true)
|
originPkScript, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
str := fmt.Sprintf("transaction input #%d script parse "+
|
||||||
|
"failure: %v", i, err)
|
||||||
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A negative value for expected inputs indicates the script is
|
// A negative value for expected inputs indicates the script is
|
||||||
|
@ -349,7 +353,7 @@ func checkInputsStandard(tx *btcutil.Tx, txStore btcchain.TxStore) error {
|
||||||
if scriptInfo.ExpectedInputs < 0 {
|
if scriptInfo.ExpectedInputs < 0 {
|
||||||
str := fmt.Sprintf("transaction input #%d expects %d "+
|
str := fmt.Sprintf("transaction input #%d expects %d "+
|
||||||
"inputs", i, scriptInfo.ExpectedInputs)
|
"inputs", i, scriptInfo.ExpectedInputs)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The script pair is non-standard if the number of available
|
// The script pair is non-standard if the number of available
|
||||||
|
@ -359,7 +363,7 @@ func checkInputsStandard(tx *btcutil.Tx, txStore btcchain.TxStore) error {
|
||||||
"inputs, but referenced output script provides "+
|
"inputs, but referenced output script provides "+
|
||||||
"%d", i, scriptInfo.ExpectedInputs,
|
"%d", i, scriptInfo.ExpectedInputs,
|
||||||
scriptInfo.NumInputs)
|
scriptInfo.NumInputs)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +515,7 @@ func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error {
|
||||||
str := fmt.Sprintf("orphan transaction size of %d bytes is "+
|
str := fmt.Sprintf("orphan transaction size of %d bytes is "+
|
||||||
"larger than max allowed size of %d bytes",
|
"larger than max allowed size of %d bytes",
|
||||||
serializedLen, maxOrphanTxSize)
|
serializedLen, maxOrphanTxSize)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the orphan if the none of the above disqualified it.
|
// Add the orphan if the none of the above disqualified it.
|
||||||
|
@ -677,7 +681,7 @@ func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
|
||||||
if txR, exists := mp.outpoints[txIn.PreviousOutpoint]; exists {
|
if txR, exists := mp.outpoints[txIn.PreviousOutpoint]; exists {
|
||||||
str := fmt.Sprintf("transaction %v in the pool "+
|
str := fmt.Sprintf("transaction %v in the pool "+
|
||||||
"already spends the same coins", txR.Sha())
|
"already spends the same coins", txR.Sha())
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectDuplicate, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,7 +748,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
// be a quick check to weed out duplicates.
|
// be a quick check to weed out duplicates.
|
||||||
if mp.haveTransaction(txHash) {
|
if mp.haveTransaction(txHash) {
|
||||||
str := fmt.Sprintf("already have transaction %v", txHash)
|
str := fmt.Sprintf("already have transaction %v", txHash)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectDuplicate, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform preliminary sanity checks on the transaction. This makes
|
// Perform preliminary sanity checks on the transaction. This makes
|
||||||
|
@ -752,8 +756,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
// transactions are allowed into blocks.
|
// transactions are allowed into blocks.
|
||||||
err := btcchain.CheckTransactionSanity(tx)
|
err := btcchain.CheckTransactionSanity(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(btcchain.RuleError); ok {
|
if cerr, ok := err.(btcchain.RuleError); ok {
|
||||||
return TxRuleError(err.Error())
|
return chainRuleError(cerr)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -762,7 +766,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if btcchain.IsCoinBase(tx) {
|
if btcchain.IsCoinBase(tx) {
|
||||||
str := fmt.Sprintf("transaction %v is an individual coinbase",
|
str := fmt.Sprintf("transaction %v is an individual coinbase",
|
||||||
txHash)
|
txHash)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectInvalid, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't accept transactions with a lock time after the maximum int32
|
// Don't accept transactions with a lock time after the maximum int32
|
||||||
|
@ -772,7 +776,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if tx.MsgTx().LockTime > math.MaxInt32 {
|
if tx.MsgTx().LockTime > math.MaxInt32 {
|
||||||
str := fmt.Sprintf("transaction %v has a lock time after "+
|
str := fmt.Sprintf("transaction %v has a lock time after "+
|
||||||
"2038 which is not accepted yet", txHash)
|
"2038 which is not accepted yet", txHash)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current height of the main chain. A standalone transaction
|
// Get the current height of the main chain. A standalone transaction
|
||||||
|
@ -780,6 +784,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
// one more than the current height.
|
// one more than the current height.
|
||||||
_, curHeight, err := mp.server.db.NewestSha()
|
_, curHeight, err := mp.server.db.NewestSha()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// This is an unexpected error so don't turn it into a rule
|
||||||
|
// error.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
nextBlockHeight := curHeight + 1
|
nextBlockHeight := curHeight + 1
|
||||||
|
@ -789,9 +795,16 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if !activeNetParams.RelayNonStdTxs {
|
if !activeNetParams.RelayNonStdTxs {
|
||||||
err := checkTransactionStandard(tx, nextBlockHeight)
|
err := checkTransactionStandard(tx, nextBlockHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("transaction %v is not a standard "+
|
// Attempt to extract a reject code from the error so
|
||||||
"transaction: %v", txHash, err)
|
// it can be retained. When not possible, fall back to
|
||||||
return TxRuleError(str)
|
// a non standard error.
|
||||||
|
rejectCode, found := extractRejectCode(err)
|
||||||
|
if !found {
|
||||||
|
rejectCode = btcwire.RejectNonstandard
|
||||||
|
}
|
||||||
|
str := fmt.Sprintf("transaction %v is not standard: %v",
|
||||||
|
txHash, err)
|
||||||
|
return txRuleError(rejectCode, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,6 +827,9 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
// needing to do a separate lookup.
|
// needing to do a separate lookup.
|
||||||
txStore, err := mp.fetchInputTransactions(tx)
|
txStore, err := mp.fetchInputTransactions(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if cerr, ok := err.(btcchain.RuleError); ok {
|
||||||
|
return chainRuleError(cerr)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,7 +838,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if txD, exists := txStore[*txHash]; exists && txD.Err == nil {
|
if txD, exists := txStore[*txHash]; exists && txD.Err == nil {
|
||||||
for _, isOutputSpent := range txD.Spent {
|
for _, isOutputSpent := range txD.Spent {
|
||||||
if !isOutputSpent {
|
if !isOutputSpent {
|
||||||
return TxRuleError("transaction already exists")
|
return txRuleError(btcwire.RejectDuplicate,
|
||||||
|
"transaction already exists")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,8 +861,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
// used later.
|
// used later.
|
||||||
txFee, err := btcchain.CheckTransactionInputs(tx, nextBlockHeight, txStore)
|
txFee, err := btcchain.CheckTransactionInputs(tx, nextBlockHeight, txStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(btcchain.RuleError); ok {
|
if cerr, ok := err.(btcchain.RuleError); ok {
|
||||||
return TxRuleError(err.Error())
|
return chainRuleError(cerr)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -855,9 +872,16 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if !activeNetParams.RelayNonStdTxs {
|
if !activeNetParams.RelayNonStdTxs {
|
||||||
err := checkInputsStandard(tx, txStore)
|
err := checkInputsStandard(tx, txStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Attempt to extract a reject code from the error so
|
||||||
|
// it can be retained. When not possible, fall back to
|
||||||
|
// a non standard error.
|
||||||
|
rejectCode, found := extractRejectCode(err)
|
||||||
|
if !found {
|
||||||
|
rejectCode = btcwire.RejectNonstandard
|
||||||
|
}
|
||||||
str := fmt.Sprintf("transaction %v has a non-standard "+
|
str := fmt.Sprintf("transaction %v has a non-standard "+
|
||||||
"input: %v", txHash, err)
|
"input: %v", txHash, err)
|
||||||
return TxRuleError(str)
|
return txRuleError(rejectCode, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,7 +895,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
str := fmt.Sprintf("transaction %v has %d fees which is under "+
|
str := fmt.Sprintf("transaction %v has %d fees which is under "+
|
||||||
"the required amount of %d", txHash, txFee,
|
"the required amount of %d", txHash, txFee,
|
||||||
minRequiredFee)
|
minRequiredFee)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectInsufficientFee, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free-to-relay transactions are rate limited here to prevent
|
// Free-to-relay transactions are rate limited here to prevent
|
||||||
|
@ -888,7 +912,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
if mp.pennyTotal >= cfg.FreeTxRelayLimit*10*1000 {
|
if mp.pennyTotal >= cfg.FreeTxRelayLimit*10*1000 {
|
||||||
str := fmt.Sprintf("transaction %v has 0 fees and has "+
|
str := fmt.Sprintf("transaction %v has 0 fees and has "+
|
||||||
"been rejected by the rate limiter", txHash)
|
"been rejected by the rate limiter", txHash)
|
||||||
return TxRuleError(str)
|
return txRuleError(btcwire.RejectInsufficientFee, str)
|
||||||
}
|
}
|
||||||
oldTotal := mp.pennyTotal
|
oldTotal := mp.pennyTotal
|
||||||
|
|
||||||
|
@ -903,6 +927,9 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNe
|
||||||
err = btcchain.ValidateTransactionScripts(tx, txStore,
|
err = btcchain.ValidateTransactionScripts(tx, txStore,
|
||||||
standardScriptVerifyFlags)
|
standardScriptVerifyFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if cerr, ok := err.(btcchain.RuleError); ok {
|
||||||
|
return chainRuleError(cerr)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,7 +1063,14 @@ func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit b
|
||||||
// The transaction is an orphan (has inputs missing). Reject
|
// The transaction is an orphan (has inputs missing). Reject
|
||||||
// it if the flag to allow orphans is not set.
|
// it if the flag to allow orphans is not set.
|
||||||
if !allowOrphan {
|
if !allowOrphan {
|
||||||
return TxRuleError("transaction spends unknown inputs")
|
// NOTE: RejectDuplicate is really not an accurate
|
||||||
|
// reject code here, but it matches the reference
|
||||||
|
// implementation and there isn't a better choice due
|
||||||
|
// to the limited number of reject codes. Missing
|
||||||
|
// inputs is assumed to mean they are already spent
|
||||||
|
// which is not really always the case.
|
||||||
|
return txRuleError(btcwire.RejectDuplicate,
|
||||||
|
"transaction spends unknown inputs")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Potentially add the orphan transaction to the orphan pool.
|
// Potentially add the orphan transaction to the orphan pool.
|
||||||
|
|
134
mempoolerror.go
Normal file
134
mempoolerror.go
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright (c) 2014 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/conformal/btcchain"
|
||||||
|
"github.com/conformal/btcwire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RuleError identifies a rule violation. It is used to indicate that
|
||||||
|
// processing of a 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 use the Err field to access the
|
||||||
|
// underlying error, which will be either a TxRuleError or a btcchain.RuleError.
|
||||||
|
type RuleError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the error interface and prints human-readable errors.
|
||||||
|
func (e RuleError) Error() string {
|
||||||
|
if e.Err == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxRuleError identifies a rule violation. It is used to indicate that
|
||||||
|
// processing of a 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 TxRuleError struct {
|
||||||
|
RejectCode btcwire.RejectCode // The code to send with reject messages
|
||||||
|
Description string // Human readable description of the issue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the error interface and prints human-readable errors.
|
||||||
|
func (e TxRuleError) Error() string {
|
||||||
|
return e.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// txRuleError creates an underlying TxRuleError with the given a set of
|
||||||
|
// arguments and returns a RuleError that encapsulates it.
|
||||||
|
func txRuleError(c btcwire.RejectCode, desc string) RuleError {
|
||||||
|
return RuleError{
|
||||||
|
Err: TxRuleError{RejectCode: c, Description: desc},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chainRuleError returns a RuleError that encapsulates the given
|
||||||
|
// btcchain.RuleError.
|
||||||
|
func chainRuleError(chainErr btcchain.RuleError) RuleError {
|
||||||
|
return RuleError{
|
||||||
|
Err: chainErr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractRejectCode attempts to return a relevant reject code for a given error
|
||||||
|
// by examining the error for known types. It will return true if a code
|
||||||
|
// was successfully extracted.
|
||||||
|
func extractRejectCode(err error) (btcwire.RejectCode, bool) {
|
||||||
|
// Pull the underlying error out of a RuleError.
|
||||||
|
if rerr, ok := err.(RuleError); ok {
|
||||||
|
err = rerr.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch err := err.(type) {
|
||||||
|
case btcchain.RuleError:
|
||||||
|
// Convert the chain error to a reject code.
|
||||||
|
var code btcwire.RejectCode
|
||||||
|
switch err.ErrorCode {
|
||||||
|
// Rejected due to duplicate.
|
||||||
|
case btcchain.ErrDuplicateBlock:
|
||||||
|
fallthrough
|
||||||
|
case btcchain.ErrDoubleSpend:
|
||||||
|
code = btcwire.RejectDuplicate
|
||||||
|
|
||||||
|
// Rejected due to obsolete version.
|
||||||
|
case btcchain.ErrBlockVersionTooOld:
|
||||||
|
code = btcwire.RejectObsolete
|
||||||
|
|
||||||
|
// Rejected due to checkpoint.
|
||||||
|
case btcchain.ErrCheckpointTimeTooOld:
|
||||||
|
fallthrough
|
||||||
|
case btcchain.ErrDifficultyTooLow:
|
||||||
|
fallthrough
|
||||||
|
case btcchain.ErrBadCheckpoint:
|
||||||
|
fallthrough
|
||||||
|
case btcchain.ErrForkTooOld:
|
||||||
|
code = btcwire.RejectCheckpoint
|
||||||
|
|
||||||
|
// Everything else is due to the block or transaction being invalid.
|
||||||
|
default:
|
||||||
|
code = btcwire.RejectInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return code, true
|
||||||
|
|
||||||
|
case TxRuleError:
|
||||||
|
return err.RejectCode, true
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
return btcwire.RejectInvalid, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcwire.RejectInvalid, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// errToRejectErr examines the underlying type of the error and returns a reject
|
||||||
|
// code and string appropriate to be sent in a btcwire.MsgReject message.
|
||||||
|
func errToRejectErr(err error) (btcwire.RejectCode, string) {
|
||||||
|
// Return the reject code along with the error text if it can be
|
||||||
|
// extracted from the error.
|
||||||
|
rejectCode, found := extractRejectCode(err)
|
||||||
|
if found {
|
||||||
|
return rejectCode, err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a generic rejected string if there is no error. This really
|
||||||
|
// should not happen unless the code elsewhere is not setting an error
|
||||||
|
// as it should be, but it's best to be safe and simply return a generic
|
||||||
|
// string rather than allowing the following code that derferences the
|
||||||
|
// err to panic.
|
||||||
|
if err == nil {
|
||||||
|
return btcwire.RejectInvalid, "rejected"
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the underlying error is not one of the above cases, just return
|
||||||
|
// btcwire.RejectInvalid with a generic rejected string plus the error
|
||||||
|
// text.
|
||||||
|
return btcwire.RejectInvalid, "rejected: " + err.Error()
|
||||||
|
}
|
110
peer.go
110
peer.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -26,7 +27,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// maxProtocolVersion is the max protocol version the peer supports.
|
// maxProtocolVersion is the max protocol version the peer supports.
|
||||||
maxProtocolVersion = 70001
|
maxProtocolVersion = 70002
|
||||||
|
|
||||||
// outputBufferSize is the number of elements the output channels use.
|
// outputBufferSize is the number of elements the output channels use.
|
||||||
outputBufferSize = 50
|
outputBufferSize = 50
|
||||||
|
@ -353,12 +354,35 @@ func (p *peer) handleVersionMsg(msg *btcwire.MsgVersion) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.StatsMtx.Lock() // Updating a bunch of stats.
|
// Notify and disconnect clients that have a protocol version that is
|
||||||
|
// too old.
|
||||||
|
if msg.ProtocolVersion < int32(btcwire.MultipleAddressVersion) {
|
||||||
|
// Send a reject message indicating the protocol version is
|
||||||
|
// obsolete and wait for the message to be sent before
|
||||||
|
// disconnecting.
|
||||||
|
reason := fmt.Sprintf("protocol version must be %d or greater",
|
||||||
|
btcwire.MultipleAddressVersion)
|
||||||
|
p.PushRejectMsg(msg.Command(), btcwire.RejectObsolete, reason,
|
||||||
|
nil, true)
|
||||||
|
p.Disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updating a bunch of stats.
|
||||||
|
p.StatsMtx.Lock()
|
||||||
|
|
||||||
// Limit to one version message per peer.
|
// Limit to one version message per peer.
|
||||||
if p.versionKnown {
|
if p.versionKnown {
|
||||||
p.logError("Only one version message per peer is allowed %s.",
|
p.logError("Only one version message per peer is allowed %s.",
|
||||||
p)
|
p)
|
||||||
p.StatsMtx.Unlock()
|
p.StatsMtx.Unlock()
|
||||||
|
|
||||||
|
// Send an reject message indicating the version message was
|
||||||
|
// incorrectly sent twice and wait for the message to be sent
|
||||||
|
// before disconnecting.
|
||||||
|
p.PushRejectMsg(msg.Command(), btcwire.RejectDuplicate,
|
||||||
|
"duplicate version message", nil, true)
|
||||||
|
|
||||||
p.Disconnect()
|
p.Disconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -653,6 +677,40 @@ func (p *peer) PushGetHeadersMsg(locator btcchain.BlockLocator, stopHash *btcwir
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushRejectMsg sends a reject message for the provided command, reject code,
|
||||||
|
// and reject reason, and hash. The hash will only be used when the command
|
||||||
|
// is a tx or block and should be nil in other cases. The wait parameter will
|
||||||
|
// cause the function to block until the reject message has actually been sent.
|
||||||
|
func (p *peer) PushRejectMsg(command string, code btcwire.RejectCode, reason string, hash *btcwire.ShaHash, wait bool) {
|
||||||
|
// Don't bother sending the reject message if the protocol version
|
||||||
|
// is too low.
|
||||||
|
if p.VersionKnown() && p.ProtocolVersion() < btcwire.RejectVersion {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := btcwire.NewMsgReject(command, code, reason)
|
||||||
|
if command == btcwire.CmdTx || command == btcwire.CmdBlock {
|
||||||
|
if hash == nil {
|
||||||
|
peerLog.Warnf("Sending a reject message for command "+
|
||||||
|
"type %v which should have specified a hash "+
|
||||||
|
"but does not", command)
|
||||||
|
hash = &zeroHash
|
||||||
|
}
|
||||||
|
msg.Hash = *hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message without waiting if the caller has not requested it.
|
||||||
|
if !wait {
|
||||||
|
p.QueueMessage(msg, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message and block until it has been sent before returning.
|
||||||
|
doneChan := make(chan struct{}, 1)
|
||||||
|
p.QueueMessage(msg, doneChan)
|
||||||
|
<-doneChan
|
||||||
|
}
|
||||||
|
|
||||||
// handleMemPoolMsg is invoked when a peer receives a mempool bitcoin message.
|
// handleMemPoolMsg is invoked when a peer receives a mempool bitcoin message.
|
||||||
// It creates and sends an inventory message with the contents of the memory
|
// It creates and sends an inventory message with the contents of the memory
|
||||||
// pool up to the maximum inventory allowed per message. When the peer has a
|
// pool up to the maximum inventory allowed per message. When the peer has a
|
||||||
|
@ -1231,9 +1289,11 @@ func (p *peer) writeMessage(msg btcwire.Message) {
|
||||||
switch msg.(type) {
|
switch msg.(type) {
|
||||||
case *btcwire.MsgVersion:
|
case *btcwire.MsgVersion:
|
||||||
// This is OK.
|
// This is OK.
|
||||||
|
case *btcwire.MsgReject:
|
||||||
|
// This is OK.
|
||||||
default:
|
default:
|
||||||
// We drop all messages other than version if we
|
// Drop all messages other than version and reject if
|
||||||
// haven't done the handshake already.
|
// the handshake has not already been done.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1332,10 +1392,32 @@ out:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only log the error if we're not forcibly disconnecting.
|
// Only log the error and possibly send reject message
|
||||||
|
// if we're not forcibly disconnecting.
|
||||||
if atomic.LoadInt32(&p.disconnect) == 0 {
|
if atomic.LoadInt32(&p.disconnect) == 0 {
|
||||||
p.logError("Can't read message from %s: %v",
|
errMsg := fmt.Sprintf("Can't read message "+
|
||||||
p, err)
|
"from %s: %v", p, err)
|
||||||
|
p.logError(errMsg)
|
||||||
|
|
||||||
|
// Only send the reject message if it's not
|
||||||
|
// because the remote client disconnected.
|
||||||
|
if err != io.EOF {
|
||||||
|
// Push a reject message for the
|
||||||
|
// malformed message and wait for the
|
||||||
|
// message to be sent before
|
||||||
|
// disconnecting.
|
||||||
|
//
|
||||||
|
// NOTE: Ideally this would include the
|
||||||
|
// command in the header if at least
|
||||||
|
// that much of the message was valid,
|
||||||
|
// but that is not currently exposed by
|
||||||
|
// btcwire, so just used malformed for
|
||||||
|
// the command.
|
||||||
|
p.PushRejectMsg("malformed",
|
||||||
|
btcwire.RejectMalformed, errMsg,
|
||||||
|
nil, true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
|
@ -1344,8 +1426,14 @@ out:
|
||||||
p.StatsMtx.Unlock()
|
p.StatsMtx.Unlock()
|
||||||
|
|
||||||
// Ensure version message comes first.
|
// Ensure version message comes first.
|
||||||
if _, ok := rmsg.(*btcwire.MsgVersion); !ok && !p.VersionKnown() {
|
if vmsg, ok := rmsg.(*btcwire.MsgVersion); !ok && !p.VersionKnown() {
|
||||||
p.logError("A version message must precede all others")
|
errStr := "A version message must precede all others"
|
||||||
|
p.logError(errStr)
|
||||||
|
|
||||||
|
// Push a reject message and wait for the message to be
|
||||||
|
// sent before disconnecting.
|
||||||
|
p.PushRejectMsg(vmsg.Command(), btcwire.RejectMalformed,
|
||||||
|
errStr, nil, true)
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,6 +1505,10 @@ out:
|
||||||
case *btcwire.MsgFilterLoad:
|
case *btcwire.MsgFilterLoad:
|
||||||
p.handleFilterLoadMsg(msg)
|
p.handleFilterLoadMsg(msg)
|
||||||
|
|
||||||
|
case *btcwire.MsgReject:
|
||||||
|
// Nothing to do currently. Logging of the rejected
|
||||||
|
// message is handled already in readMessage.
|
||||||
|
|
||||||
default:
|
default:
|
||||||
peerLog.Debugf("Received unhandled message of type %v: Fix Me",
|
peerLog.Debugf("Received unhandled message of type %v: Fix Me",
|
||||||
rmsg.Command())
|
rmsg.Command())
|
||||||
|
|
|
@ -2868,7 +2868,7 @@ func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan st
|
||||||
// so log it as an actual error. In both cases, a JSON-RPC
|
// so log it as an actual error. In both cases, a JSON-RPC
|
||||||
// error is returned to the client with the deserialization
|
// error is returned to the client with the deserialization
|
||||||
// error code (to match bitcoind behavior).
|
// error code (to match bitcoind behavior).
|
||||||
if _, ok := err.(TxRuleError); ok {
|
if _, ok := err.(RuleError); ok {
|
||||||
rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(),
|
rpcsLog.Debugf("Rejected transaction %v: %v", tx.Sha(),
|
||||||
err)
|
err)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue