diff --git a/btcjson/jsonrpcerr.go b/btcjson/jsonrpcerr.go index c9431c5a..8996c474 100644 --- a/btcjson/jsonrpcerr.go +++ b/btcjson/jsonrpcerr.go @@ -76,6 +76,9 @@ const ( ErrRPCInvalidTxVout RPCErrorCode = -5 ErrRPCRawTxString RPCErrorCode = -32602 ErrRPCDecodeHexString RPCErrorCode = -22 + ErrRPCTxError RPCErrorCode = -25 + ErrRPCTxRejected RPCErrorCode = -26 + ErrRPCTxAlreadyInChain RPCErrorCode = -27 ) // Errors that are specific to btcd. diff --git a/rpcserver.go b/rpcserver.go index 6ade888d..a3cf1e0f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3323,19 +3323,44 @@ func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan st if err != nil { // When the error is a rule error, it means the transaction was // simply rejected as opposed to something actually going wrong, - // so log it as such. Otherwise, something really did go wrong, - // so log it as an actual error. In both cases, a JSON-RPC - // error is returned to the client with the deserialization - // error code (to match bitcoind behavior). - if _, ok := err.(mempool.RuleError); ok { - rpcsLog.Debugf("Rejected transaction %v: %v", tx.Hash(), - err) - } else { + // so log it as such. Otherwise, something really did go wrong, + // so log it as an actual error and return. + ruleErr, ok := err.(mempool.RuleError) + if !ok { rpcsLog.Errorf("Failed to process transaction %v: %v", tx.Hash(), err) + + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCTxError, + Message: "TX rejected: " + err.Error(), + } } + + rpcsLog.Debugf("Rejected transaction %v: %v", tx.Hash(), err) + + // We'll then map the rule error to the appropriate RPC error, + // matching bitcoind's behavior. + code := btcjson.ErrRPCTxError + if txRuleErr, ok := ruleErr.Err.(mempool.TxRuleError); ok { + errDesc := txRuleErr.Description + switch { + case strings.Contains( + strings.ToLower(errDesc), "orphan transaction", + ): + code = btcjson.ErrRPCTxError + + case strings.Contains( + strings.ToLower(errDesc), "transaction already exists", + ): + code = btcjson.ErrRPCTxAlreadyInChain + + default: + code = btcjson.ErrRPCTxRejected + } + } + return nil, &btcjson.RPCError{ - Code: btcjson.ErrRPCDeserialization, + Code: code, Message: "TX rejected: " + err.Error(), } } @@ -4281,7 +4306,7 @@ func newRPCServer(config *rpcserverConfig) (*rpcServer, error) { gbtWorkState: newGbtWorkState(config.TimeSource), helpCacher: newHelpCacher(), requestProcessShutdown: make(chan struct{}), - quit: make(chan int), + quit: make(chan int), } if cfg.RPCUser != "" && cfg.RPCPass != "" { login := cfg.RPCUser + ":" + cfg.RPCPass