xud
Version:
Exchange Union Daemon
156 lines • 7.4 kB
JavaScript
;
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 grpc_1 = __importDefault(require("grpc"));
const node_forge_1 = require("node-forge");
const os_1 = require("os");
const xudrpc_grpc_pb_1 = require("../proto/xudrpc_grpc_pb");
const errors_1 = __importDefault(require("./errors"));
const GrpcInitService_1 = __importDefault(require("./GrpcInitService"));
const GrpcService_1 = __importDefault(require("./GrpcService"));
const serverProxy_1 = __importDefault(require("./serverProxy"));
class GrpcServer {
constructor(logger) {
this.logger = logger;
this.grpcService = new GrpcService_1.default();
this.grpcInitService = new GrpcInitService_1.default();
/**
* Start the server and begin listening on the provided port
* @returns true if the server started listening successfully, false otherwise
*/
this.listen = (port, host, tlsCertPath, tlsKeyPath) => __awaiter(this, void 0, void 0, function* () {
assert_1.default(Number.isInteger(port) && port > 1023 && port < 65536, 'port must be an integer between 1024 and 65535');
let certificate;
let privateKey;
try {
[certificate, privateKey] = yield Promise.all([fs_1.promises.readFile(tlsCertPath), fs_1.promises.readFile(tlsKeyPath)]);
}
catch (err) {
this.logger.info('Could not load gRPC TLS certificate. Generating new one');
const { tlsCert, tlsKey } = yield this.generateCertificate(tlsCertPath, tlsKeyPath);
this.logger.info('gRPC TLS certificate created');
certificate = Buffer.from(tlsCert);
privateKey = Buffer.from(tlsKey);
}
// tslint:disable-next-line:no-null-keyword
const credentials = grpc_1.default.ServerCredentials.createSsl(null, [{
cert_chain: certificate,
private_key: privateKey,
}], false);
const bindCode = this.server.bind(`${host}:${port}`, credentials);
if (bindCode !== port) {
const error = errors_1.default.COULD_NOT_BIND(port.toString());
this.logger.error(error.message);
throw error;
}
this.server.start();
this.logger.info(`gRPC server listening on ${host}:${port}`);
});
/**
* Stop listening for requests
*/
this.close = () => {
if (this.grpcService) {
this.grpcService.closeStreams();
}
return new Promise((resolve) => {
this.server.tryShutdown(() => {
this.logger.info('gRPC server completed shutdown');
resolve();
});
});
};
/**
* Generate a new certificate and save it to the disk
* @returns the cerificate and its private key
*/
this.generateCertificate = (tlsCertPath, tlsKeyPath) => __awaiter(this, void 0, void 0, function* () {
const keys = node_forge_1.pki.rsa.generateKeyPair(1024);
const cert = node_forge_1.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = String(Math.floor(Math.random() * 1024) + 1);
// TODO: handle expired certificates
const date = new Date();
cert.validity.notBefore = date;
cert.validity.notAfter = new Date(date.getFullYear() + 5, date.getMonth(), date.getDay());
const attributes = [
{
name: 'organizationName',
value: 'XUD autogenerated certificate',
},
{
name: 'commonName',
value: os_1.hostname(),
},
];
cert.setSubject(attributes);
cert.setIssuer(attributes);
// TODO: add tlsextradomain and tlsextraip options
cert.setExtensions([
{
name: 'subjectAltName',
altNames: [
{
type: 2,
value: 'localhost',
},
{
type: 7,
ip: '127.0.0.1',
},
],
},
]);
cert.sign(keys.privateKey, node_forge_1.md.sha256.create());
const certificate = node_forge_1.pki.certificateToPem(cert);
const privateKey = node_forge_1.pki.privateKeyToPem(keys.privateKey);
yield Promise.all([fs_1.promises.writeFile(tlsCertPath, certificate), fs_1.promises.writeFile(tlsKeyPath, privateKey)]);
return {
tlsCert: certificate,
tlsKey: privateKey,
};
});
this.server = serverProxy_1.default(new grpc_1.default.Server());
this.grpcInitService = new GrpcInitService_1.default();
this.grpcService = new GrpcService_1.default();
this.server.addService(xudrpc_grpc_pb_1.XudInitService, this.grpcInitService);
this.server.addService(xudrpc_grpc_pb_1.XudService, this.grpcService);
this.server.use((ctx, next) => __awaiter(this, void 0, void 0, function* () {
logger.trace(`received call ${ctx.service.path}`);
yield next();
const status = ctx.status || ctx.call.status;
if (!status) {
logger.trace(`unknown status for call ${ctx.service.path}`);
}
else if (status.code !== 0) {
if (typeof status.details === 'object') {
logger.error(`call ${ctx.service.path} errored with code ${status.details.code}: ${status.details.message}`);
}
else if (typeof status.details === 'string') {
logger.error(`call ${ctx.service.path} errored with code ${status.code}: ${status.details}`);
}
else {
logger.error(`call ${ctx.service.path} errored with code ${status.code}`);
}
}
else {
logger.trace(`call ${ctx.service.path} succeeded`);
}
}));
}
}
exports.default = GrpcServer;
//# sourceMappingURL=GrpcServer.js.map