madeline-ton
Version:
Pure JS client-side implementation of the Telegram TON blockchain protocol
379 lines (283 loc) • 11 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _stream = _interopRequireDefault(require("./stream"));
var _objects = _interopRequireDefault(require("./objects"));
var _random = require("../crypto-sync/random");
var _zlib = require("zlib");
var _tools = require("../tools");
var _long = _interopRequireDefault(require("../lib/bigint/long"));
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
/**
* Custom TL parser based on an unreleased project of mine (madeline.py).
* Could've based it on my MadelineProto, but madeline.py's parser is way cleaner.
*/
var Parser =
/*#__PURE__*/
function () {
/**
* Set TL Object store
* @param {Objects} objects
*/
function Parser(objects, callbacks) {
(0, _classCallCheck2["default"])(this, Parser);
this.objects = objects;
this.callbacks = callbacks;
}
/**
* Set TL Object store
* @param {Objects} objects
*/
(0, _createClass2["default"])(Parser, [{
key: "setObjectStore",
value: function setObjectStore(objects) {
this.objects = objects;
}
/**
* Deserialize TL object
* @param {Stream} data Data
* @param {Object} type Type info
*/
}, {
key: "deserialize",
value: function deserialize(data, type) {
type = type || {
type: ''
};
switch (type['type']) {
case 'int':
return data.readSignedInt();
case '#':
return data.readUnsignedInt();
case 'long':
return data.readSignedLong();
case 'int128':
return data.readUnsignedInts(4);
case 'int256':
return data.readUnsignedInts(8);
case 'int512':
return data.readUnsignedInts(16);
case 'double':
return data.readDouble();
case 'Bool':
return data.readSignedInt() === 0x997275b5;
case 'string':
return data.readString();
case 'bytes':
return data.readBytes();
}
if (!type['predicate']) {
type = _objectSpread({}, type, {}, this.objects.findById(data.readSignedInt()));
}
switch (type['predicate']) {
case 'gzip_packed':
return this.deserialize((0, _zlib.gunzipSync)(data.readBytes()), {
layer: type['layer']
});
case 'dataJSON':
return JSON.parse(data.readString());
case 'vector':
var length = data.readUnsignedInt();
var _result = Array(length);
for (var x = 0; x < length; x++) {
_result[x] = this.deserialize(data, type['subtype']);
}
return _result;
}
var result = {
_: type['predicate']
};
for (var key in type['params']) {
var param = type['params'][key];
if (param['pow']) {
if (param['type'] === 'true') {
result[key] = Boolean(result[param['flag']] & param['pow']);
continue;
}
if (!(result[param['flag']] & param['pow'])) {
continue;
}
}
result[key] = this.deserialize(data, param);
} // Later should delete unused flags
return result;
}
/**
* Serialize TL
* @param {Stream} stream Stream
* @param {mixed} data Data to serialize
* @param {Object} type TL type definition
*/
}, {
key: "serialize",
value: function serialize(stream, data, type) {
type = type || {};
switch (type['type']) {
case 'int':
return stream.writeSignedInt(data);
case "#":
return stream.writeUnsignedInt(data);
case 'long':
if (typeof data === 'string' || data instanceof String) {
data = _long["default"].fromString(data, 10);
data = [data.low_, data.high_];
}
return stream.writeSignedLong(data);
case 'int128':
if (typeof data === 'string' || data instanceof String) {
data = (0, _tools.atobInt32)(data);
}
return stream.writeUnsignedInts(data);
case 'int256':
if (typeof data === 'string' || data instanceof String) {
data = (0, _tools.atobInt32)(data);
}
return stream.writeUnsignedInts(data);
case 'int512':
if (typeof data === 'string' || data instanceof String) {
data = (0, _tools.atobInt32)(data);
}
return stream.writeUnsignedInts(data);
case 'double':
return stream.writeDouble(data);
case 'Bool':
return stream.writeSignedInt(data ? 0x997275b5 : 0xbc799737);
case 'string':
return stream.writeString(data);
case 'bytes':
return stream.writeBytes(data);
case 'DataJSON':
data = {
_: 'dataJSON',
data: JSON.stringify(data)
};
}
if (!type['predicate']) {
if (type['type'] === 'Vector t') {
type = _objectSpread({}, type, {}, this.objects.findByPredicateAndLayer('vector', type['layer']));
} else {
type = _objectSpread({}, type, {}, this.objects.findByPredicateAndLayer(data['_'] || data['@type'], type['layer']));
}
stream.prepareLength(1 + type['minSize']).writeSignedInt(type['id']);
} else {
stream.prepareLength(type['minSize']);
}
if (type['predicate'] === 'vector') {
stream.writeUnsignedInt(data.length);
if (this.objects.basicSizes[type['subtype']]) {
stream.prepareLength(data.length * this.objects.basicSizes[type['subtype']]);
}
for (var element in data) {
this.serialize(stream, data[element], type['subtype']);
}
return;
}
if (typeof data === 'string' || data instanceof String) {
data = this.callbacks.typeConversion[type['type']](data);
}
var flagSize = 0;
for (var key in type['params']) {
var param = type['params'][key];
if (param['pow']) {
var flags = data[param['flag']] || 0;
if (!data[key]) {
flags = flags & ~param['pow'];
} else {
flags = flags | param['pow'];
flagSize += this.objects.basicSizes[param['type']] || 0;
}
data[param['flag']] = flags;
}
}
stream.prepareLength(flagSize);
for (var _key in type['params']) {
var _param = type['params'][_key];
if (typeof data[_key] === 'undefined') {
if (_param['pow']) {
continue;
}
if (_param['type'] === '#') {
stream.writeUnsignedInt(0);
continue;
}
if (_key === 'random_bytes') {
// This should run in a worker
stream.writeBytes((0, _random.secureRandom)(new Uint8Array(15 + 4 * (0, _random.fastRandomInt)(3))));
continue;
}
if (_key === 'random_id') {
switch (_param['type']) {
case 'int':
stream.writeUnsignedInts((0, _random.fastRandom)(new Uint32Array(1)));
break;
case 'long':
stream.writeUnsignedInts((0, _random.fastRandom)(new Uint32Array(2)));
break;
case 'Vector t':
if (data['id']) {
var len = data['id'].length; // forwardMessages
if (!_param['predicate']) {
stream.prepareLength(1).writeSignedInt(this.objects.findByPredicateAndLayer('vector', type['layer'])['id']);
}
stream.prepareLength(1 + len * 2).writeUnsignedInt(len).writeUnsignedInts((0, _random.fastRandom)(new Uint32Array(len * 2)));
break;
}
// Fall through
default:
throw new Error('Missing parameter ' + _key);
}
continue;
}
if (_param['type'] === 'string' || _param['type'] === 'bytes') {
stream.writeBytes(new Uint8Array(0));
continue;
}
if (_param['type'] === 'Vector t') {
if (!_param['predicate']) {
stream.prepareLength(1).writeSignedInt(this.objects.findByPredicateAndLayer('vector', type['layer'])['id']);
}
stream.prepareLength(1).writeUnsignedInt(0);
continue;
}
if (_key === 'hash' && _param['type'] === 'int') {
stream.writeSignedInt(0);
continue;
}
if (type['type'] === 'DocumentAttribute' && ['w', 'h', 'duration'].includes(_key)) {
stream.writeSignedInt(0);
continue;
}
try {
var id = this.objects.findByPredicateAndLayer('input' + _param['type'] + "Empty", _param['layer']);
if (id['type'] === _param['type']) {
stream.writeSignedInt(id['id']);
continue;
}
} catch (e) {}
var paramType = _param['type'];
paramType = paramType[0].toLowerCase().concat(paramType.slice(1));
try {
var _id = this.objects.findByPredicateAndLayer(paramType + "Empty", _param['layer']);
if (_id['type'] === _param['type']) {
stream.writeSignedInt(_id['id']);
continue;
}
} catch (e) {}
throw new Error('Missing parameter ' + _key);
}
this.serialize(stream, data[_key], _param);
}
return stream;
}
}]);
return Parser;
}();
var _default = Parser;
exports["default"] = _default;