Skip to content
This repository has been archived by the owner on Aug 28, 2019. It is now read-only.

Commit

Permalink
feat: save swap information in database (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 authored Mar 12, 2019
1 parent fdfeeaf commit 0102760
Show file tree
Hide file tree
Showing 14 changed files with 293 additions and 106 deletions.
2 changes: 1 addition & 1 deletion lib/BoltzMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class BoltzMiddleware {
await this.service.init(this.config.pairs);
await this.notifications.init();

this.api.init();
await this.api.init();
}
}

Expand Down
9 changes: 6 additions & 3 deletions lib/api/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ type ApiConfig = {

class Api {
private app: Application;
private controller: Controller;

constructor(private logger: Logger, private config: ApiConfig, service: Service) {
this.app = express();

this.app.use(cors());
this.app.use(express.json());

const controller = new Controller(logger, service);
this.registerRoutes(controller);
this.controller = new Controller(logger, service);
this.registerRoutes(this.controller);
}

public init = () => {
public init = async () => {
await this.controller.init();

this.app.listen(this.config.port, this.config.host, () => {
this.logger.info(`API server listening on: ${this.config.host}:${this.config.port}`);
});
Expand Down
35 changes: 31 additions & 4 deletions lib/api/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { Request, Response } from 'express';
import Logger from '../Logger';
import { stringify } from '../Utils';
import Service from '../service/Service';
import { SwapUpdateEvent } from '../consts/Enums';

class Controller {
// A map between the ids and HTTP responses of all pending swaps
private pendingSwaps = new Map<string, Response>();

// A map between the ids and statuses of the swaps
private pendingSwapInfos = new Map<string, Object>();
private pendingSwapInfos = new Map<string, object>();

constructor(private logger: Logger, private service: Service) {
this.service.on('swap.update', (id: string, message: object) => {
this.service.on('swap.update', (id, message) => {
this.pendingSwapInfos.set(id, message);

const response = this.pendingSwaps.get(id);
Expand All @@ -23,6 +24,32 @@ class Controller {
});
}

public init = async () => {
// Get the latest status of all swaps in the database
const [swaps, reverseSwaps] = await Promise.all([
this.service.swapRepository.getSwaps(),
this.service.reverseSwapRepository.getReverseSwaps(),
]);

swaps.forEach((swap) => {
if (swap.status) {
this.pendingSwapInfos.set(swap.id, { event: swap.status });
}
});

reverseSwaps.forEach((reverseSwap) => {
if (reverseSwap.status) {
const event = reverseSwap.status;

if (event !== SwapUpdateEvent.InvoiceSettled) {
this.pendingSwapInfos.set(reverseSwap.id, { event });
} else {
this.pendingSwapInfos.set(reverseSwap.id, { event, preimage: reverseSwap.preimage! });
}
}
});
}

// GET requests
public getPairs = (_req: Request, res: Response) => {
this.successResponse(res, this.service.getPairs());
Expand Down Expand Up @@ -131,9 +158,9 @@ class Controller {
]);

res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
});

Expand Down
10 changes: 5 additions & 5 deletions lib/boltz/BoltzClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class BoltzClient extends BaseClient {

this.transactionSubscription = this.boltz.subscribeTransactions(new boltzrpc.SubscribeTransactionsRequest(), this.meta)
.on('data', (response: boltzrpc.SubscribeTransactionsResponse) => {
this.logger.silly(`Found transaction to address ${response.getOutputAddress()} confirmed: ${response.getTransactionHash()}`);
this.logger.debug(`Found transaction to address ${response.getOutputAddress()} confirmed: ${response.getTransactionHash()}`);
this.emit('transaction.confirmed', response.getTransactionHash(), response.getOutputAddress());
})
.on('error', async (error) => {
Expand All @@ -253,19 +253,19 @@ class BoltzClient extends BaseClient {

switch (response.getEvent()) {
case boltzrpc.InvoiceEvent.PAID:
this.logger.silly(`Invoice paid: ${invoice}`);
this.logger.debug(`Invoice paid: ${invoice}`);
this.emit('invoice.paid', invoice);

break;

case boltzrpc.InvoiceEvent.FAILED_TO_PAY:
this.logger.silly(`Failed to pay invoice: ${invoice}`);
this.logger.debug(`Failed to pay invoice: ${invoice}`);
this.emit('invoice.failedToPay', invoice);

break;

case boltzrpc.InvoiceEvent.SETTLED:
this.logger.silly(`Invoice settled: ${invoice}`);
this.logger.debug(`Invoice settled: ${invoice}`);
this.emit('invoice.settled', invoice, response.getPreimage());

break;
Expand All @@ -290,7 +290,7 @@ class BoltzClient extends BaseClient {
this.refundsSubscription = this.boltz.subscribeRefunds(new boltzrpc.SubscribeRefundsRequest(), this.meta)
.on('data', (response: boltzrpc.SubscribeRefundsResponse) => {
const lockupTransactionHash = response.getLockupTransactionHash();
this.logger.silly(`Refunded lockup transaction: ${lockupTransactionHash}`);
this.logger.debug(`Refunded lockup transaction: ${lockupTransactionHash}`);
this.emit('refund', lockupTransactionHash);
})
.on('error', async (error) => {
Expand Down
24 changes: 24 additions & 0 deletions lib/consts/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,27 @@ export type PairAttributes = PairFactory & {
};

export type PairInstance = PairAttributes & Sequelize.Instance<PairAttributes>;

type Swap = {
id: string;
pair: string;
status?: string;
invoice: string;
};

export type SwapFactory = Swap & {
lockupAddress: string;
};

export type SwapAttributes = SwapFactory;

export type SwapInstance = SwapFactory & Sequelize.Instance<SwapAttributes>;

export type ReverseSwapFactory = Swap & {
preimage?: string;
transactionId: string;
};

export type ReverseSwapAttributes = ReverseSwapFactory;

export type ReverseSwapInstance = ReverseSwapFactory & Sequelize.Instance<ReverseSwapAttributes>;
8 changes: 8 additions & 0 deletions lib/consts/Types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { SwapUpdateEvent } from './Enums';

export type Error = {
message: string;
code: string;
};

export type SwapUpdate = {
event: SwapUpdateEvent,

preimage?: string;
};
9 changes: 7 additions & 2 deletions lib/db/Database.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import fs from 'fs';
import path from 'path';
import Sequelize from 'sequelize';
import * as db from '../consts/Database';
import Logger from '../Logger';
import * as db from '../consts/Database';

type Models = {
Pair: Sequelize.Model<db.PairInstance, db.PairAttributes>;
Swap: Sequelize.Model<db.SwapInstance, db.SwapAttributes>;
ReverseSwap: Sequelize.Model<db.ReverseSwapInstance, db.ReverseSwapAttributes>;
};

class Database {
Expand Down Expand Up @@ -35,8 +37,11 @@ class Database {
throw error;
}

await this.models.Pair.sync(),

await Promise.all([
this.models.Pair.sync(),
this.models.Swap.sync(),
this.models.ReverseSwap.sync(),
]);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/db/models/Pair.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Sequelize from 'sequelize';
import * as db from '../../consts/Database';
import { getPairId } from '../../Utils';
import * as db from '../../consts/Database';

export default (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.PairAttributes> = {
Expand Down
27 changes: 27 additions & 0 deletions lib/db/models/ReverseSwap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Sequelize from 'sequelize';
import * as db from '../../consts/Database';

export default (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.ReverseSwapAttributes> = {
id: { type: dataTypes.STRING, primaryKey: true, allowNull: false },
pair: { type: dataTypes.STRING, allowNull: false },
status: { type: dataTypes.STRING, allowNull: true },
invoice: { type: dataTypes.STRING, allowNull: false },
preimage: { type: dataTypes.STRING, allowNull: true },
transactionId: { type: dataTypes.STRING, allowNull: false },
};

const options: Sequelize.DefineOptions<db.ReverseSwapInstance> = {
tableName: 'reverseSwaps',
};

const ReverseSwap = sequelize.define<db.ReverseSwapInstance, db.ReverseSwapAttributes>('ReverseSwap', attributes, options);

ReverseSwap.associate = (models: Sequelize.Models) => {
models.ReverseSwap.belongsTo(models.Pair, {
foreignKey: 'pair',
});
};

return ReverseSwap;
};
32 changes: 32 additions & 0 deletions lib/db/models/Swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Sequelize from 'sequelize';
import * as db from '../../consts/Database';

export default (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.SwapAttributes> = {
id: { type: dataTypes.STRING, primaryKey: true, allowNull: false },
pair: { type: dataTypes.STRING, allowNull: false },
status: { type: dataTypes.STRING, allowNull: true },
invoice: { type: dataTypes.STRING, unique: true, allowNull: false },
lockupAddress: { type: dataTypes.STRING, allowNull: false },
};

const options: Sequelize.DefineOptions<db.SwapInstance> = {
tableName: 'swaps',
indexes: [
{
unique: true,
fields: ['id', 'invoice'],
},
],
};

const Swap = sequelize.define<db.SwapInstance, db.SwapAttributes>('Swap', attributes, options);

Swap.associate = (models: Sequelize.Models) => {
models.Swap.belongsTo(models.Pair, {
foreignKey: 'pair',
});
};

return Swap;
};
4 changes: 4 additions & 0 deletions lib/service/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ export default {
message: `${amount} is less than minimal ${minimalAmount}`,
code: concatErrorCode(ErrorCodePrefix.Service, 4),
}),
SWAP_WITH_INVOICE_EXISTS_ALREADY: (invoice: string): Error => ({
message: `a swap with the invoice ${invoice} exists already`,
code: concatErrorCode(ErrorCodePrefix.Service, 5),
}),
};
33 changes: 33 additions & 0 deletions lib/service/ReverseSwapRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { WhereOptions } from 'sequelize';
import { Models } from '../db/Database';
import * as db from '../consts/Database';

class ReverseSwapRepository {
constructor(private models: Models) {}

public getReverseSwaps = async () => {
return this.models.ReverseSwap.findAll({});
}

public getReverseSwap = async (options: WhereOptions<db.ReverseSwapFactory>) => {
return this.models.ReverseSwap.findOne({
where: options,
});
}

public addReverseSwap = async (reverseSwap: db.ReverseSwapFactory) => {
return this.models.ReverseSwap.create(reverseSwap);
}

public setReverseSwapStatus = async (reverseSwap: db.ReverseSwapInstance, status: string) => {
return reverseSwap.update({
status,
});
}

public updateReverseSwap = async (reverseSwap: db.ReverseSwapInstance, keys: object) => {
return reverseSwap.update(keys);
}
}

export default ReverseSwapRepository;
Loading

0 comments on commit 0102760

Please sign in to comment.