Skip to content

Commit 31b41a1

Browse files
committed
fix(swaps): set cltvLimit correctly
This fixes a bug whereby the `makerCltvDelta` was erroneously used for the `cltvLimit` for the payment from maker to taker. `makerCltvDelta` is irrelevent to the payment to the taker. Instead, this value is set to the time lock of the route found from maker to taker for lnd, raiden currently has no equivalent to `cltvLimit`. In tests, payments would still fail occasionally due to no route found. Adding 3 blocks to the `cltvLimit` value consistently resolved these failures - any fewer than 3 and the payments would still fail. More investigation is warranted into why this is necessary, it's possible that there is a bug in the `cltvLimit` implementation within lnd. Fixes #1158.
1 parent de47115 commit 31b41a1

File tree

8 files changed

+23
-17
lines changed

8 files changed

+23
-17
lines changed

lib/db/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export type CurrencyInstance = CurrencyAttributes & Sequelize.Instance<CurrencyA
2929

3030
/* SwapDeal */
3131
export type SwapDealFactory = Pick<SwapDeal, Exclude<keyof SwapDeal,
32-
'makerToTakerRoutes' | 'price' | 'pairId' | 'isBuy' | 'takerUnits' | 'makerUnits'>>;
32+
'takerMaxTimeLock' | 'price' | 'pairId' | 'isBuy' | 'takerUnits' | 'makerUnits'>>;
3333

3434
export type SwapDealAttributes = SwapDealFactory & {
3535
/** The internal db node id of the counterparty peer for this swap deal. */

lib/lndclient/LndClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,11 @@ class LndClient extends SwapClient {
361361
rHash: deal.rHash,
362362
destination: deal.takerPubKey!,
363363
amount: deal.takerAmount,
364+
finalCltvDelta: deal.takerCltvDelta,
364365
// Enforcing the maximum duration/length of the payment by
365366
// specifying the cltvLimit.
366-
finalCltvDelta: deal.takerCltvDelta,
367-
cltvLimit: deal.makerCltvDelta,
367+
// TODO: investigate why we need to add 3 blocks - if not lnd says route not found
368+
cltvLimit: deal.takerMaxTimeLock! + 3,
368369
});
369370
}
370371
const preimage = await this.executeSendRequest(request);
@@ -408,6 +409,7 @@ class LndClient extends SwapClient {
408409
private executeSendRequest = async (
409410
request: lndrpc.SendRequest,
410411
): Promise<string> => {
412+
this.logger.trace(`sending payment with ${JSON.stringify(request.toObject())}`);
411413
let sendPaymentResponse: lndrpc.SendResponse;
412414
try {
413415
sendPaymentResponse = await this.sendPaymentSync(request);

lib/raidenclient/RaidenClient.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,26 +165,28 @@ class RaidenClient extends SwapClient {
165165
public sendPayment = async (deal: SwapDeal): Promise<string> => {
166166
assert(deal.state === SwapState.Active);
167167
assert(deal.destination);
168-
let amount = 0;
169-
let tokenAddress;
168+
let amount: number;
169+
let tokenAddress: string;
170+
let lock_timeout: number | undefined;
170171
if (deal.role === SwapRole.Maker) {
171172
// we are the maker paying the taker
172173
amount = deal.takerUnits;
173-
tokenAddress = this.tokenAddresses.get(deal.takerCurrency);
174+
tokenAddress = this.tokenAddresses.get(deal.takerCurrency)!;
174175
} else {
175176
// we are the taker paying the maker
176177
amount = deal.makerUnits;
177-
tokenAddress = this.tokenAddresses.get(deal.makerCurrency);
178+
tokenAddress = this.tokenAddresses.get(deal.makerCurrency)!;
179+
lock_timeout = deal.makerCltvDelta!;
178180
}
179181
if (!tokenAddress) {
180182
throw(errors.TOKEN_ADDRESS_NOT_FOUND);
181183
}
182184
const tokenPaymentResponse = await this.tokenPayment({
183185
amount,
186+
lock_timeout,
184187
token_address: tokenAddress,
185188
target_address: deal.destination!,
186189
secret_hash: deal.rHash,
187-
lock_timeout: deal.makerCltvDelta,
188190
});
189191
return this.sanitizeTokenPaymentResponse(tokenPaymentResponse);
190192
}

lib/swaps/Swaps.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import SwapRepository from './SwapRepository';
99
import { OwnOrder, PeerOrder } from '../orderbook/types';
1010
import assert from 'assert';
1111
import { SwapDealInstance } from '../db/types';
12-
import { SwapDeal, SwapSuccess, SanitySwap, ResolveRequest } from './types';
12+
import { SwapDeal, SwapSuccess, SanitySwap, ResolveRequest, Route } from './types';
1313
import { generatePreimageAndHash, setTimeoutPromise } from '../utils/utils';
1414
import { PacketType } from '../p2p/packets';
1515
import SwapClientManager from './SwapClientManager';
@@ -519,8 +519,9 @@ class Swaps extends EventEmitter {
519519
return false;
520520
}
521521

522+
let makerToTakerRoutes: Route[];
522523
try {
523-
deal.makerToTakerRoutes = await takerSwapClient.getRoutes(takerUnits, takerIdentifier, deal.takerCurrency, deal.takerCltvDelta);
524+
makerToTakerRoutes = await takerSwapClient.getRoutes(takerUnits, takerIdentifier, deal.takerCurrency, deal.takerCltvDelta);
524525
} catch (err) {
525526
this.failDeal(deal, SwapFailureReason.UnexpectedClientError, err.message);
526527
await this.sendErrorToPeer({
@@ -533,7 +534,7 @@ class Swaps extends EventEmitter {
533534
return false;
534535
}
535536

536-
if (deal.makerToTakerRoutes.length === 0) {
537+
if (makerToTakerRoutes.length === 0) {
537538
this.failDeal(deal, SwapFailureReason.NoRouteFound, 'Unable to find route to destination');
538539
await this.sendErrorToPeer({
539540
peer,
@@ -563,10 +564,11 @@ class Swaps extends EventEmitter {
563564
if (height) {
564565
this.logger.debug(`got block height of ${height} for ${takerCurrency}`);
565566

566-
const routeAbsoluteTimeLock = deal.makerToTakerRoutes[0].getTotalTimeLock();
567+
const routeAbsoluteTimeLock = makerToTakerRoutes[0].getTotalTimeLock();
567568
this.logger.debug(`choosing a route with total time lock of ${routeAbsoluteTimeLock}`);
568569
const routeTimeLock = routeAbsoluteTimeLock - height;
569570
this.logger.debug(`route time lock: ${routeTimeLock}`);
571+
deal.takerMaxTimeLock = routeTimeLock;
570572

571573
const makerClientLockBuffer = this.swapClientManager.get(makerCurrency)!.lockBuffer;
572574
this.logger.debug(`maker client lock buffer: ${makerClientLockBuffer}`);

lib/swaps/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ export type SwapDeal = {
5656
rHash: string;
5757
/** The hex-encoded preimage. */
5858
rPreimage?: string;
59-
/** The routes the maker should use to send to the taker. */
60-
makerToTakerRoutes?: Route[];
59+
/** The maximum time lock from the maker to the taker in blocks. */
60+
takerMaxTimeLock?: number;
6161
/** The identifier for the payment channel network node we should pay to complete the swap. */
6262
destination?: string;
6363
/** The time when we created this swap deal locally. */

test/jest/LndClient.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ describe('LndClient', () => {
204204
amount: deal.takerAmount,
205205
destination: deal.takerPubKey,
206206
rHash: deal.rHash,
207-
cltvLimit: deal.makerCltvDelta,
207+
cltvLimit: deal.takerMaxTimeLock + 3,
208208
finalCltvDelta: deal.takerCltvDelta,
209209
});
210210
});

test/jest/integration/Swaps.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ describe('Swaps', () => {
149149
expect(dealAccepted).toEqual(false);
150150
});
151151

152-
test('it rejects upon 0 makerToTakerRoutes found', async () => {
152+
test('it rejects upon 0 maker to taker routes found', async () => {
153153
lndBtc.getRoutes = jest.fn().mockReturnValue([]);
154154
swapClientManager.get = jest.fn().mockImplementation((currency) => {
155155
if (currency === takerCurrency) {

test/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,6 @@ export const getValidDeal = () => {
115115
state: 0,
116116
role: 1,
117117
createTime: 1559120485138,
118-
makerToTakerRoutes: [{ getTotalTimeLock: () => {} }],
118+
takerMaxTimeLock: 100,
119119
};
120120
};

0 commit comments

Comments
 (0)