Skip to content

Commit 04af7f8

Browse files
authored
Merge pull request #210 from joostjager/mpp-probe
loopin: mpp pre-swap probe
2 parents 4bb59f8 + c8666ca commit 04af7f8

12 files changed

+255
-105
lines changed

loopdb/protocol_version.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ const (
3535
// HTLC v2 scrips for swaps.
3636
ProtocolVersionHtlcV2 ProtocolVersion = 5
3737

38+
// ProtocolVersionMultiLoopIn indicates that the client creates a probe
39+
// invoice so that the server can perform a multi-path probe.
40+
ProtocolVersionMultiLoopIn ProtocolVersion = 6
41+
3842
// ProtocolVersionUnrecorded is set for swaps were created before we
3943
// started saving protocol version with swaps.
4044
ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32
4145

42-
// CurrentRpcProtocolVersion defines the version of the RPC protocol
46+
// CurrentRPCProtocolVersion defines the version of the RPC protocol
4347
// that is currently supported by the loop client.
44-
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_HTLC_V2
48+
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_MULTI_LOOP_IN
4549

46-
// CurrentInteranlProtocolVersionInternal defines the RPC current
47-
// protocol in the internal representation.
50+
// CurrentInternalProtocolVersion defines the RPC current protocol in
51+
// the internal representation.
4852
CurrentInternalProtocolVersion = ProtocolVersion(CurrentRPCProtocolVersion)
4953
)
5054

loopdb/protocol_version_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func TestProtocolVersionSanity(t *testing.T) {
2020
ProtocolVersionPreimagePush,
2121
ProtocolVersionUserExpiryLoopOut,
2222
ProtocolVersionHtlcV2,
23+
ProtocolVersionMultiLoopIn,
2324
}
2425

2526
rpcVersions := [...]looprpc.ProtocolVersion{
@@ -29,6 +30,7 @@ func TestProtocolVersionSanity(t *testing.T) {
2930
looprpc.ProtocolVersion_PREIMAGE_PUSH_LOOP_OUT,
3031
looprpc.ProtocolVersion_USER_EXPIRY_LOOP_OUT,
3132
looprpc.ProtocolVersion_HTLC_V2,
33+
looprpc.ProtocolVersion_MULTI_LOOP_IN,
3234
}
3335

3436
require.Equal(t, len(versions), len(rpcVersions))

loopin.go

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/rand"
66
"crypto/sha256"
7+
"errors"
78
"fmt"
89
"sync"
910
"time"
@@ -135,17 +136,55 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
135136
return nil, err
136137
}
137138

139+
// Create the probe invoice in lnd. Derive the payment hash
140+
// deterministically from the swap hash in such a way that the server
141+
// can be sure that we don't know the preimage.
142+
probeHash := lntypes.Hash(sha256.Sum256(swapHash[:]))
143+
probeHash[0] ^= 1
144+
145+
log.Infof("Creating probe invoice %v", probeHash)
146+
probeInvoice, err := cfg.lnd.Invoices.AddHoldInvoice(
147+
globalCtx, &invoicesrpc.AddInvoiceData{
148+
Hash: &probeHash,
149+
Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt),
150+
Memo: "loop in probe",
151+
Expiry: 3600,
152+
},
153+
)
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
// Create a cancellable context that is used for monitoring the probe.
159+
probeWaitCtx, probeWaitCancel := context.WithCancel(globalCtx)
160+
161+
// Launch a goroutine to monitor the probe.
162+
probeResult, err := awaitProbe(probeWaitCtx, *cfg.lnd, probeHash)
163+
if err != nil {
164+
probeWaitCancel()
165+
return nil, fmt.Errorf("probe failed: %v", err)
166+
}
167+
138168
// Post the swap parameters to the swap server. The response contains
139169
// the server success key and the expiry height of the on-chain swap
140170
// htlc.
141171
log.Infof("Initiating swap request at height %v", currentHeight)
142172
swapResp, err := cfg.server.NewLoopInSwap(globalCtx, swapHash,
143-
request.Amount, senderKey, swapInvoice, request.LastHop,
173+
request.Amount, senderKey, swapInvoice, probeInvoice,
174+
request.LastHop,
144175
)
176+
probeWaitCancel()
145177
if err != nil {
146178
return nil, fmt.Errorf("cannot initiate swap: %v", err)
147179
}
148180

181+
// Because the context is cancelled, it is guaranteed that we will be
182+
// able to read from the probeResult channel.
183+
err = <-probeResult
184+
if err != nil {
185+
return nil, fmt.Errorf("probe error: %v", err)
186+
}
187+
149188
// Validate the response parameters the prevent us continuing with a
150189
// swap that is based on parameters outside our allowed range.
151190
err = validateLoopInContract(cfg.lnd, currentHeight, request, swapResp)
@@ -209,6 +248,72 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
209248
}, nil
210249
}
211250

251+
// awaitProbe waits for a probe payment to arrive and cancels it. This is a
252+
// workaround for the current lack of multi-path probing.
253+
func awaitProbe(ctx context.Context, lnd lndclient.LndServices,
254+
probeHash lntypes.Hash) (chan error, error) {
255+
256+
// Subscribe to the probe invoice.
257+
updateChan, errChan, err := lnd.Invoices.SubscribeSingleInvoice(
258+
ctx, probeHash,
259+
)
260+
if err != nil {
261+
return nil, err
262+
}
263+
264+
// Wait in the background for the probe to arrive.
265+
probeResult := make(chan error, 1)
266+
267+
go func() {
268+
for {
269+
select {
270+
case update := <-updateChan:
271+
switch update.State {
272+
case channeldb.ContractAccepted:
273+
log.Infof("Server probe successful")
274+
probeResult <- nil
275+
276+
// Cancel probe invoice so that the
277+
// server will know that its probe was
278+
// successful.
279+
err := lnd.Invoices.CancelInvoice(
280+
ctx, probeHash,
281+
)
282+
if err != nil {
283+
log.Errorf("Cancel probe "+
284+
"invoice: %v", err)
285+
}
286+
287+
return
288+
289+
case channeldb.ContractCanceled:
290+
probeResult <- errors.New(
291+
"probe invoice expired")
292+
293+
return
294+
295+
case channeldb.ContractSettled:
296+
probeResult <- errors.New(
297+
"impossible that probe " +
298+
"invoice was settled")
299+
300+
return
301+
}
302+
303+
case err := <-errChan:
304+
probeResult <- err
305+
return
306+
307+
case <-ctx.Done():
308+
probeResult <- ctx.Err()
309+
return
310+
}
311+
}
312+
}()
313+
314+
return probeResult, nil
315+
}
316+
212317
// resumeLoopInSwap returns a swap object representing a pending swap that has
213318
// been restored from the database.
214319
func resumeLoopInSwap(reqContext context.Context, cfg *swapConfig,

loopin_testcontext_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type loopInTestContext struct {
2222

2323
func newLoopInTestContext(t *testing.T) *loopInTestContext {
2424
lnd := test.NewMockLnd()
25-
server := newServerMock()
25+
server := newServerMock(lnd)
2626
store := newStoreMock(t)
2727
sweeper := sweep.Sweeper{Lnd: &lnd.LndServices}
2828

loopout_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestLoopOutPaymentParameters(t *testing.T) {
2727
// Set up test context objects.
2828
lnd := test.NewMockLnd()
2929
ctx := test.NewContext(t, lnd)
30-
server := newServerMock()
30+
server := newServerMock(lnd)
3131
store := newStoreMock(t)
3232

3333
expiryChan := make(chan time.Time)
@@ -138,7 +138,7 @@ func TestLateHtlcPublish(t *testing.T) {
138138

139139
ctx := test.NewContext(t, lnd)
140140

141-
server := newServerMock()
141+
server := newServerMock(lnd)
142142

143143
store := newStoreMock(t)
144144

@@ -223,7 +223,7 @@ func TestCustomSweepConfTarget(t *testing.T) {
223223

224224
lnd := test.NewMockLnd()
225225
ctx := test.NewContext(t, lnd)
226-
server := newServerMock()
226+
server := newServerMock(lnd)
227227

228228
// Use the highest sweep confirmation target before we attempt to use
229229
// the default.
@@ -424,7 +424,7 @@ func TestPreimagePush(t *testing.T) {
424424

425425
lnd := test.NewMockLnd()
426426
ctx := test.NewContext(t, lnd)
427-
server := newServerMock()
427+
server := newServerMock(lnd)
428428

429429
// Start with a high confirmation delta which will have a very high fee
430430
// attached to it.

0 commit comments

Comments
 (0)