Skip to content

Commit

Permalink
feat: batched normal swap claims
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Feb 4, 2024
1 parent 4ad8a4f commit 9cc7009
Show file tree
Hide file tree
Showing 30 changed files with 1,917 additions and 119 deletions.
9 changes: 9 additions & 0 deletions lib/Config.ts
Expand Up @@ -13,6 +13,7 @@ import { LndConfig } from './lightning/LndClient';
import { ClnConfig } from './lightning/cln/Types';
import { BlocksConfig } from './service/Blocks';
import { MarkingsConfig } from './service/CountryCodes';
import { SwapConfig } from './service/cooperative/DeferredClaimer';
import { NodeSwitchConfig } from './swap/NodeSwitch';

type PostgresConfig = {
Expand Down Expand Up @@ -164,6 +165,8 @@ type ConfigType = {
prepayminerfee: boolean;
swapwitnessaddress: boolean;

swap: SwapConfig;

marking: MarkingsConfig;
blocks: BlocksConfig;

Expand Down Expand Up @@ -223,6 +226,12 @@ class Config {
prepayminerfee: false,
swapwitnessaddress: false,

swap: {
deferredClaimSymbols: ['L-BTC'],
batchClaimInterval: '*/15 * * * *',
expiryTolerance: 120,
},

marking: {
ipV4Ranges:
'https://cdn.jsdelivr.net/npm/@ip-location-db/asn-country/asn-country-ipv4-num.csv',
Expand Down
55 changes: 54 additions & 1 deletion lib/Core.ts
Expand Up @@ -10,11 +10,14 @@ import {
import {
ClaimDetails,
Musig,
OutputType,
RefundDetails,
SwapTreeSerializer,
TaprootUtils,
Types,
constructClaimTransaction as constructClaimTransactionBitcoin,
constructRefundTransaction as constructRefundTransactionBitcoin,
detectSwap,
init,
targetFee,
} from 'boltz-core';
Expand Down Expand Up @@ -45,8 +48,10 @@ import {
reverseBuffer,
} from './Utils';
import ChainClient from './chain/ChainClient';
import { CurrencyType } from './consts/Enums';
import { CurrencyType, SwapVersion } from './consts/Enums';
import { liquidSymbol } from './consts/LiquidTypes';
import Swap from './db/models/Swap';
import SwapOutputType from './swap/SwapOutputType';
import Wallet from './wallet/Wallet';
import WalletLiquid from './wallet/WalletLiquid';
import { Currency } from './wallet/WalletManager';
Expand Down Expand Up @@ -156,6 +161,54 @@ export const getOutputValue = (
return unblinded.isLbtc ? unblinded.value : 0;
};

export const constructClaimDetails = (
swapOutputType: SwapOutputType,
wallet: Wallet,
swap: Swap,
transaction: Transaction | LiquidTransaction,
preimage: Buffer,
): ClaimDetails | LiquidClaimDetails => {
// Compatibility mode with database schema version 0 in which this column didn't exist
if (swap.lockupTransactionVout === undefined) {
swap.lockupTransactionVout = detectSwap(
getHexBuffer(swap.redeemScript!),
transaction,
)!.vout;
}

const output = transaction.outs[swap.lockupTransactionVout!];
const claimDetails = {
...output,
preimage,
txHash: transaction.getHash(),
vout: swap.lockupTransactionVout!,
keys: wallet.getKeysByIndex(swap.keyIndex!),
} as ClaimDetails | LiquidClaimDetails;

switch (swap.version) {
case SwapVersion.Taproot: {
claimDetails.type = OutputType.Taproot;
claimDetails.cooperative = false;
claimDetails.swapTree = SwapTreeSerializer.deserializeSwapTree(
swap.redeemScript!,
);
claimDetails.internalKey = createMusig(
claimDetails.keys,
getHexBuffer(swap.refundPublicKey!),
).getAggregatedPublicKey();
break;
}

default: {
claimDetails.type = swapOutputType.get(wallet.type);
claimDetails.redeemScript = getHexBuffer(swap.redeemScript!);
break;
}
}

return claimDetails;
};

export const constructClaimTransaction = (
wallet: Wallet,
claimDetails: ClaimDetails[] | LiquidClaimDetails[],
Expand Down
4 changes: 4 additions & 0 deletions lib/chain/ChainClient.ts
Expand Up @@ -233,6 +233,10 @@ class ChainClient extends BaseClient {
}
};

public getRawMempool = async () => {
return this.client.request<string[]>('getrawmempool');
};

public estimateFee = async (confTarget = 2): Promise<number> => {
return this.estimateFeeWithFloor(confTarget);
};
Expand Down
35 changes: 35 additions & 0 deletions lib/cli/commands/SweepSwaps.ts
@@ -0,0 +1,35 @@
import { Arguments } from 'yargs';
import { SweepSwapsRequest } from '../../proto/boltzrpc_pb';
import BuilderComponents from '../BuilderComponents';
import { callback, loadBoltzClient } from '../Command';

export const command = 'sweepswaps [symbol]';

export const describe = 'sweeps all deferred swap claims';

export const builder = {
symbol: BuilderComponents.symbol,
};

export const handler = (argv: Arguments<any>): void => {
const request = new SweepSwapsRequest();

if (argv.symbol !== undefined && argv.symbol !== '') {
request.setSymbol(argv.symbol);
}

loadBoltzClient(argv).sweepSwaps(
request,
callback((res) => {
const sweep: Record<string, string[]> = {};

for (const [, [symbol, swapIds]] of res
.toObject()
.claimedSymbolsMap.entries()) {
sweep[symbol] = swapIds.claimedIdsList;
}

return sweep;
}),
);
};
1 change: 1 addition & 0 deletions lib/consts/Enums.ts
Expand Up @@ -40,6 +40,7 @@ export enum SwapUpdateEvent {
TransactionFailed = 'transaction.failed',

TransactionMempool = 'transaction.mempool',
TransactionClaimPending = 'transaction.claim.pending',
TransactionClaimed = 'transaction.claimed',
TransactionRefunded = 'transaction.refunded',
TransactionConfirmed = 'transaction.confirmed',
Expand Down
8 changes: 8 additions & 0 deletions lib/db/repositories/SwapRepository.ts
Expand Up @@ -26,6 +26,14 @@ class SwapRepository {
});
};

public static getSwapsClaimable = () => {
return Swap.findAll({
where: {
status: SwapUpdateEvent.TransactionClaimPending,
},
});
};

public static getSwap = (options: WhereOptions): Promise<Swap | null> => {
return Swap.findOne({
where: options,
Expand Down
1 change: 1 addition & 0 deletions lib/grpc/GrpcServer.ts
Expand Up @@ -26,6 +26,7 @@ class GrpcServer {
sendCoins: grpcService.sendCoins,
updateTimeoutBlockDelta: grpcService.updateTimeoutBlockDelta,
addReferral: grpcService.addReferral,
sweepSwaps: grpcService.sweepSwaps,
});
}

Expand Down
31 changes: 31 additions & 0 deletions lib/grpc/GrpcService.ts
Expand Up @@ -172,6 +172,37 @@ class GrpcService {
});
};

public sweepSwaps: handleUnaryCall<
boltzrpc.SweepSwapsRequest,
boltzrpc.SweepSwapsResponse
> = async (call, callback) => {
await this.handleCallback(call, callback, async () => {
const { symbol } = call.request.toObject();

const claimed = symbol
? new Map<string, string[]>([
[
symbol,
await this.service.swapManager.deferredClaimer.sweepSymbol(
symbol,
),
],
])
: await this.service.swapManager.deferredClaimer.sweep();

const response = new boltzrpc.SweepSwapsResponse();
const grpcMap = response.getClaimedSymbolsMap();

for (const [symbol, swapIds] of claimed) {
const ids = new boltzrpc.SweepSwapsResponse.ClaimedSwaps();
ids.setClaimedIdsList(swapIds);
grpcMap.set(symbol, ids);
}

return response;
});
};

private handleCallback = async <R, T>(
call: R,
callback: (error: any, res: T | null) => void,
Expand Down
15 changes: 15 additions & 0 deletions lib/notifications/CommandHandler.ts
Expand Up @@ -6,6 +6,7 @@ import {
formatError,
getChainCurrency,
getHexString,
mapToObject,
splitPairId,
stringify,
} from '../Utils';
Expand Down Expand Up @@ -40,6 +41,7 @@ enum Command {
LockedFunds = 'lockedfunds',
PendingSwaps = 'pendingswaps',
GetReferrals = 'getreferrals',
PendingSweeps = 'pendingsweeps',

// Commands that generate a value or trigger a function
Backup = 'backup',
Expand Down Expand Up @@ -142,6 +144,13 @@ class CommandHandler {
description: 'gets a list of pending (reverse) swaps',
},
],
[
Command.PendingSweeps,
{
executor: this.pendingSweeps,
description: 'gets all pending sweeps',
},
],
[
Command.GetReferrals,
{
Expand Down Expand Up @@ -465,6 +474,12 @@ class CommandHandler {
await this.notificationClient.sendMessage(message);
};

private pendingSweeps = async () => {
await this.notificationClient.sendMessage(
`${codeBlock}${stringify(mapToObject(this.service.swapManager.deferredClaimer.pendingSweeps()))}${codeBlock}`,
);
};

private getReferrals = async () => {
await this.notificationClient.sendMessage(
`${codeBlock}${stringify(await ReferralStats.getReferralFees())}${codeBlock}`,
Expand Down
17 changes: 17 additions & 0 deletions lib/proto/boltzrpc_grpc_pb.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions lib/proto/boltzrpc_grpc_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9cc7009

Please sign in to comment.