xud
Version:
Exchange Union Daemon
260 lines • 11.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const fs_1 = require("fs");
const sequelize_1 = require("sequelize");
const enums_1 = require("../constants/enums");
const seeds_1 = require("../db/seeds");
const utils_1 = require("../utils/utils");
const migrations_1 = __importDefault(require("./migrations"));
const Models = __importStar(require("./models"));
function loadModels(sequelize) {
const models = {
Currency: Models.Currency(sequelize),
Node: Models.Node(sequelize),
Order: Models.Order(sequelize),
Pair: Models.Pair(sequelize),
ReputationEvent: Models.ReputationEvent(sequelize),
SwapDeal: Models.SwapDeal(sequelize),
Trade: Models.Trade(sequelize),
Password: Models.Password(sequelize),
};
models.Currency.hasMany(models.Pair, {
as: 'quoteCurrencies',
foreignKey: 'quoteCurrency',
constraints: true,
});
models.Currency.hasMany(models.Pair, {
as: 'baseCurrencies',
foreignKey: 'baseCurrency',
constraints: true,
});
models.Node.hasMany(models.ReputationEvent, {
foreignKey: 'nodeId',
constraints: true,
});
models.Order.belongsTo(models.Node, {
foreignKey: 'nodeId',
constraints: true,
});
models.Order.belongsTo(models.Pair, {
foreignKey: 'pairId',
constraints: false,
});
models.Order.hasMany(models.Trade, {
as: 'makerTrades',
foreignKey: 'makerOrderId',
constraints: true,
});
models.Order.hasMany(models.Trade, {
as: 'takerTrades',
foreignKey: 'takerOrderId',
constraints: true,
});
models.Order.hasMany(models.SwapDeal, {
foreignKey: 'orderId',
constraints: true,
});
models.Pair.belongsTo(models.Currency, {
as: 'baseCurrencyInstance',
constraints: true,
foreignKey: 'baseCurrency',
});
models.Pair.belongsTo(models.Currency, {
as: 'takerCurrencyInstance',
constraints: true,
foreignKey: 'quoteCurrency',
});
models.Pair.beforeBulkCreate(pairs => pairs.forEach(pair => pair.id = utils_1.derivePairId(pair)));
models.Pair.beforeCreate((pair) => { pair.id = utils_1.derivePairId(pair); });
models.ReputationEvent.belongsTo(models.Node, {
foreignKey: 'nodeId',
constraints: true,
});
models.SwapDeal.belongsTo(models.Order, {
foreignKey: 'orderId',
constraints: true,
});
models.SwapDeal.belongsTo(models.Node, {
foreignKey: 'nodeId',
constraints: true,
});
models.Trade.belongsTo(models.Order, {
as: 'makerOrder',
foreignKey: 'makerOrderId',
constraints: true,
});
models.Trade.belongsTo(models.Order, {
as: 'takerOrder',
foreignKey: 'takerOrderId',
constraints: false,
});
models.Trade.belongsTo(models.SwapDeal, {
foreignKey: 'rHash',
constraints: false,
});
return models;
}
/** A class representing a connection to a SQL database. */
let DB = /** @class */ (() => {
class DB {
/**
* @param storage the file path for the sqlite database file, if ':memory:' or not specified the db is stored in memory
*/
constructor(logger, storage) {
this.logger = logger;
this.storage = storage;
/**
* Initialize the connection to the database.
* @param initDb whether to intialize a new database with default values if no database exists
*/
this.init = (network = enums_1.XuNetwork.SimNet, initDb = false) => __awaiter(this, void 0, void 0, function* () {
const isNewDb = yield this.isNewDb();
try {
yield this.sequelize.authenticate();
this.logger.info(`connected to database ${this.storage ? this.storage : 'in memory'}`);
}
catch (err) {
this.logger.error('unable to connect to the database', err);
throw err;
}
if (isNewDb) {
yield this.sequelize.query(`PRAGMA user_version=${DB.VERSION};`);
}
// version is useful for tracking migrations & upgrades to the xud database when
// the database schema is modified or restructured
let version;
const userVersionPragma = (yield this.sequelize.query('PRAGMA user_version;'));
assert_1.default(Array.isArray(userVersionPragma) && Array.isArray(userVersionPragma[0]));
const userVersion = userVersionPragma[0][0].user_version;
assert_1.default(typeof userVersion === 'number');
version = userVersion;
this.logger.trace(`db version is ${version}`);
if (version <= DB.VERSION) {
// if our db is not the latest version, we call each migration procedure necessary
// to bring us from our current version up to the latest version.
for (let n = version; n < DB.VERSION; n += 1) {
this.logger.info(`migrating db from version ${n} to version ${n + 1}`);
yield migrations_1.default[n](this.sequelize);
yield this.sequelize.query(`PRAGMA user_version=${n + 1};`);
this.logger.info(`migration to version ${n + 1} complete`);
}
}
const { Node, Currency, Pair, ReputationEvent, SwapDeal, Order, Trade, Password } = this.models;
// sync schemas with the database in phases, according to FKs dependencies
yield Promise.all([
Node.sync(),
Currency.sync(),
Password.sync(),
]);
// Pair is dependent on Currency, ReputationEvent is dependent on Node
yield Promise.all([
Pair.sync(),
ReputationEvent.sync(),
]);
// Order is dependent on Pair
yield Promise.all([
Order.sync(),
]);
yield Promise.all([
Trade.sync(),
SwapDeal.sync(),
]);
if (initDb) {
// initialize database with the seed nodes for the configured network
const nodes = seeds_1.defaultNodes(network);
if (nodes) {
const existingNodes = yield Models.Node(this.sequelize).findAll();
const newNodes = nodes.filter(node => (!existingNodes.find(n => (n.nodePubKey === node.nodePubKey))));
if (newNodes.length > 0) {
yield Node.bulkCreate(newNodes);
}
}
// initialize database with the default currencies for the configured network
const currencies = seeds_1.defaultCurrencies(network);
if (currencies) {
const existingCurrencies = yield Models.Currency(this.sequelize).findAll();
const newCurrencies = currencies.filter(currency => (!existingCurrencies.find(n => (n.id === currency.id))));
if (newCurrencies.length > 0) {
yield Currency.bulkCreate(newCurrencies);
}
}
// initialize database with the default trading pairs for the configured network
const pairs = seeds_1.defaultPairs(network);
if (pairs) {
const existingPairs = yield Models.Pair(this.sequelize).findAll();
const newPairs = pairs.filter(pair => (!existingPairs.find(n => (n.baseCurrency === pair.baseCurrency &&
n.quoteCurrency === pair.quoteCurrency))));
if (newPairs.length > 0) {
yield Pair.bulkCreate(newPairs);
}
}
}
});
/**
* Checks whether the database is new, in other words whether we are not
* loading a preexisting database from disk.
*/
this.isNewDb = () => __awaiter(this, void 0, void 0, function* () {
if (this.storage && this.storage !== ':memory:') {
// check if database file exists
try {
yield fs_1.promises.access(this.storage);
return false;
}
catch (err) {
if (err.code !== 'ENOENT') {
// we ignore errors due to file not existing, otherwise throw
throw err;
}
}
}
return true;
});
this.close = () => {
return this.sequelize.close();
};
this.sequelize = new sequelize_1.Sequelize({
storage,
logging: this.logger.trace,
dialect: 'sqlite',
});
this.models = loadModels(this.sequelize);
}
}
DB.VERSION = 1;
return DB;
})();
exports.default = DB;
//# sourceMappingURL=DB.js.map