Skip to content

Commit

Permalink
feat: cooperative chain swap claims
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Mar 22, 2024
1 parent 2ba0a92 commit 9ba1065
Show file tree
Hide file tree
Showing 26 changed files with 1,163 additions and 378 deletions.
12 changes: 6 additions & 6 deletions lib/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ export const constructClaimDetails = (
swapType: SwapType,
swap: Swap | ChainSwapData,
transaction: Transaction | LiquidTransaction,
preimage: Buffer,
preimage?: Buffer,
cooperative: boolean = false,
): ClaimDetails | LiquidClaimDetails => {
const isSwap = swapType === SwapType.Submarine;
const isSubmarine = swapType === SwapType.Submarine;

let lockupVout = isSwap
let lockupVout = isSubmarine
? (swap as Swap).lockupTransactionVout
: (swap as ChainSwapData).transactionVout;

Expand All @@ -194,19 +194,19 @@ export const constructClaimDetails = (
keys: wallet.getKeysByIndex(swap.keyIndex!),
} as ClaimDetails | LiquidClaimDetails;

switch (isSwap ? (swap as Swap).version : SwapVersion.Taproot) {
switch (isSubmarine ? (swap as Swap).version : SwapVersion.Taproot) {
case SwapVersion.Taproot: {
claimDetails.type = OutputType.Taproot;
claimDetails.cooperative = cooperative;
claimDetails.swapTree = SwapTreeSerializer.deserializeSwapTree(
isSwap
isSubmarine
? (swap as Swap).redeemScript!
: (swap as ChainSwapData).swapTree!,
);
claimDetails.internalKey = createMusig(
claimDetails.keys!,
getHexBuffer(
isSwap
isSubmarine
? (swap as Swap).refundPublicKey!
: (swap as ChainSwapData).theirPublicKey!,
),
Expand Down
18 changes: 14 additions & 4 deletions lib/api/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import Logger from '../Logger';
import CountryCodes from '../service/CountryCodes';
import Service from '../service/Service';
import Controller from './Controller';
import SwapInfos from './SwapInfos';
import { errorResponse } from './Utils';
import ApiV2 from './v2/ApiV2';
import WebSocketHandler from './v2/WebSocketHandler';

class Api {
private app: Application;

private readonly swapInfos: SwapInfos;

private readonly websocket: WebSocketHandler;
private readonly controller: Controller;

Expand Down Expand Up @@ -47,20 +51,26 @@ class Api {
},
);

this.controller = new Controller(logger, service, countryCodes);
this.websocket = new WebSocketHandler(service, this.controller);
this.swapInfos = new SwapInfos(this.logger, service);
this.controller = new Controller(
logger,
service,
countryCodes,
this.swapInfos,
);
this.websocket = new WebSocketHandler(service, this.swapInfos);

new ApiV2(
this.logger,
service,
this.controller,
this.swapInfos,
countryCodes,
).registerRoutes(this.app);
this.registerRoutes(this.controller);
}

public init = async (): Promise<void> => {
await this.controller.init();
await this.swapInfos.init();

await new Promise<void>((resolve) => {
const server = this.app.listen(this.config.port, this.config.host, () => {
Expand Down
141 changes: 7 additions & 134 deletions lib/api/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
import { Request, Response } from 'express';
import path from 'path';
import Logger from '../Logger';
import {
getChainCurrency,
getVersion,
mapToObject,
saneStringify,
splitPairId,
stringify,
} from '../Utils';
import {
SwapType,
SwapUpdateEvent,
SwapVersion,
swapTypeToString,
} from '../consts/Enums';
import { getVersion, mapToObject, saneStringify, stringify } from '../Utils';
import { SwapType, SwapVersion, swapTypeToString } from '../consts/Enums';
import ReferralStats from '../data/ReferralStats';
import ChannelCreationRepository from '../db/repositories/ChannelCreationRepository';
import ReverseSwapRepository from '../db/repositories/ReverseSwapRepository';
import SwapRepository from '../db/repositories/SwapRepository';
import LndClient from '../lightning/LndClient';
import ClnClient from '../lightning/cln/ClnClient';
import CountryCodes from '../service/CountryCodes';
import ServiceErrors from '../service/Errors';
import { SwapUpdate } from '../service/EventHandler';
import NodeInfo from '../service/NodeInfo';
import Service from '../service/Service';
import SwapNursery from '../swap/SwapNursery';
import Bouncer from './Bouncer';
import SwapInfos from './SwapInfos';
import {
checkPreimageHashLength,
createdResponse,
Expand All @@ -38,21 +22,18 @@ import {
} from './Utils';

class Controller {
// TODO: refactor
// A map between the ids and statuses of the swaps
public pendingSwapInfos = new Map<string, SwapUpdate>();

// A map between the ids and HTTP streams of all pending swaps
private pendingSwapStreams = new Map<string, Response>();

constructor(
private readonly logger: Logger,
private readonly service: Service,
private readonly countryCodes: CountryCodes,
private readonly swapInfos: SwapInfos,
) {
this.service.eventHandler.on('swap.update', ({ id, status }) => {
this.logger.debug(`Swap ${id} update: ${saneStringify(status)}`);
this.pendingSwapInfos.set(id, status);
this.swapInfos.set(id, status);

const response = this.pendingSwapStreams.get(id);

Expand All @@ -62,114 +43,6 @@ class Controller {
});
}

public init = async (): Promise<void> => {
this.logger.verbose('Fetching swaps status from database');

// Get the latest status of all swaps in the database
const [swaps, reverseSwaps] = await Promise.all([
SwapRepository.getSwaps(),
ReverseSwapRepository.getReverseSwaps(),
]);

for (const swap of swaps) {
const status = swap.status;

switch (status) {
case SwapUpdateEvent.ChannelCreated: {
const channelCreation =
await ChannelCreationRepository.getChannelCreation({
swapId: swap.id,
});

this.pendingSwapInfos.set(swap.id, {
status,
channel: {
fundingTransactionId: channelCreation!.fundingTransactionId!,
fundingTransactionVout: channelCreation!.fundingTransactionVout!,
},
});

break;
}

case SwapUpdateEvent.TransactionZeroConfRejected:
this.pendingSwapInfos.set(swap.id, {
status: SwapUpdateEvent.TransactionMempool,
zeroConfRejected: true,
});
break;

default:
this.pendingSwapInfos.set(swap.id, {
status: swap.status as SwapUpdateEvent,
failureReason:
swap.failureReason !== null ? swap.failureReason : undefined,
});
break;
}
}

for (const reverseSwap of reverseSwaps) {
const status = reverseSwap.status;

switch (status) {
case SwapUpdateEvent.TransactionMempool:
case SwapUpdateEvent.TransactionConfirmed: {
const { base, quote } = splitPairId(reverseSwap.pair);
const chainCurrency = getChainCurrency(
base,
quote,
reverseSwap.orderSide,
true,
);

try {
const transactionHex = await this.service.getTransaction(
chainCurrency,
reverseSwap.transactionId!,
);

this.pendingSwapInfos.set(reverseSwap.id, {
status,
transaction: {
hex: transactionHex,
id: reverseSwap.transactionId!,
eta:
status === SwapUpdateEvent.TransactionMempool
? SwapNursery.reverseSwapMempoolEta
: undefined,
},
});
} catch (error) {
// If the transaction can't be queried with the service it's either a transaction on the Ethereum network,
// or something is terribly wrong
if (
(error as any).message !==
ServiceErrors.NOT_SUPPORTED_BY_SYMBOL(chainCurrency).message
) {
throw error;
}

this.pendingSwapInfos.set(reverseSwap.id, {
status,
transaction: {
id: reverseSwap.transactionId!,
},
});
}

break;
}

default:
this.pendingSwapInfos.set(reverseSwap.id, {
status: status as SwapUpdateEvent,
});
break;
}
}
};

// Static files
public serveFile = (fileName: string) => {
return (_: Request, res: Response): void => {
Expand Down Expand Up @@ -287,7 +160,7 @@ class Controller {
{ name: 'id', type: 'string' },
]);

const response = this.pendingSwapInfos.get(id);
const response = this.swapInfos.get(id);

if (response) {
successResponse(res, response);
Expand Down Expand Up @@ -573,7 +446,7 @@ class Controller {

res.setTimeout(0);

const lastUpdate = this.pendingSwapInfos.get(id);
const lastUpdate = this.swapInfos.get(id);
if (lastUpdate) {
this.writeToSse(res, lastUpdate);
}
Expand Down
Loading

0 comments on commit 9ba1065

Please sign in to comment.