From 1dcc89cca9d3c45479b319444735a4881b5180db Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 15 Jan 2018 23:27:27 +0100 Subject: [PATCH] lnwallet/btcwallet: return concrete error type from PublishTransaction --- lnwallet/btcwallet/btcwallet.go | 85 ++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index c6229edc..4c4f50f9 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math" + "strings" "sync" "time" @@ -399,9 +400,89 @@ func (b *BtcWallet) ListUnspentWitness(minConfs int32) ([]*lnwallet.Utxo, error) } // PublishTransaction performs cursory validation (dust checks, etc), then -// finally broadcasts the passed transaction to the Bitcoin network. +// finally broadcasts the passed transaction to the Bitcoin network. If +// publishing the transaction fails, an error describing the reason is +// returned (currently ErrDoubleSpend). If the transaction is already +// published to the network (either in the mempool or chain) no error +// will be returned. func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx) error { - return b.wallet.PublishTransaction(tx) + if err := b.wallet.PublishTransaction(tx); err != nil { + switch b.chain.(type) { + case *chain.RPCClient: + if strings.Contains(err.Error(), "already have") { + // Transaction was already in the mempool, do + // not treat as an error. We do this to mimic + // the behaviour of bitcoind, which will not + // return an error if a transaction in the + // mempool is sent again using the + // sendrawtransaction RPC call. + return nil + } + if strings.Contains(err.Error(), "already exists") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "already spent") { + // Output was already spent. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "orphan transaction") { + // Transaction is spending either output that + // is missing or already spent. + return lnwallet.ErrDoubleSpend + } + + case *chain.BitcoindClient: + if strings.Contains(err.Error(), "txn-already-in-mempool") { + // Transaction in mempool, treat as non-error. + return nil + } + if strings.Contains(err.Error(), "txn-already-known") { + // Transaction in mempool, treat as non-error. + return nil + } + if strings.Contains(err.Error(), "already in block") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "txn-mempool-conflict") { + // Output was spent by other transaction + // already in the mempool. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "insufficient fee") { + // RBF enabled transaction did not have enough fee. + return lnwallet.ErrDoubleSpend + } + if strings.Contains(err.Error(), "Missing inputs") { + // Transaction is spending either output that + // is missing or already spent. + return lnwallet.ErrDoubleSpend + } + + case *chain.NeutrinoClient: + if strings.Contains(err.Error(), "already have") { + // Transaction was already in the mempool, do + // not treat as an error. + return nil + } + if strings.Contains(err.Error(), "already exists") { + // Transaction was already mined, we don't + // consider this an error. + return nil + } + if strings.Contains(err.Error(), "already spent") { + // Output was already spent. + return lnwallet.ErrDoubleSpend + } + + default: + } + return err + } + return nil } // extractBalanceDelta extracts the net balance delta from the PoV of the