rpc: Ignore another "duplicate tx" error.

sendrawtransaction is expected to fail if the transaction already exists in the mempool or a mined block. dcrd returns two different errors which can indicate this situation. Handling for one of them already existed, and this commit adds handling for the other.
This commit is contained in:
Jamie Holdstock 2023-07-01 13:25:12 +01:00 committed by Jamie Holdstock
parent a88c261cb1
commit d8bcc9292f
2 changed files with 23 additions and 9 deletions

View File

@ -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

View File

@ -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)