ilp-protocol-ccp
Version:
Implementation of the Connector-to-Connector Protocol (CCP)
243 lines • 11.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeCcpResponse = exports.constructCcpResponse = exports.deserializeCcpResponse = exports.serializeCcpRouteUpdateRequest = exports.constructCcpRouteUpdateRequest = exports.serializeCcpRouteUpdateRequestPayload = exports.deserializeCcpRouteUpdateRequest = exports.extractCcpRouteUpdateRequest = exports.deserializeCcpRouteUpdateRequestPayload = exports.serializeCcpRouteControlRequest = exports.constructCcpRouteControlRequest = exports.serializeCcpRouteControlRequestPayload = exports.deserializeCcpRouteControlRequest = exports.extractCcpRouteControlRequest = exports.deserializeCcpRouteControlRequestPayload = exports.PropId = exports.ModeReverseMap = exports.Mode = exports.PEER_PROTOCOL_CONDITION = exports.PEER_PROTOCOL_FULFILLMENT = exports.CCP_UPDATE_DESTINATION = exports.CCP_CONTROL_DESTINATION = void 0;
const ilp_packet_1 = require("ilp-packet");
const oer_utils_1 = require("oer-utils");
const uuid_1 = require("./uuid");
exports.CCP_CONTROL_DESTINATION = 'peer.route.control';
exports.CCP_UPDATE_DESTINATION = 'peer.route.update';
exports.PEER_PROTOCOL_FULFILLMENT = Buffer.alloc(32);
exports.PEER_PROTOCOL_CONDITION = Buffer.from('Zmh6rfhivXdsj8GLjp+OIAiXFIVu4jOzkCpZHQ1fKSU=', 'base64');
const PEER_PROTOCOL_EXPIRY_DURATION = 60000;
var Mode;
(function (Mode) {
Mode[Mode["MODE_IDLE"] = 0] = "MODE_IDLE";
Mode[Mode["MODE_SYNC"] = 1] = "MODE_SYNC";
})(Mode = exports.Mode || (exports.Mode = {}));
exports.ModeReverseMap = ['IDLE', 'SYNC'];
var PropId;
(function (PropId) {
})(PropId = exports.PropId || (exports.PropId = {}));
const deserializeCcpRouteControlRequestPayload = (data) => {
const reader = new oer_utils_1.Reader(data);
const mode = reader.readUInt8Number();
const lastKnownRoutingTableId = (0, uuid_1.readUuid)(reader);
const lastKnownEpoch = reader.readUInt32Number();
const featureCount = reader.readVarUIntNumber();
const features = [];
for (let i = 0; i < featureCount; i++) {
features.push(reader.readVarOctetString().toString('utf8'));
}
return {
mode,
lastKnownRoutingTableId,
lastKnownEpoch,
features,
};
};
exports.deserializeCcpRouteControlRequestPayload = deserializeCcpRouteControlRequestPayload;
const extractCcpRouteControlRequest = (packet) => {
if (packet.destination !== exports.CCP_CONTROL_DESTINATION) {
throw new TypeError('packet is not a CCP route control request.');
}
if (!exports.PEER_PROTOCOL_CONDITION.equals(packet.executionCondition)) {
throw new Error('packet does not contain correct condition for a peer protocol request.');
}
if (Date.now() > Number(packet.expiresAt)) {
throw new Error('CCP route control request packet is expired.');
}
return deserializeCcpRouteControlRequestPayload(packet.data);
};
exports.extractCcpRouteControlRequest = extractCcpRouteControlRequest;
const deserializeCcpRouteControlRequest = (request) => {
const packet = (0, ilp_packet_1.deserializeIlpPrepare)(request);
return extractCcpRouteControlRequest(packet);
};
exports.deserializeCcpRouteControlRequest = deserializeCcpRouteControlRequest;
const serializeCcpRouteControlRequestPayload = (request) => {
const writer = new oer_utils_1.Writer();
writer.writeUInt8(request.mode);
(0, uuid_1.writeUuid)(writer, request.lastKnownRoutingTableId);
writer.writeUInt32(request.lastKnownEpoch);
writer.writeVarUInt(request.features.length);
for (const feature of request.features) {
writer.writeVarOctetString(Buffer.from(feature, 'utf8'));
}
return writer.getBuffer();
};
exports.serializeCcpRouteControlRequestPayload = serializeCcpRouteControlRequestPayload;
const constructCcpRouteControlRequest = (request) => {
return {
amount: '0',
destination: exports.CCP_CONTROL_DESTINATION,
executionCondition: exports.PEER_PROTOCOL_CONDITION,
expiresAt: new Date(Date.now() + PEER_PROTOCOL_EXPIRY_DURATION),
data: serializeCcpRouteControlRequestPayload(request),
};
};
exports.constructCcpRouteControlRequest = constructCcpRouteControlRequest;
const serializeCcpRouteControlRequest = (request) => {
return (0, ilp_packet_1.serializeIlpPrepare)(constructCcpRouteControlRequest(request));
};
exports.serializeCcpRouteControlRequest = serializeCcpRouteControlRequest;
const deserializeCcpRouteUpdateRequestPayload = (payload) => {
const reader = new oer_utils_1.Reader(payload);
const routingTableId = (0, uuid_1.readUuid)(reader);
const currentEpochIndex = reader.readUInt32Number();
const fromEpochIndex = reader.readUInt32Number();
const toEpochIndex = reader.readUInt32Number();
const holdDownTime = reader.readUInt32Number();
const speaker = reader.readVarOctetString().toString('ascii');
const newRoutesCount = reader.readVarUIntNumber();
const newRoutes = [];
for (let i = 0; i < newRoutesCount; i++) {
const prefix = reader.readVarOctetString().toString('ascii');
const pathLength = reader.readVarUIntNumber();
const path = [];
for (let i = 0; i < pathLength; i++) {
path.push(reader.readVarOctetString().toString('ascii'));
}
const auth = reader.read(32);
const propCount = reader.readVarUIntNumber();
const props = [];
for (let i = 0; i < propCount; i++) {
const meta = reader.readUInt8Number();
const isOptional = Boolean(meta & 0x80);
const isTransitive = Boolean(meta & 0x40);
const isPartial = Boolean(meta & 0x20);
const isUtf8 = Boolean(meta & 0x10);
const id = reader.readUInt16Number();
const value = reader.readVarOctetString();
const incompleteProp = {
isOptional,
isTransitive,
isPartial,
id,
};
if (isUtf8) {
props.push(Object.assign(Object.assign({}, incompleteProp), { isUtf8: true, value: value.toString('utf8') }));
}
else {
props.push(Object.assign(Object.assign({}, incompleteProp), { isUtf8: false, value: value }));
}
}
newRoutes.push({
prefix,
path,
auth,
props,
});
}
const withdrawnRoutesCount = reader.readVarUIntNumber();
const withdrawnRoutes = [];
for (let i = 0; i < withdrawnRoutesCount; i++) {
withdrawnRoutes.push(reader.readVarOctetString().toString('utf8'));
}
return {
routingTableId,
currentEpochIndex,
fromEpochIndex,
toEpochIndex,
holdDownTime,
speaker,
newRoutes,
withdrawnRoutes,
};
};
exports.deserializeCcpRouteUpdateRequestPayload = deserializeCcpRouteUpdateRequestPayload;
const extractCcpRouteUpdateRequest = (packet) => {
if (packet.destination !== exports.CCP_UPDATE_DESTINATION) {
throw new TypeError('packet is not a CCP route update request.');
}
if (!exports.PEER_PROTOCOL_CONDITION.equals(packet.executionCondition)) {
throw new Error('packet does not contain correct condition for a peer protocol request.');
}
if (Date.now() > Number(packet.expiresAt)) {
throw new Error('CCP route update request packet is expired.');
}
return deserializeCcpRouteUpdateRequestPayload(packet.data);
};
exports.extractCcpRouteUpdateRequest = extractCcpRouteUpdateRequest;
const deserializeCcpRouteUpdateRequest = (request) => {
const packet = (0, ilp_packet_1.deserializeIlpPrepare)(request);
return extractCcpRouteUpdateRequest(packet);
};
exports.deserializeCcpRouteUpdateRequest = deserializeCcpRouteUpdateRequest;
const serializeCcpRouteUpdateRequestPayload = (request) => {
const writer = new oer_utils_1.Writer();
(0, uuid_1.writeUuid)(writer, request.routingTableId);
writer.writeUInt32(request.currentEpochIndex);
writer.writeUInt32(request.fromEpochIndex);
writer.writeUInt32(request.toEpochIndex);
writer.writeUInt32(request.holdDownTime);
writer.writeVarOctetString(Buffer.from(request.speaker, 'ascii'));
writer.writeVarUInt(request.newRoutes.length);
for (const route of request.newRoutes) {
writer.writeVarOctetString(Buffer.from(route.prefix, 'ascii'));
writer.writeVarUInt(route.path.length);
for (const hop of route.path) {
writer.writeVarOctetString(Buffer.from(hop, 'ascii'));
}
if (route.auth.length !== 32) {
throw new Error('route auth must be 32 bytes. prefix=' + route.prefix);
}
writer.write(route.auth);
writer.writeVarUInt(route.props.length);
for (const prop of route.props) {
let meta = 0;
meta |= prop.isOptional ? 0x80 : 0;
if (prop.isOptional) {
meta |= prop.isTransitive ? 0x40 : 0;
if (prop.isTransitive) {
meta |= prop.isPartial ? 0x20 : 0;
}
}
else {
meta |= 0x40;
}
meta |= prop.isUtf8 ? 0x10 : 0;
writer.writeUInt8(meta);
writer.writeUInt16(prop.id);
writer.writeVarOctetString(prop.isUtf8 ? Buffer.from(prop.value, 'utf8') : prop.value);
}
}
writer.writeVarUInt(request.withdrawnRoutes.length);
for (const route of request.withdrawnRoutes) {
writer.writeVarOctetString(Buffer.from(route, 'ascii'));
}
return writer.getBuffer();
};
exports.serializeCcpRouteUpdateRequestPayload = serializeCcpRouteUpdateRequestPayload;
const constructCcpRouteUpdateRequest = (request) => {
return {
amount: '0',
destination: exports.CCP_UPDATE_DESTINATION,
executionCondition: exports.PEER_PROTOCOL_CONDITION,
expiresAt: new Date(Date.now() + PEER_PROTOCOL_EXPIRY_DURATION),
data: serializeCcpRouteUpdateRequestPayload(request),
};
};
exports.constructCcpRouteUpdateRequest = constructCcpRouteUpdateRequest;
const serializeCcpRouteUpdateRequest = (request) => {
return (0, ilp_packet_1.serializeIlpPrepare)(constructCcpRouteUpdateRequest(request));
};
exports.serializeCcpRouteUpdateRequest = serializeCcpRouteUpdateRequest;
const deserializeCcpResponse = (response) => {
const { fulfillment } = (0, ilp_packet_1.deserializeIlpFulfill)(response);
if (!exports.PEER_PROTOCOL_FULFILLMENT.equals(fulfillment)) {
throw new Error('CCP response does not contain the expected fulfillment.');
}
};
exports.deserializeCcpResponse = deserializeCcpResponse;
const constructCcpResponse = () => {
return {
fulfillment: exports.PEER_PROTOCOL_FULFILLMENT,
data: Buffer.alloc(0),
};
};
exports.constructCcpResponse = constructCcpResponse;
const serializeCcpResponse = () => {
return (0, ilp_packet_1.serializeIlpFulfill)(constructCcpResponse());
};
exports.serializeCcpResponse = serializeCcpResponse;
//# sourceMappingURL=index.js.map