UNPKG

ilp-protocol-stream

Version:

Interledger Transport Protocol for sending multiple streams of money and data over ILP.

463 lines 19.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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 (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamReceiptFrame = exports.StreamDataBlockedFrame = exports.StreamMaxDataFrame = exports.StreamDataFrame = exports.StreamMoneyBlockedFrame = exports.StreamMaxMoneyFrame = exports.StreamMoneyFrame = exports.StreamCloseFrame = exports.ConnectionStreamIdBlockedFrame = exports.ConnectionMaxStreamIdFrame = exports.ConnectionDataBlockedFrame = exports.ConnectionMaxDataFrame = exports.ConnectionAssetDetailsFrame = exports.ConnectionNewAddressFrame = exports.ConnectionCloseFrame = exports.BaseFrame = exports.Packet = exports.FrameType = exports.ErrorCode = exports.IlpPacketType = void 0; const oer_utils_1 = require("oer-utils"); const IlpPacket = __importStar(require("ilp-packet")); const long_1 = __importDefault(require("long")); const crypto_1 = require("./crypto"); const long_2 = require("./util/long"); const VERSION = long_1.default.fromNumber(1, true); const ZERO_BYTES = Buffer.alloc(32); exports.IlpPacketType = { Prepare: IlpPacket.Type.TYPE_ILP_PREPARE, Fulfill: IlpPacket.Type.TYPE_ILP_FULFILL, Reject: IlpPacket.Type.TYPE_ILP_REJECT, }; var ErrorCode; (function (ErrorCode) { ErrorCode[ErrorCode["NoError"] = 1] = "NoError"; ErrorCode[ErrorCode["InternalError"] = 2] = "InternalError"; ErrorCode[ErrorCode["EndpointBusy"] = 3] = "EndpointBusy"; ErrorCode[ErrorCode["FlowControlError"] = 4] = "FlowControlError"; ErrorCode[ErrorCode["StreamIdError"] = 5] = "StreamIdError"; ErrorCode[ErrorCode["StreamStateError"] = 6] = "StreamStateError"; ErrorCode[ErrorCode["FrameFormatError"] = 7] = "FrameFormatError"; ErrorCode[ErrorCode["ProtocolViolation"] = 8] = "ProtocolViolation"; ErrorCode[ErrorCode["ApplicationError"] = 9] = "ApplicationError"; })(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {})); var FrameType; (function (FrameType) { FrameType[FrameType["ConnectionClose"] = 1] = "ConnectionClose"; FrameType[FrameType["ConnectionNewAddress"] = 2] = "ConnectionNewAddress"; FrameType[FrameType["ConnectionMaxData"] = 3] = "ConnectionMaxData"; FrameType[FrameType["ConnectionDataBlocked"] = 4] = "ConnectionDataBlocked"; FrameType[FrameType["ConnectionMaxStreamId"] = 5] = "ConnectionMaxStreamId"; FrameType[FrameType["ConnectionStreamIdBlocked"] = 6] = "ConnectionStreamIdBlocked"; FrameType[FrameType["ConnectionAssetDetails"] = 7] = "ConnectionAssetDetails"; FrameType[FrameType["StreamClose"] = 16] = "StreamClose"; FrameType[FrameType["StreamMoney"] = 17] = "StreamMoney"; FrameType[FrameType["StreamMaxMoney"] = 18] = "StreamMaxMoney"; FrameType[FrameType["StreamMoneyBlocked"] = 19] = "StreamMoneyBlocked"; FrameType[FrameType["StreamData"] = 20] = "StreamData"; FrameType[FrameType["StreamMaxData"] = 21] = "StreamMaxData"; FrameType[FrameType["StreamDataBlocked"] = 22] = "StreamDataBlocked"; FrameType[FrameType["StreamReceipt"] = 23] = "StreamReceipt"; })(FrameType = exports.FrameType || (exports.FrameType = {})); class Packet { constructor(sequence, ilpPacketType, packetAmount = long_1.default.UZERO, frames = []) { this.sequence = (0, long_2.longFromValue)(sequence, true); this.ilpPacketType = ilpPacketType; this.prepareAmount = (0, long_2.longFromValue)(packetAmount, true); this.frames = frames; } static async decryptAndDeserialize(pskEncryptionKey, buffer) { let decrypted; try { decrypted = await (0, crypto_1.decrypt)(pskEncryptionKey, buffer); } catch (err) { throw new Error(`Unable to decrypt packet. Data was corrupted or packet was encrypted with the wrong key`); } return Packet._deserializeUnencrypted(decrypted); } static _deserializeUnencrypted(buffer) { const reader = oer_utils_1.Reader.from(buffer); const version = reader.readUInt8Long(); if (!version.equals(VERSION)) { throw new Error(`Unsupported protocol version: ${version}`); } const ilpPacketType = reader.readUInt8Number(); const sequence = reader.readVarUIntLong(); const packetAmount = reader.readVarUIntLong(); const numFrames = reader.readVarUIntNumber(); const frames = []; for (let i = 0; i < numFrames; i++) { const frame = parseFrame(reader); if (frame) { frames.push(frame); } } return new Packet(sequence, ilpPacketType, packetAmount, frames); } serializeAndEncrypt(pskEncryptionKey, padPacketToSize) { const serialized = this._serialize(); if (padPacketToSize !== undefined) { const paddingSize = padPacketToSize - crypto_1.ENCRYPTION_OVERHEAD - serialized.length; const args = [serialized]; for (let i = 0; i < Math.floor(paddingSize / 32); i++) { args.push(ZERO_BYTES); } args.push(ZERO_BYTES.slice(0, paddingSize % 32)); return (0, crypto_1.encrypt)(pskEncryptionKey, ...args); } return (0, crypto_1.encrypt)(pskEncryptionKey, serialized); } _serialize() { const predictor = new oer_utils_1.Predictor(); this.writeTo(predictor); const writer = new oer_utils_1.Writer(predictor.length); this.writeTo(writer); return writer.getBuffer(); } writeTo(writer) { writer.writeUInt8(VERSION); writer.writeUInt8(this.ilpPacketType); writer.writeVarUInt(this.sequence); writer.writeVarUInt(this.prepareAmount); writer.writeVarUInt(this.frames.length); for (const frame of this.frames) { frame.writeTo(writer); } } byteLength() { const predictor = new oer_utils_1.Predictor(); this.writeTo(predictor); return predictor.getSize() + crypto_1.ENCRYPTION_OVERHEAD; } } exports.Packet = Packet; class BaseFrame { constructor(name) { this.type = FrameType[name]; this.name = name; } static fromContents(_reader) { throw new Error(`class method "fromContents" is not implemented`); } writeTo(writer) { const predictor = new oer_utils_1.Predictor(); this.writeContentsTo(predictor); writer.writeUInt8(this.type); this.writeContentsTo(writer.createVarOctetString(predictor.length)); return writer; } writeContentsTo(contents) { const properties = Object.getOwnPropertyNames(this).filter((propName) => propName !== 'type' && propName !== 'name'); for (const prop of properties) { const value = this[prop]; if (typeof value === 'number') { contents.writeUInt8(value); } else if (typeof value === 'string') { contents.writeVarOctetString(Buffer.from(value, 'utf8')); } else if (Buffer.isBuffer(value)) { contents.writeVarOctetString(value); } else if (long_1.default.isLong(value)) { contents.writeVarUInt(value); } else { throw new Error(`Unexpected property type for property "${prop}": ${typeof value}`); } } } byteLength() { const predictor = new oer_utils_1.Predictor(); this.writeTo(predictor); return predictor.getSize(); } } exports.BaseFrame = BaseFrame; class ConnectionCloseFrame extends BaseFrame { constructor(errorCode, errorMessage) { super('ConnectionClose'); this.type = FrameType.ConnectionClose; this.errorCode = errorCode; this.errorMessage = errorMessage; } static fromContents(reader) { const errorCode = reader.readUInt8Number(); const errorMessage = reader.readVarOctetString().toString(); return new ConnectionCloseFrame(errorCode, errorMessage); } } exports.ConnectionCloseFrame = ConnectionCloseFrame; class ConnectionNewAddressFrame extends BaseFrame { constructor(sourceAccount) { super('ConnectionNewAddress'); this.type = FrameType.ConnectionNewAddress; this.sourceAccount = sourceAccount; } static fromContents(reader) { const sourceAccount = reader.readVarOctetString().toString('utf8'); return new ConnectionNewAddressFrame(sourceAccount); } } exports.ConnectionNewAddressFrame = ConnectionNewAddressFrame; class ConnectionAssetDetailsFrame extends BaseFrame { constructor(sourceAssetCode, sourceAssetScale) { super('ConnectionAssetDetails'); this.type = FrameType.ConnectionAssetDetails; this.sourceAssetCode = sourceAssetCode; this.sourceAssetScale = sourceAssetScale; } static fromContents(reader) { const sourceAssetCode = reader.readVarOctetString().toString('utf8'); const sourceAssetScale = reader.readUInt8Number(); return new ConnectionAssetDetailsFrame(sourceAssetCode, sourceAssetScale); } } exports.ConnectionAssetDetailsFrame = ConnectionAssetDetailsFrame; class ConnectionMaxDataFrame extends BaseFrame { constructor(maxOffset) { super('ConnectionMaxData'); this.type = FrameType.ConnectionMaxData; this.maxOffset = (0, long_2.longFromValue)(maxOffset, true); } static fromContents(reader) { const maxOffset = reader.readVarUIntLong(); return new ConnectionMaxDataFrame(maxOffset); } } exports.ConnectionMaxDataFrame = ConnectionMaxDataFrame; class ConnectionDataBlockedFrame extends BaseFrame { constructor(maxOffset) { super('ConnectionDataBlocked'); this.type = FrameType.ConnectionDataBlocked; this.maxOffset = (0, long_2.longFromValue)(maxOffset, true); } static fromContents(reader) { const maxOffset = reader.readVarUIntLong(); return new ConnectionDataBlockedFrame(maxOffset); } } exports.ConnectionDataBlockedFrame = ConnectionDataBlockedFrame; class ConnectionMaxStreamIdFrame extends BaseFrame { constructor(maxStreamId) { super('ConnectionMaxStreamId'); this.type = FrameType.ConnectionMaxStreamId; this.maxStreamId = (0, long_2.longFromValue)(maxStreamId, true); } static fromContents(reader) { const maxStreamId = reader.readVarUIntLong(); return new ConnectionMaxStreamIdFrame(maxStreamId); } } exports.ConnectionMaxStreamIdFrame = ConnectionMaxStreamIdFrame; class ConnectionStreamIdBlockedFrame extends BaseFrame { constructor(maxStreamId) { super('ConnectionStreamIdBlocked'); this.type = FrameType.ConnectionStreamIdBlocked; this.maxStreamId = (0, long_2.longFromValue)(maxStreamId, true); } static fromContents(reader) { const maxStreamId = reader.readVarUIntLong(); return new ConnectionStreamIdBlockedFrame(maxStreamId); } } exports.ConnectionStreamIdBlockedFrame = ConnectionStreamIdBlockedFrame; class StreamCloseFrame extends BaseFrame { constructor(streamId, errorCode, errorMessage) { super('StreamClose'); this.type = FrameType.StreamClose; this.streamId = (0, long_2.longFromValue)(streamId, true); this.errorCode = errorCode; this.errorMessage = errorMessage; } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const errorCode = reader.readUInt8Number(); const errorMessage = reader.readVarOctetString().toString('utf8'); return new StreamCloseFrame(streamId, errorCode, errorMessage); } } exports.StreamCloseFrame = StreamCloseFrame; class StreamMoneyFrame extends BaseFrame { constructor(streamId, shares) { super('StreamMoney'); this.type = FrameType.StreamMoney; this.streamId = (0, long_2.longFromValue)(streamId, true); this.shares = (0, long_2.longFromValue)(shares, true); } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const amount = reader.readVarUIntLong(); return new StreamMoneyFrame(streamId, amount); } } exports.StreamMoneyFrame = StreamMoneyFrame; class StreamMaxMoneyFrame extends BaseFrame { constructor(streamId, receiveMax, totalReceived) { super('StreamMaxMoney'); this.type = FrameType.StreamMaxMoney; if (typeof receiveMax === 'number' && !isFinite(receiveMax)) { receiveMax = long_1.default.MAX_UNSIGNED_VALUE; } this.streamId = (0, long_2.longFromValue)(streamId, true); this.receiveMax = (0, long_2.longFromValue)(receiveMax, true); this.totalReceived = (0, long_2.longFromValue)(totalReceived, true); } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const receiveMax = saturatingReadVarUInt(reader); const totalReceived = reader.readVarUIntLong(); return new StreamMaxMoneyFrame(streamId, receiveMax, totalReceived); } } exports.StreamMaxMoneyFrame = StreamMaxMoneyFrame; class StreamMoneyBlockedFrame extends BaseFrame { constructor(streamId, sendMax, totalSent) { super('StreamMoneyBlocked'); this.type = FrameType.StreamMoneyBlocked; this.streamId = (0, long_2.longFromValue)(streamId, true); this.sendMax = (0, long_2.longFromValue)(sendMax, true); this.totalSent = (0, long_2.longFromValue)(totalSent, true); } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const sendMax = saturatingReadVarUInt(reader); const totalSent = reader.readVarUIntLong(); return new StreamMoneyBlockedFrame(streamId, sendMax, totalSent); } } exports.StreamMoneyBlockedFrame = StreamMoneyBlockedFrame; class StreamDataFrame extends BaseFrame { constructor(streamId, offset, data) { super('StreamData'); this.type = FrameType.StreamData; this.streamId = (0, long_2.longFromValue)(streamId, true); this.offset = (0, long_2.longFromValue)(offset, true); this.data = data; } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const offset = reader.readVarUIntLong(); const data = reader.readVarOctetString(); return new StreamDataFrame(streamId, offset, data); } toJSON() { return { type: this.type, name: this.name, streamId: this.streamId, offset: this.offset, dataLength: this.data.length, }; } } exports.StreamDataFrame = StreamDataFrame; class StreamMaxDataFrame extends BaseFrame { constructor(streamId, maxOffset) { super('StreamMaxData'); this.type = FrameType.StreamMaxData; this.streamId = (0, long_2.longFromValue)(streamId, true); this.maxOffset = (0, long_2.longFromValue)(maxOffset, true); } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const maxOffset = reader.readVarUIntLong(); return new StreamMaxDataFrame(streamId, maxOffset); } } exports.StreamMaxDataFrame = StreamMaxDataFrame; class StreamDataBlockedFrame extends BaseFrame { constructor(streamId, maxOffset) { super('StreamDataBlocked'); this.type = FrameType.StreamDataBlocked; this.streamId = (0, long_2.longFromValue)(streamId, true); this.maxOffset = (0, long_2.longFromValue)(maxOffset, true); } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const maxOffset = reader.readVarUIntLong(); return new StreamDataBlockedFrame(streamId, maxOffset); } } exports.StreamDataBlockedFrame = StreamDataBlockedFrame; class StreamReceiptFrame extends BaseFrame { constructor(streamId, receipt) { super('StreamReceipt'); this.type = FrameType.StreamReceipt; this.streamId = (0, long_2.longFromValue)(streamId, true); this.receipt = receipt; } static fromContents(reader) { const streamId = reader.readVarUIntLong(); const receipt = reader.readVarOctetString(); return new StreamReceiptFrame(streamId, receipt); } toJSON() { return { type: this.type, name: this.name, streamId: this.streamId, receipt: this.receipt.toString('base64'), }; } } exports.StreamReceiptFrame = StreamReceiptFrame; function parseFrame(reader) { const type = reader.readUInt8Number(); const contents = oer_utils_1.Reader.from(reader.readVarOctetString()); switch (type) { case FrameType.ConnectionClose: return ConnectionCloseFrame.fromContents(contents); case FrameType.ConnectionNewAddress: return ConnectionNewAddressFrame.fromContents(contents); case FrameType.ConnectionAssetDetails: return ConnectionAssetDetailsFrame.fromContents(contents); case FrameType.ConnectionMaxData: return ConnectionMaxDataFrame.fromContents(contents); case FrameType.ConnectionDataBlocked: return ConnectionDataBlockedFrame.fromContents(contents); case FrameType.ConnectionMaxStreamId: return ConnectionMaxStreamIdFrame.fromContents(contents); case FrameType.ConnectionStreamIdBlocked: return ConnectionStreamIdBlockedFrame.fromContents(contents); case FrameType.StreamClose: return StreamCloseFrame.fromContents(contents); case FrameType.StreamMoney: return StreamMoneyFrame.fromContents(contents); case FrameType.StreamMaxMoney: return StreamMaxMoneyFrame.fromContents(contents); case FrameType.StreamMoneyBlocked: return StreamMoneyBlockedFrame.fromContents(contents); case FrameType.StreamData: return StreamDataFrame.fromContents(contents); case FrameType.StreamMaxData: return StreamMaxDataFrame.fromContents(contents); case FrameType.StreamDataBlocked: return StreamDataBlockedFrame.fromContents(contents); case FrameType.StreamReceipt: return StreamReceiptFrame.fromContents(contents); default: return undefined; } } function saturatingReadVarUInt(reader) { if (reader.peekVarOctetString().length > 8) { reader.skipVarOctetString(); return long_1.default.MAX_UNSIGNED_VALUE; } else { return reader.readVarUIntLong(); } } //# sourceMappingURL=packet.js.map