diff --git a/rpc/dcrd.go b/rpc/dcrd.go index d0975ae..be5594f 100644 --- a/rpc/dcrd.go +++ b/rpc/dcrd.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "errors" "fmt" + "strings" "github.com/decred/dcrd/chaincfg/v3" dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4" @@ -22,11 +23,14 @@ var ( requiredDcrdVersion = semver{Major: 8, Minor: 0, Patch: 0} ) -// These error codes are defined in dcrd/dcrjson. They are copied here so we -// dont need to import the whole package. const ( + // These numerical error codes are defined in dcrd/dcrjson. Copied here so + // we dont need to import the whole package. ErrRPCDuplicateTx = -40 ErrNoTxInfo = -5 + // This error string is defined in dcrd/internal/mempool. Copied here + // because it is not exported. + ErrUnknownOutputs = "references outputs of unknown or fully-spent transaction" ) // DcrdRPC provides methods for calling dcrd JSON-RPCs without exposing the details @@ -167,16 +171,26 @@ func (c *DcrdRPC) SendRawTransaction(txHex string) error { allowHighFees := false err := c.Call(c.ctx, "sendrawtransaction", nil, txHex, allowHighFees) if err != nil { - // sendrawtransaction returns error code -40 (ErrRPCDuplicateTx) if the - // provided transaction already exists in the mempool or in a mined - // block. - // It's not a problem if the transaction has already been broadcast, so - // we will capture this error and return nil. + + // Ignore errors caused by the transaction already existing in the + // mempool or in a mined block. + + // Error code -40 (ErrRPCDuplicateTx) is completely ignorable because it + // indicates that dcrd definitely already has this transaction. var e *wsrpc.Error if errors.As(err, &e) && e.Code == ErrRPCDuplicateTx { return nil } + // Errors about orphan/spent outputs indicate that dcrd *might* already + // have this transaction. Use getrawtransaction to confirm. + if strings.Contains(err.Error(), ErrUnknownOutputs) { + _, getErr := c.GetRawTransaction(txHex) + if getErr == nil { + return nil + } + } + return err } return nil diff --git a/webapi/payfee.go b/webapi/payfee.go index 59b8aba..0c25a5b 100644 --- a/webapi/payfee.go +++ b/webapi/payfee.go @@ -257,8 +257,8 @@ func (s *Server) payFee(c *gin.Context) { ticket.FeeTxStatus = database.FeeError - if strings.Contains(err.Error(), - "references outputs of unknown or fully-spent transaction") { + // Send the client an explicit error if the issue is unknown outputs. + if strings.Contains(err.Error(), rpc.ErrUnknownOutputs) { s.sendError(types.ErrCannotBroadcastFeeUnknownOutputs, c) } else { s.sendError(types.ErrCannotBroadcastFee, c)