-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
probe payment as sanity check #260
Changes from 5 commits
da1fe17
da8ecf3
e5cbb6b
5c30387
dbec5bb
a6af654
52e2ff3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -259,6 +259,13 @@ func (cl *ClightningClient) getMaxHtlcAmtMsat(scid, nodeId string) (uint64, erro | |
return htlcMaximumMilliSatoshis, nil | ||
} | ||
|
||
func min(x, y uint64) uint64 { | ||
if x < y { | ||
return x | ||
} | ||
return y | ||
} | ||
|
||
// SpendableMsat returns an estimate of the total we could send through the | ||
// channel with given scid. Falls back to the owned amount in the channel. | ||
func (cl *ClightningClient) SpendableMsat(scid string) (uint64, error) { | ||
|
@@ -288,13 +295,6 @@ func (cl *ClightningClient) SpendableMsat(scid string) (uint64, error) { | |
return 0, fmt.Errorf("could not find a channel with scid: %s", scid) | ||
} | ||
|
||
func min(x, y uint64) uint64 { | ||
if x < y { | ||
return x | ||
} | ||
return y | ||
} | ||
|
||
// ReceivableMsat returns an estimate of the total we could receive through the | ||
// channel with given scid. | ||
func (cl *ClightningClient) ReceivableMsat(scid string) (uint64, error) { | ||
|
@@ -619,6 +619,63 @@ func (cl *ClightningClient) GetPeers() []string { | |
return peerlist | ||
} | ||
|
||
// ProbePayment trying to pay via a route with a random payment hash | ||
// that the receiver doesn't have the preimage of. | ||
// The receiver node aren't able to settle the payment. | ||
// When the probe is successful, the receiver will return | ||
// a incorrect_or_unknown_payment_details error to the sender. | ||
func (cl *ClightningClient) ProbePayment(scid string, amountMsat uint64) (bool, string, error) { | ||
var res ListPeerChannelsResponse | ||
err := cl.glightning.Request(ListPeerChannelsRequest{}, &res) | ||
if err != nil { | ||
return false, "", fmt.Errorf("ListPeerChannelsRequest() %w", err) | ||
} | ||
var channel PeerChannel | ||
for _, ch := range res.Channels { | ||
if ch.ShortChannelId == lightning.Scid(scid).ClnStyle() { | ||
if err := cl.checkChannel(ch); err != nil { | ||
return false, "", err | ||
} | ||
channel = ch | ||
} | ||
} | ||
|
||
route, err := cl.glightning.GetRoute(channel.PeerId, amountMsat, 1, 0, cl.nodeId, 0, nil, 1) | ||
if err != nil { | ||
return false, "", fmt.Errorf("GetRoute() %w", err) | ||
} | ||
preimage, err := lightning.GetPreimage() | ||
if err != nil { | ||
return false, "", fmt.Errorf("GetPreimage() %w", err) | ||
} | ||
paymentHash := preimage.Hash().String() | ||
_, err = cl.glightning.SendPay( | ||
route, | ||
paymentHash, | ||
"", | ||
amountMsat, | ||
"", | ||
"", | ||
0, | ||
) | ||
if err != nil { | ||
return false, "", fmt.Errorf("SendPay() %w", err) | ||
} | ||
_, err = cl.glightning.WaitSendPay(paymentHash, 0) | ||
if err != nil { | ||
pe, ok := err.(*glightning.PaymentError) | ||
if !ok { | ||
return false, "", fmt.Errorf("WaitSendPay() %w", err) | ||
} | ||
faiCodeWireIncorrectOrUnknownPaymentDetails := 203 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: typo |
||
if pe.RpcError.Code != faiCodeWireIncorrectOrUnknownPaymentDetails { | ||
log.Debugf("send pay would be failed. reason:%w", err) | ||
return false, pe.Error(), nil | ||
} | ||
} | ||
return true, "", nil | ||
} | ||
|
||
type Glightninglogger struct { | ||
plugin *glightning.Plugin | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1141,3 +1141,77 @@ func Test_ClnCln_ExcessiveAmount(t *testing.T) { | |
}) | ||
|
||
} | ||
|
||
// Test_ClnCln_StuckChannels tests that the swap fails if the channel is stuck. | ||
// For more information about stuck channel, please check the link. | ||
// https://github.com/lightning/bolts/issues/728 | ||
func Test_ClnCln_StuckChannels(t *testing.T) { | ||
IsIntegrationTest(t) | ||
t.Parallel() | ||
|
||
require := require.New(t) | ||
// repro by using the push_msat in the open_channel. | ||
// Assumption that feperkw is 253perkw in reg test. | ||
bitcoind, lightningds, scid := clnclnSetupWithConfig(t, 3794, 3573, []string{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Numbers checked. They match 1% reserve 37940 msat and a CommitTx fee of 183172 msat. |
||
"--dev-bitcoind-poll=1", | ||
"--dev-fast-gossip", | ||
"--large-channels", | ||
"--min-capacity-sat=1000", | ||
}) | ||
|
||
defer func() { | ||
if t.Failed() { | ||
filter := os.Getenv("PEERSWAP_TEST_FILTER") | ||
pprintFail( | ||
tailableProcess{ | ||
p: bitcoind.DaemonProcess, | ||
lines: defaultLines, | ||
}, | ||
tailableProcess{ | ||
p: lightningds[0].DaemonProcess, | ||
filter: filter, | ||
lines: defaultLines, | ||
}, | ||
tailableProcess{ | ||
p: lightningds[1].DaemonProcess, | ||
lines: defaultLines, | ||
}, | ||
) | ||
} | ||
}() | ||
|
||
var channelBalances []uint64 | ||
var walletBalances []uint64 | ||
for _, lightningd := range lightningds { | ||
b, err := lightningd.GetBtcBalanceSat() | ||
require.NoError(err) | ||
walletBalances = append(walletBalances, b) | ||
|
||
b, err = lightningd.GetChannelBalanceSat(scid) | ||
require.NoError(err) | ||
channelBalances = append(channelBalances, b) | ||
} | ||
|
||
params := &testParams{ | ||
swapAmt: channelBalances[0], | ||
scid: scid, | ||
origTakerWallet: walletBalances[0], | ||
origMakerWallet: walletBalances[1], | ||
origTakerBalance: channelBalances[0], | ||
origMakerBalance: channelBalances[1], | ||
takerNode: lightningds[0], | ||
makerNode: lightningds[1], | ||
takerPeerswap: lightningds[0].DaemonProcess, | ||
makerPeerswap: lightningds[1].DaemonProcess, | ||
chainRpc: bitcoind.RpcProxy, | ||
chaind: bitcoind, | ||
confirms: BitcoinConfirms, | ||
csv: BitcoinCsv, | ||
swapType: swap.SWAPTYPE_IN, | ||
} | ||
|
||
// Swap in should fail by probing payment as the channel is stuck. | ||
var response map[string]interface{} | ||
err := lightningds[1].Rpc.Request(&clightning.SwapIn{SatAmt: 100, ShortChannelId: params.scid, Asset: "btc"}, &response) | ||
assert.Error(t, err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fail to see the reason in choosing a route that core lightning constructs for us? Should we not construct the route that uses the exact channel for probing that we also use for the payment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree with your suggestion.
Like
PayInvoiceViaChannel
, I have modified to ensure that invoice payments are probed through a direct channel to peers.