UNPKG

ilp-protocol-ccp

Version:

Implementation of the Connector-to-Connector Protocol (CCP)

243 lines 11.1 kB
"use strict"; 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