UNPKG

bluzelle-binary-codec

Version:
195 lines (168 loc) 6.81 kB
'use strict';var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {return typeof obj;} : function (obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;};function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}var _ = require('lodash'); var assert = require('assert'); var BN = require('bn.js'); var Decimal = require('decimal.js'); var makeClass = require('../utils/make-class');var _require = require('./serialized-type'),SerializedType = _require.SerializedType;var _require2 = require('../utils/bytes-utils'),bytesToHex = _require2.bytesToHex;var _require3 = require('./currency'),Currency = _require3.Currency;var _require4 = require('./account-id'),AccountID = _require4.AccountID;var _require5 = require('./uint-64'),UInt64 = _require5.UInt64; var MIN_IOU_EXPONENT = -96; var MAX_IOU_EXPONENT = 80; var MAX_IOU_PRECISION = 16; var MIN_IOU_MANTISSA = '1000' + '0000' + '0000' + '0000'; // 16 digits var MAX_IOU_MANTISSA = '9999' + '9999' + '9999' + '9999'; // .. var MAX_IOU = new Decimal(MAX_IOU_MANTISSA + 'e' + MAX_IOU_EXPONENT); var MIN_IOU = new Decimal(MIN_IOU_MANTISSA + 'e' + MIN_IOU_EXPONENT); var DROPS_PER_XPA = new Decimal('1e6'); var MAX_NETWORK_DROPS = new Decimal('1e17'); var MIN_XPA = new Decimal('1e-6'); var MAX_XPA = MAX_NETWORK_DROPS.dividedBy(DROPS_PER_XPA); // Never use exponential form Decimal.config({ toExpPos: MAX_IOU_EXPONENT + MAX_IOU_PRECISION, toExpNeg: MIN_IOU_EXPONENT - MAX_IOU_PRECISION }); var AMOUNT_PARAMETERS_DESCRIPTION = '\nNative values must be described in drops, a million of which equal one XPA.\nThis must be an integer number, with the absolute value not exceeding ' + MAX_NETWORK_DROPS + '\n\nIOU values must have a maximum precision of ' + MAX_IOU_PRECISION + ' significant digits. They are serialized as\na canonicalised mantissa and exponent. \n\nThe valid range for a mantissa is between ' + MIN_IOU_MANTISSA + ' and ' + MAX_IOU_MANTISSA + '\nThe exponent must be >= ' + MIN_IOU_EXPONENT + ' and <= ' + MAX_IOU_EXPONENT + '\n\nThus the largest serializable IOU value is:\n' + MAX_IOU.toString() + '\n\nAnd the smallest:\n' + MIN_IOU.toString() + '\n'; function isDefined(val) { return !_.isUndefined(val); } function raiseIllegalAmountError(value) { throw new Error(value.toString() + ' is an illegal amount\n' + AMOUNT_PARAMETERS_DESCRIPTION); } var parsers = { string: function string(str) { if (!str.match(/\d+/)) { raiseIllegalAmountError(str); } return [new Decimal(str).dividedBy(DROPS_PER_XPA), Currency.XPA]; }, object: function object(_object) { assert(isDefined(_object.currency), 'currency must be defined'); assert(isDefined(_object.issuer), 'issuer must be defined'); return [new Decimal(_object.value), Currency.from(_object.currency), AccountID.from(_object.issuer)]; } }; var Amount = makeClass({ Amount: function Amount(value, currency, issuer) { this.value = value || new Decimal('0'); this.currency = currency || Currency.XPA; this.issuer = issuer || null; this.assertValueIsValid(); }, mixins: SerializedType, statics: { from: function from(value) { if (value instanceof this) { return value; } var parser = parsers[typeof value === 'undefined' ? 'undefined' : _typeof(value)]; if (parser) { return new (Function.prototype.bind.apply(this, [null].concat(_toConsumableArray(parser(value)))))(); } throw new Error('unsupported value: ' + value); }, fromParser: function fromParser(parser) { var mantissa = parser.read(8); var b1 = mantissa[0]; var b2 = mantissa[1]; var isIOU = b1 & 0x80; var isPositive = b1 & 0x40; var sign = isPositive ? '' : '-'; if (isIOU) { mantissa[0] = 0; var currency = parser.readType(Currency); var issuer = parser.readType(AccountID); var exponent = ((b1 & 0x3F) << 2) + ((b2 & 0xff) >> 6) - 97; mantissa[1] &= 0x3F; // decimal.js won't accept e notation with hex var value = new Decimal(sign + '0x' + bytesToHex(mantissa)). times('1e' + exponent); return new this(value, currency, issuer); } mantissa[0] &= 0x3F; var drops = new Decimal(sign + '0x' + bytesToHex(mantissa)); var xpaValue = drops.dividedBy(DROPS_PER_XPA); return new this(xpaValue, Currency.XPA); } }, assertValueIsValid: function assertValueIsValid() { // zero is always a valid amount value if (!this.isZero()) { if (this.isNative()) { var abs = this.value.abs(); if (abs.lt(MIN_XPA) || abs.gt(MAX_XPA)) { // value is in XPA scale, but show the value in canonical json form raiseIllegalAmountError(this.value.times(DROPS_PER_XPA)); } } else { var p = this.value.precision(); var e = this.exponent(); if (p > MAX_IOU_PRECISION || e > MAX_IOU_EXPONENT || e < MIN_IOU_EXPONENT) { raiseIllegalAmountError(this.value); } } } }, isNative: function isNative() { return this.currency.isNative(); }, mantissa: function mantissa() { return new UInt64( new BN(this.value.times('1e' + -this.exponent()).abs().toString())); }, isZero: function isZero() { return this.value.isZero(); }, exponent: function exponent() { return this.isNative() ? -6 : this.value.e - 15; }, valueString: function valueString() { return (this.isNative() ? this.value.times(DROPS_PER_XPA) : this.value). toString(); }, toBytesSink: function toBytesSink(sink) { var isNative = this.isNative(); var notNegative = !this.value.isNegative(); var mantissa = this.mantissa().toBytes(); if (isNative) { mantissa[0] |= notNegative ? 0x40 : 0; sink.put(mantissa); } else { mantissa[0] |= 0x80; if (!this.isZero()) { if (notNegative) { mantissa[0] |= 0x40; } var exponent = this.value.e - 15; var exponentByte = 97 + exponent; mantissa[0] |= exponentByte >>> 2; mantissa[1] |= (exponentByte & 0x03) << 6; } sink.put(mantissa); this.currency.toBytesSink(sink); this.issuer.toBytesSink(sink); } }, toJSON: function toJSON() { var valueString = this.valueString(); if (this.isNative()) { return valueString; } return { value: valueString, currency: this.currency.toJSON(), issuer: this.issuer.toJSON() }; } }); module.exports = { Amount: Amount };