Skip to content

Commit 8d7a272

Browse files
author
Bjorn Olav Jalborg
committed
loop+loopout: validate hash of swap invoice
This commit fixes a possible exploit by the loop server, where - in a loop out - the server could claim money off-chain, without publishing an on-chain swap htlc. The server could do this by responding with a regular invoice, whose hash is different than the hash in the NewLoopOutSwap request. To prevent the exploit, we validate that the hash of the swap invoice is equal to the hash the client generated.
1 parent 6da8494 commit 8d7a272

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

loopout.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
8686
return nil, fmt.Errorf("cannot initiate swap: %v", err)
8787
}
8888

89-
err = validateLoopOutContract(cfg.lnd, currentHeight, request, swapResp)
89+
err = validateLoopOutContract(cfg.lnd, currentHeight, request, swapHash, swapResp)
9090
if err != nil {
9191
return nil, err
9292
}
9393

94-
// Instantie a struct that contains all required data to start the swap.
94+
// Instantiate a struct that contains all required data to start the swap.
9595
initiationTime := time.Now()
9696

9797
contract := loopdb.LoopOutContract{
@@ -660,21 +660,26 @@ func (s *loopOutSwap) sweep(ctx context.Context,
660660
// validateLoopOutContract validates the contract parameters against our
661661
// request.
662662
func validateLoopOutContract(lnd *lndclient.LndServices,
663-
height int32,
664-
request *OutRequest,
663+
height int32, request *OutRequest, swapHash lntypes.Hash,
665664
response *newLoopOutResponse) error {
666665

667666
// Check invoice amounts.
668667
chainParams := lnd.ChainParams
669668

670-
swapInvoiceAmt, err := swap.GetInvoiceAmt(
669+
swapInvoiceHash, swapInvoiceAmt, err := swap.DecodeInvoice(
671670
chainParams, response.swapInvoice,
672671
)
673672
if err != nil {
674673
return err
675674
}
676675

677-
prepayInvoiceAmt, err := swap.GetInvoiceAmt(
676+
if swapInvoiceHash != swapHash {
677+
return fmt.Errorf(
678+
"cannot initiate swap, swap invoice hash %v not equal generated swap hash %v",
679+
swapInvoiceHash, swapHash)
680+
}
681+
682+
_, prepayInvoiceAmt, err := swap.DecodeInvoice(
678683
chainParams, response.prepayInvoice,
679684
)
680685
if err != nil {

swap/fees.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/btcsuite/btcd/chaincfg"
77
"github.com/btcsuite/btcutil"
8+
"github.com/lightningnetwork/lnd/lntypes"
89
"github.com/lightningnetwork/lnd/zpay32"
910
)
1011

@@ -26,21 +27,24 @@ func FeeRateAsPercentage(feeRate int64) float64 {
2627
return float64(feeRate) / (FeeRateTotalParts / 100)
2728
}
2829

29-
// GetInvoiceAmt gets the invoice amount. It requires an amount to be
30-
// specified.
31-
func GetInvoiceAmt(params *chaincfg.Params,
32-
payReq string) (btcutil.Amount, error) {
30+
// DecodeInvoice gets the hash and the amount of an invoice.
31+
// It requires an amount to be specified.
32+
func DecodeInvoice(params *chaincfg.Params,
33+
payReq string) (lntypes.Hash, btcutil.Amount, error) {
3334

3435
swapPayReq, err := zpay32.Decode(
3536
payReq, params,
3637
)
3738
if err != nil {
38-
return 0, err
39+
return lntypes.Hash{}, 0, err
3940
}
4041

4142
if swapPayReq.MilliSat == nil {
42-
return 0, errors.New("no amount in invoice")
43+
return lntypes.Hash{}, 0, errors.New("no amount in invoice")
4344
}
4445

45-
return swapPayReq.MilliSat.ToSatoshis(), nil
46+
var hash lntypes.Hash
47+
copy(hash[:], swapPayReq.PaymentHash[:])
48+
49+
return hash, swapPayReq.MilliSat.ToSatoshis(), nil
4650
}

0 commit comments

Comments
 (0)