Skip to content

Commit

Permalink
feat: address filter
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Feb 3, 2024
1 parent 57ee9a5 commit 28855ee
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 365 deletions.
10 changes: 9 additions & 1 deletion lib/Boltz.ts
Expand Up @@ -23,6 +23,7 @@ import LndClient from './lightning/LndClient';
import ClnClient from './lightning/cln/ClnClient';
import MpayClient from './lightning/cln/MpayClient';
import NotificationProvider from './notifications/NotificationProvider';
import Blocks from './service/Blocks';
import CountryCodes from './service/CountryCodes';
import Service from './service/Service';
import NodeSwitch from './swap/NodeSwitch';
Expand All @@ -44,6 +45,7 @@ class Boltz {
private readonly notifications!: NotificationProvider;

private readonly api!: Api;
private readonly blocks: Blocks;
private readonly countryCodes: CountryCodes;
private readonly grpcServer!: GrpcServer;
private readonly prometheus: Prometheus;
Expand Down Expand Up @@ -115,13 +117,16 @@ class Boltz {
this.ethereumManagers,
);

this.blocks = new Blocks(this.logger, this.config.blocks);

try {
this.service = new Service(
this.logger,
this.config,
this.walletManager,
new NodeSwitch(this.logger, this.config.nodeSwitch),
this.currencies,
this.blocks,
);

this.backup = new BackupScheduler(
Expand Down Expand Up @@ -214,7 +219,10 @@ class Boltz {

await this.grpcServer.listen();

await this.countryCodes.downloadRanges();
await Promise.all([
this.countryCodes.downloadRanges(),
this.blocks.updateBlocks(),
]);
await this.api.init();

// Rescan chains after everything else was initialized to avoid race conditions
Expand Down
6 changes: 5 additions & 1 deletion lib/Config.ts
Expand Up @@ -10,7 +10,8 @@ import { Network } from './consts/Enums';
import Errors from './consts/Errors';
import { PairConfig } from './consts/Types';
import { LndConfig } from './lightning/LndClient';
import { ClnConfig } from './lightning/cln/ClnClient';
import { ClnConfig } from './lightning/cln/Types';
import { BlocksConfig } from './service/Blocks';
import { MarkingsConfig } from './service/CountryCodes';
import { NodeSwitchConfig } from './swap/NodeSwitch';

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

marking: MarkingsConfig;
blocks: BlocksConfig;

api: ApiConfig;
grpc: GrpcConfig;
Expand Down Expand Up @@ -228,6 +230,8 @@ class Config {
'https://cdn.jsdelivr.net/npm/@ip-location-db/asn-country/asn-country-ipv6-num.csv',
},

blocks: {},

api: {
host: '127.0.0.1',
port: 9001,
Expand Down
5 changes: 4 additions & 1 deletion lib/cli/Command.ts
Expand Up @@ -8,6 +8,7 @@ import {
detectSwap,
} from 'boltz-core';
import { Networks as LiquidNetworks } from 'boltz-core/dist/lib/liquid';
import * as console from 'console';
import { randomBytes } from 'crypto';
import { ECPairInterface } from 'ecpair';
import { Transaction as LiquidTransaction } from 'liquidjs-lib';
Expand Down Expand Up @@ -87,7 +88,9 @@ export const prepareTx = async (

// If the redeem script can be parsed as JSON, it is a swap tree
try {
const tree = SwapTreeSerializer.deserializeSwapTree(argv.redeemScript);
const tree = SwapTreeSerializer.deserializeSwapTree(
argv.redeemScript.replaceAll('\\"', '"'),
);

const { musig, swapOutput } = musigFromExtractedKey(
type,
Expand Down
40 changes: 40 additions & 0 deletions lib/service/Blocks.ts
@@ -0,0 +1,40 @@
import axios from 'axios';
import Logger from '../Logger';

type BlocksConfig = {
urls?: string[];
};

class Blocks {
private blocked = new Set<string>();

constructor(
private readonly logger: Logger,
private readonly config: BlocksConfig,
) {}

public updateBlocks = async () => {
if (this.config.urls === undefined || this.config.urls.length === 0) {
return;
}

const addresses = await Promise.all(
this.config.urls.map(async (url) => {
const response = await axios.get<string[]>(url);
this.logger.verbose(
`Fetched ${response.data.length} blocked addresses from: ${url}`,
);
return response.data;
}),
);

for (const addr of addresses.flat()) {
this.blocked.add(addr);
}
};

public isBlocked = (addr: string) => this.blocked.has(addr);
}

export default Blocks;
export { BlocksConfig };
3 changes: 3 additions & 0 deletions lib/service/Service.ts
Expand Up @@ -64,6 +64,7 @@ import SwapManager, { ChannelCreationInfo } from '../swap/SwapManager';
import SwapOutputType from '../swap/SwapOutputType';
import WalletManager, { Currency } from '../wallet/WalletManager';
import EthereumManager from '../wallet/ethereum/EthereumManager';
import Blocks from './Blocks';
import ElementsService from './ElementsService';
import Errors from './Errors';
import EventHandler from './EventHandler';
Expand Down Expand Up @@ -119,6 +120,7 @@ class Service {
private walletManager: WalletManager,
private nodeSwitch: NodeSwitch,
public currencies: Map<string, Currency>,
blocks: Blocks,
) {
this.prepayMinerFee = config.prepayminerfee;
this.logger.debug(
Expand Down Expand Up @@ -163,6 +165,7 @@ class Service {
: OutputType.Compatibility,
),
config.retryInterval,
blocks,
);

this.eventHandler = new EventHandler(
Expand Down
4 changes: 4 additions & 0 deletions lib/swap/Errors.ts
Expand Up @@ -98,4 +98,8 @@ export default {
message: 'invalid address',
code: concatErrorCode(ErrorCodePrefix.Swap, 19),
}),
BLOCKED_ADDRESS: (): Error => ({
message: 'blocked address',
code: concatErrorCode(ErrorCodePrefix.Swap, 20),
}),
};
30 changes: 21 additions & 9 deletions lib/swap/EthereumNursery.ts
@@ -1,4 +1,4 @@
import { TransactionResponse } from 'ethers';
import { Transaction, TransactionResponse } from 'ethers';
import { EventEmitter } from 'events';
import { Op } from 'sequelize';
import Logger from '../Logger';
Expand All @@ -15,6 +15,7 @@ import ReverseSwap from '../db/models/ReverseSwap';
import Swap from '../db/models/Swap';
import ReverseSwapRepository from '../db/repositories/ReverseSwapRepository';
import SwapRepository from '../db/repositories/SwapRepository';
import Blocks from '../service/Blocks';
import Wallet from '../wallet/Wallet';
import WalletManager from '../wallet/WalletManager';
import EthereumManager from '../wallet/ethereum/EthereumManager';
Expand Down Expand Up @@ -109,6 +110,7 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
private readonly logger: Logger,
private readonly walletManager: WalletManager,
public readonly ethereumManager: EthereumManager,
private readonly blocks: Blocks,
) {
super();

Expand Down Expand Up @@ -184,7 +186,7 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
private listenEtherSwap = () => {
this.ethereumManager.contractEventHandler.on(
'eth.lockup',
async (transactionHash, etherSwapValues) => {
async (transaction: Transaction, etherSwapValues) => {
let swap = await SwapRepository.getSwap({
preimageHash: getHexString(etherSwapValues.preimageHash),
status: {
Expand All @@ -209,12 +211,12 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
}

this.logger.debug(
`Found lockup in ${this.ethereumManager.networkDetails.name} EtherSwap contract for Swap ${swap.id}: ${transactionHash}`,
`Found lockup in ${this.ethereumManager.networkDetails.name} EtherSwap contract for Swap ${swap.id}: ${transaction.hash}`,
);

swap = await SwapRepository.setLockupTransaction(
swap,
transactionHash,
transaction.hash!,
Number(etherSwapValues.amount / etherDecimals),
true,
);
Expand Down Expand Up @@ -261,7 +263,12 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
}
}

this.emit('eth.lockup', swap, transactionHash, etherSwapValues);
if (this.blocks.isBlocked(transaction.from!)) {
this.emit('lockup.failed', swap, Errors.BLOCKED_ADDRESS().message);
return;
}

this.emit('eth.lockup', swap, transaction.hash, etherSwapValues);
},
);

Expand Down Expand Up @@ -291,7 +298,7 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
private listenERC20Swap = () => {
this.ethereumManager.contractEventHandler.on(
'erc20.lockup',
async (transactionHash, erc20SwapValues) => {
async (transaction: Transaction, erc20SwapValues) => {
let swap = await SwapRepository.getSwap({
preimageHash: getHexString(erc20SwapValues.preimageHash),
status: {
Expand Down Expand Up @@ -320,12 +327,12 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
const erc20Wallet = wallet.walletProvider as ERC20WalletProvider;

this.logger.debug(
`Found lockup in ${this.ethereumManager.networkDetails.name} ERC20Swap contract for Swap ${swap.id}: ${transactionHash}`,
`Found lockup in ${this.ethereumManager.networkDetails.name} ERC20Swap contract for Swap ${swap.id}: ${transaction.hash}`,
);

swap = await SwapRepository.setLockupTransaction(
swap,
transactionHash,
transaction.hash!,
erc20Wallet.normalizeTokenAmount(erc20SwapValues.amount),
true,
);
Expand Down Expand Up @@ -383,7 +390,12 @@ class EthereumNursery extends EventEmitter implements IEthereumNursery {
}
}

this.emit('erc20.lockup', swap, transactionHash, erc20SwapValues);
if (this.blocks.isBlocked(transaction.from!)) {
this.emit('lockup.failed', swap, Errors.BLOCKED_ADDRESS().message);
return;
}

this.emit('erc20.lockup', swap, transaction.hash, erc20SwapValues);
},
);

Expand Down
16 changes: 12 additions & 4 deletions lib/swap/SwapManager.ts
Expand Up @@ -45,6 +45,7 @@ import ChannelCreationRepository from '../db/repositories/ChannelCreationReposit
import ReverseSwapRepository from '../db/repositories/ReverseSwapRepository';
import SwapRepository from '../db/repositories/SwapRepository';
import RateProvider from '../rates/RateProvider';
import Blocks from '../service/Blocks';
import InvoiceExpiryHelper from '../service/InvoiceExpiryHelper';
import PaymentRequestUtils from '../service/PaymentRequestUtils';
import TimeoutDeltaProvider from '../service/TimeoutDeltaProvider';
Expand Down Expand Up @@ -133,6 +134,7 @@ class SwapManager {
private readonly paymentRequestUtils: PaymentRequestUtils,
private readonly swapOutputType: SwapOutputType,
retryInterval: number,
private readonly blocks: Blocks,
) {
this.nursery = new SwapNursery(
this.logger,
Expand All @@ -142,6 +144,7 @@ class SwapManager {
this.walletManager,
this.swapOutputType,
retryInterval,
this.blocks,
);
}

Expand Down Expand Up @@ -593,6 +596,14 @@ class SwapManager {
);
}

const isBitcoinLike =
sendingCurrency.type === CurrencyType.BitcoinLike ||
sendingCurrency.type === CurrencyType.Liquid;

if (!isBitcoinLike && this.blocks.isBlocked(args.claimAddress!)) {
throw Errors.BLOCKED_ADDRESS();
}

const pair = getPairId({
base: args.baseCurrency,
quote: args.quoteCurrency,
Expand Down Expand Up @@ -672,10 +683,7 @@ class SwapManager {
invoice: paymentRequest,
};

if (
sendingCurrency.type === CurrencyType.BitcoinLike ||
sendingCurrency.type === CurrencyType.Liquid
) {
if (isBitcoinLike) {
const { keys, index } = sendingCurrency.wallet.getNewKeys();
const { blocks } = await sendingCurrency.chainClient!.getBlockchainInfo();
result.timeoutBlockHeight = blocks + args.onchainTimeoutBlockDelta;
Expand Down
6 changes: 4 additions & 2 deletions lib/swap/SwapNursery.ts
Expand Up @@ -45,6 +45,7 @@ import {
} from '../lightning/LightningClient';
import FeeProvider from '../rates/FeeProvider';
import RateProvider from '../rates/RateProvider';
import Blocks from '../service/Blocks';
import TimeoutDeltaProvider from '../service/TimeoutDeltaProvider';
import Wallet from '../wallet/Wallet';
import WalletManager, { Currency } from '../wallet/WalletManager';
Expand Down Expand Up @@ -198,12 +199,13 @@ class SwapNursery extends EventEmitter implements ISwapNursery {
private walletManager: WalletManager,
private swapOutputType: SwapOutputType,
private retryInterval: number,
blocks: Blocks,
) {
super();

this.logger.info(`Setting Swap retry interval to ${retryInterval} seconds`);

this.utxoNursery = new UtxoNursery(this.logger, this.walletManager);
this.utxoNursery = new UtxoNursery(this.logger, this.walletManager, blocks);
this.lightningNursery = new LightningNursery(this.logger);
this.invoiceNursery = new InvoiceNursery(this.logger);
this.channelNursery = new ChannelNursery(
Expand All @@ -213,7 +215,7 @@ class SwapNursery extends EventEmitter implements ISwapNursery {

this.ethereumNurseries = this.walletManager.ethereumManagers.map(
(manager) =>
new EthereumNursery(this.logger, this.walletManager, manager),
new EthereumNursery(this.logger, this.walletManager, manager, blocks),
);

this.paymentHandler = new PaymentHandler(
Expand Down

0 comments on commit 28855ee

Please sign in to comment.