Handle interception probing payments

probing payments uses a probing payment hash which is:
sha256("probing-01:" || payment_hash).

When the interceptor detects such a hash for a payment which is supposed
to trigger a channel creation , it checks if the destination is online,
and if online, fails with INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS error in
order to let the payer knows that the payment would be successful.
This commit is contained in:
Yaacov Akiba Slama
2020-11-08 17:46:26 +02:00
parent faff8f60a9
commit f407ec9e9c
5 changed files with 53 additions and 20 deletions

14
db.go
View File

@@ -24,25 +24,25 @@ func pgConnect() error {
return nil
}
func paymentInfo(paymentHash []byte) ([]byte, []byte, int64, int64, []byte, uint32, error) {
func paymentInfo(htlcPaymentHash []byte) ([]byte, []byte, []byte, int64, int64, []byte, uint32, error) {
var (
paymentSecret, destination []byte
paymentHash, paymentSecret, destination []byte
incomingAmountMsat, outgoingAmountMsat int64
fundingTxID []byte
fundingTxOutnum pgtype.Int4
)
err := pgxPool.QueryRow(context.Background(),
`SELECT payment_secret, destination, incoming_amount_msat, outgoing_amount_msat, funding_tx_id, funding_tx_outnum
`SELECT payment_hash, payment_secret, destination, incoming_amount_msat, outgoing_amount_msat, funding_tx_id, funding_tx_outnum
FROM payments
WHERE payment_hash=$1`,
paymentHash).Scan(&paymentSecret, &destination, &incomingAmountMsat, &outgoingAmountMsat, &fundingTxID, &fundingTxOutnum)
WHERE payment_hash=$1 OR sha256('probing-01:' || payment_hash)=$1`,
htlcPaymentHash).Scan(&paymentHash, &paymentSecret, &destination, &incomingAmountMsat, &outgoingAmountMsat, &fundingTxID, &fundingTxOutnum)
if err != nil {
if err == pgx.ErrNoRows {
err = nil
}
return nil, nil, 0, 0, nil, 0, err
return nil, nil, nil, 0, 0, nil, 0, err
}
return paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, fundingTxID, uint32(fundingTxOutnum.Int), nil
return paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, fundingTxID, uint32(fundingTxOutnum.Int), nil
}
func setFundingTx(paymentHash, fundingTxID []byte, fundingTxOutnum int) error {

8
go.mod
View File

@@ -4,16 +4,16 @@ go 1.14
require (
github.com/aws/aws-sdk-go v1.30.20
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcd v0.20.1-beta.0.20200730232343-1db1b6f8217f
github.com/caddyserver/certmagic v0.11.2
github.com/golang/protobuf v1.4.2
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0
github.com/jackc/pgtype v1.4.2
github.com/jackc/pgx/v4 v4.8.1
github.com/lightningnetwork/lightning-onion v1.0.1
github.com/lightningnetwork/lnd v0.10.0-beta
github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea
github.com/lightningnetwork/lnd v0.11.0-beta
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
google.golang.org/grpc v1.31.0
)
replace github.com/lightningnetwork/lnd v0.10.0-beta => github.com/breez/lnd v0.10.0-beta.rc6.0.20200727142715-f67a1052c0e0
replace github.com/lightningnetwork/lnd v0.11.0-beta => github.com/breez/lnd v0.11.0-beta.rc4.0.20201101122458-227226f00b18

View File

@@ -3,6 +3,7 @@ package main
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"log"
"math/big"
@@ -32,6 +33,23 @@ func checkPayment(incomingAmountMsat, outgoingAmountMsat int64) error {
return nil
}
func isConnected(ctx context.Context, client lnrpc.LightningClient, destination []byte) error {
pubKey := hex.EncodeToString(destination)
r, err := client.ListPeers(ctx, &lnrpc.ListPeersRequest{LatestError: true})
if err != nil {
log.Printf("client.ListPeers() error: %v", err)
return fmt.Errorf("client.ListPeers() error: %w", err)
}
for _, peer := range r.Peers {
if pubKey == peer.PubKey {
log.Printf("destination online: %x", destination)
return nil
}
}
log.Printf("destination offline: %x", destination)
return fmt.Errorf("destination offline")
}
func openChannel(ctx context.Context, client lnrpc.LightningClient, paymentHash, destination []byte, incomingAmountMsat int64) ([]byte, uint32, error) {
capacity := incomingAmountMsat/1000 + additionalChannelCapacity
channelPoint, err := client.OpenChannelSync(ctx, &lnrpc.OpenChannelRequest{
@@ -104,15 +122,16 @@ func intercept() {
request.OnionBlob,
)
paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, fundingTxID, fundingTxOutnum, err := paymentInfo(request.PaymentHash)
paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, fundingTxID, fundingTxOutnum, err := paymentInfo(request.PaymentHash)
if err != nil {
log.Printf("paymentInfo(%x) error: %v", request.PaymentHash, err)
}
log.Printf("paymentSecret: %x\ndestination:%x\nincomingAmountMsat:%v\noutgoingAmountMsat:%v\n\n",
paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat)
log.Printf("paymentHash:%x\npaymentSecret:%x\ndestination:%x\nincomingAmountMsat:%v\noutgoingAmountMsat:%v\n\n",
paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat)
if paymentSecret != nil {
if fundingTxID == nil {
if bytes.Compare(paymentHash, request.PaymentHash) == 0 {
fundingTxID, fundingTxOutnum, err = openChannel(clientCtx, client, request.PaymentHash, destination, incomingAmountMsat)
log.Printf("openChannel(%x, %v) err: %v", destination, incomingAmountMsat, err)
if err != nil {
@@ -122,6 +141,18 @@ func intercept() {
})
continue
}
} else { //probing
failureCode := routerrpc.ForwardHtlcInterceptResponse_TEMPORARY_CHANNEL_FAILURE
if err := isConnected(clientCtx, client, destination); err == nil {
failureCode = routerrpc.ForwardHtlcInterceptResponse_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS
}
interceptorClient.Send(&routerrpc.ForwardHtlcInterceptResponse{
IncomingCircuitKey: request.IncomingCircuitKey,
Action: routerrpc.ResolveHoldForwardAction_FAIL,
FailureCode: failureCode,
})
continue
}
}
pubKey, err := btcec.ParsePubKey(destination, btcec.S256())

View File

@@ -0,0 +1 @@
DROP INDEX probe_payment_hash;

View File

@@ -0,0 +1 @@
CREATE INDEX probe_payment_hash ON public.payments (sha256('probing-01:' || payment_hash));