ilp-protocol-stream
Version:
Interledger Transport Protocol for sending multiple streams of money and data over ILP.
463 lines • 19.8 kB
JavaScript
"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