From 5e9f59769590d27f1ab3ee9de3fbb5c6a0b804fb Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 10 Apr 2020 14:14:22 -0300 Subject: Update wallet-core to latest master HEAD --- taler-wallet-android.js | 19925 +++++++++++++++++++++++----------------------- 1 file changed, 9975 insertions(+), 9950 deletions(-) diff --git a/taler-wallet-android.js b/taler-wallet-android.js index bb9b4dc..b1203f4 100644 --- a/taler-wallet-android.js +++ b/taler-wallet-android.js @@ -14,7 +14,6 @@ var tty = _interopDefault(require('tty')); var util = _interopDefault(require('util')); var os = _interopDefault(require('os')); var zlib = _interopDefault(require('zlib')); -var querystring = _interopDefault(require('querystring')); var fs = _interopDefault(require('fs')); var worker_threads = _interopDefault(require('worker_threads')); @@ -32,6 +31,244 @@ function getCjsExportFromNamespace (n) { return n && n['default'] || n; } +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* global Reflect, Promise */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; + +function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +function __exportStar(m, exports) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} + +function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +} + +function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +} +function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } +} + +function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +} +function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result.default = mod; + return result; +} + +function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + +function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); +} + +function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; +} + +var tslib_es6 = /*#__PURE__*/Object.freeze({ + __proto__: null, + __extends: __extends, + get __assign () { return __assign; }, + __rest: __rest, + __decorate: __decorate, + __param: __param, + __metadata: __metadata, + __awaiter: __awaiter, + __generator: __generator, + __exportStar: __exportStar, + __values: __values, + __read: __read, + __spread: __spread, + __spreadArrays: __spreadArrays, + __await: __await, + __asyncGenerator: __asyncGenerator, + __asyncDelegator: __asyncDelegator, + __asyncValues: __asyncValues, + __makeTemplateObject: __makeTemplateObject, + __importStar: __importStar, + __importDefault: __importDefault, + __classPrivateFieldGet: __classPrivateFieldGet, + __classPrivateFieldSet: __classPrivateFieldSet +}); + var codec = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler @@ -64,8 +301,7 @@ class DecodingError extends Error { } exports.DecodingError = DecodingError; function renderContext(c) { - var _a; - const p = (_a = c) === null || _a === void 0 ? void 0 : _a.path; + const p = c === null || c === void 0 ? void 0 : c.path; if (p) { return p.join("."); } @@ -75,8 +311,8 @@ function renderContext(c) { } exports.renderContext = renderContext; function joinContext(c, part) { - var _a, _b; - const path = (_b = (_a = c) === null || _a === void 0 ? void 0 : _a.path, (_b !== null && _b !== void 0 ? _b : [])); + var _a; + const path = (_a = c === null || c === void 0 ? void 0 : c.path) !== null && _a !== void 0 ? _a : []; return { path: path.concat([part]), }; @@ -293,14 +529,10 @@ function makeCodecOptional(innerCodec) { return undefined; } return innerCodec.decode(x, c); - } + }, }; } exports.makeCodecOptional = makeCodecOptional; -function typecheckedCodec(c) { - return c; -} -exports.typecheckedCodec = typecheckedCodec; }); @@ -318,7 +550,6 @@ var codec_10 = codec.codecForString; var codec_11 = codec.codecForAny; var codec_12 = codec.makeCodecForConstString; var codec_13 = codec.makeCodecOptional; -var codec_14 = codec.typecheckedCodec; var amounts = createCommonjsModule(function (module, exports) { /* @@ -358,11 +589,11 @@ exports.fractionalLength = 8; * Maximum allowed value field of an amount. */ exports.maxAmountValue = Math.pow(2, 52); -exports.codecForAmountJson = () => codec.typecheckedCodec(codec.makeCodecForObject() +exports.codecForAmountJson = () => codec.makeCodecForObject() .property("currency", codec.codecForString) .property("value", codec.codecForNumber) .property("fraction", codec.codecForNumber) - .build("AmountJson")); + .build("AmountJson"); /** * Get an amount that represents zero units of a currency. */ @@ -531,7 +762,7 @@ function parse(s) { if (tail.length > exports.fractionalLength + 1) { return undefined; } - let value = Number.parseInt(res[2]); + const value = Number.parseInt(res[2]); if (value > exports.maxAmountValue) { return undefined; } @@ -570,7 +801,7 @@ exports.fromFloat = fromFloat; * Convert to standard human-readable string representation that's * also used in JSON formats. */ -function toString(a) { +function stringify(a) { const av = a.value + Math.floor(a.fraction / exports.fractionalBase); const af = a.fraction % exports.fractionalBase; let s = av.toString(); @@ -587,7 +818,7 @@ function toString(a) { } return `${a.currency}:${s}`; } -exports.toString = toString; +exports.stringify = stringify; /** * Check if the argument is a valid amount in string form. */ @@ -603,7 +834,21 @@ function check(a) { return false; } } -exports.check = check; +// Export all amount-related functions here for better IDE experience. +exports.Amounts = { + stringify: stringify, + parse: parse, + parseOrThrow: parseOrThrow, + cmp: cmp, + add: add, + sum: sum, + sub: sub, + check: check, + getZero: getZero, + isZero: isZero, + maxAmountValue: exports.maxAmountValue, + fromFloat: fromFloat, +}; }); @@ -624,7 +869,8 @@ var amounts_13 = amounts.isZero; var amounts_14 = amounts.parse; var amounts_15 = amounts.parseOrThrow; var amounts_16 = amounts.fromFloat; -var amounts_17 = amounts.check; +var amounts_17 = amounts.stringify; +var amounts_18 = amounts.Amounts; var promiseUtils = createCommonjsModule(function (module, exports) { /* @@ -685,6 +931,8 @@ unwrapExports(promiseUtils); var promiseUtils_1 = promiseUtils.openPromise; var promiseUtils_2 = promiseUtils.AsyncCondition; +var tslib_1 = getCjsExportFromNamespace(tslib_es6); + var query = createCommonjsModule(function (module, exports) { /* This file is part of TALER @@ -701,16 +949,8 @@ var query = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * Database query abstractions. * @module Query @@ -720,6 +960,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); * Imports. */ +/** + * Exception that should be thrown by client code to abort a transaction. + */ +exports.TransactionAbort = Symbol("transaction_abort"); /** * Definition of an object store. */ @@ -817,7 +1061,7 @@ class ResultStream { }; } toArray() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const arr = []; while (true) { const x = yield this.next(); @@ -832,7 +1076,7 @@ class ResultStream { }); } map(f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const arr = []; while (true) { const x = yield this.next(); @@ -847,7 +1091,7 @@ class ResultStream { }); } forEachAsync(f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { while (true) { const x = yield this.next(); if (x.hasValue) { @@ -860,7 +1104,7 @@ class ResultStream { }); } forEach(f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { while (true) { const x = yield this.next(); if (x.hasValue) { @@ -873,7 +1117,7 @@ class ResultStream { }); } filter(f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const arr = []; while (true) { const x = yield this.next(); @@ -890,7 +1134,7 @@ class ResultStream { }); } next() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (this.gotCursorEnd) { return { hasValue: false }; } @@ -941,6 +1185,13 @@ class TransactionHandle { const req = this.tx.objectStore(store.name).openCursor(key); return new ResultStream(req); } + iterIndexed(index, key) { + const req = this.tx + .objectStore(index.storeName) + .index(index.indexName) + .openCursor(key); + return new ResultStream(req); + } delete(store, key) { const req = this.tx.objectStore(store.name).delete(key); return requestToPromise(req); @@ -954,7 +1205,7 @@ exports.TransactionHandle = TransactionHandle; function runWithTransaction(db, stores, f, mode) { const stack = Error("Failed transaction was started here."); return new Promise((resolve, reject) => { - const storeName = stores.map(x => x.name); + const storeName = stores.map((x) => x.name); const tx = db.transaction(storeName, mode); let funResult = undefined; let gotFunResult = false; @@ -986,21 +1237,24 @@ function runWithTransaction(db, stores, f, mode) { reject(exports.TransactionAbort); }; const th = new TransactionHandle(tx); - const resP = f(th); + const resP = Promise.resolve().then(() => f(th)); resP - .then(result => { + .then((result) => { gotFunResult = true; funResult = result; }) - .catch(e => { + .catch((e) => { if (e == exports.TransactionAbort) { console.info("aborting transaction"); } else { - tx.abort(); console.error("Transaction failed:", e); console.error(stack); + tx.abort(); } + }) + .catch((e) => { + console.error("fatal: aborting transaction failed", e); }); }); } @@ -1026,11 +1280,11 @@ exports.Index = Index; function openDatabase(idbFactory, databaseName, databaseVersion, onVersionChange, onUpgradeNeeded) { return new Promise((resolve, reject) => { const req = idbFactory.open(databaseName, databaseVersion); - req.onerror = e => { + req.onerror = (e) => { console.log("taler database error", e); reject(new Error("database error")); }; - req.onsuccess = e => { + req.onsuccess = (e) => { req.result.onversionchange = (evt) => { console.log(`handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`); req.result.close(); @@ -1038,18 +1292,18 @@ function openDatabase(idbFactory, databaseName, databaseVersion, onVersionChange }; resolve(req.result); }; - req.onupgradeneeded = e => { + req.onupgradeneeded = (e) => { const db = req.result; - onUpgradeNeeded(db, e.oldVersion, e.newVersion); + const newVersion = e.newVersion; + if (!newVersion) { + throw Error("upgrade needed, but new version unknown"); + } + onUpgradeNeeded(db, e.oldVersion, newVersion); console.log(`DB: upgrade needed: oldVersion=${e.oldVersion}, newVersion=${e.newVersion}`); }; }); } exports.openDatabase = openDatabase; -/** - * Exception that should be thrown by client code to abort a transaction. - */ -exports.TransactionAbort = Symbol("transaction_abort"); class Database { constructor(db) { this.db = db; @@ -1058,7 +1312,7 @@ class Database { idbFactory.deleteDatabase(dbName); } exportDatabase() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const db = this.db; const dump = { name: db.name, @@ -1113,7 +1367,7 @@ class Database { }); } get(store, key) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const tx = this.db.transaction([store.name], "readonly"); const req = tx.objectStore(store.name).get(key); const v = yield requestToPromise(req); @@ -1122,19 +1376,16 @@ class Database { }); } getIndexed(index, key) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const tx = this.db.transaction([index.storeName], "readonly"); - const req = tx - .objectStore(index.storeName) - .index(index.indexName) - .get(key); + const req = tx.objectStore(index.storeName).index(index.indexName).get(key); const v = yield requestToPromise(req); yield transactionToPromise(tx); return v; }); } put(store, value, key) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const tx = this.db.transaction([store.name], "readwrite"); const req = tx.objectStore(store.name).put(value, key); const v = yield requestToPromise(req); @@ -1143,7 +1394,7 @@ class Database { }); } mutate(store, key, f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const tx = this.db.transaction([store.name], "readwrite"); const req = tx.objectStore(store.name).openCursor(key); yield applyMutation(req, f); @@ -1164,12 +1415,12 @@ class Database { return new ResultStream(req); } runWithReadTransaction(stores, f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return runWithTransaction(this.db, stores, f, "readonly"); }); } runWithWriteTransaction(stores, f) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return runWithTransaction(this.db, stores, f, "readwrite"); }); } @@ -1179,11 +1430,11 @@ exports.Database = Database; }); unwrapExports(query); -var query_1 = query.Store; -var query_2 = query.TransactionHandle; -var query_3 = query.Index; -var query_4 = query.openDatabase; -var query_5 = query.TransactionAbort; +var query_1 = query.TransactionAbort; +var query_2 = query.Store; +var query_3 = query.TransactionHandle; +var query_4 = query.Index; +var query_5 = query.openDatabase; var query_6 = query.Database; var time = createCommonjsModule(function (module, exports) { @@ -1210,9 +1461,14 @@ Object.defineProperty(exports, "__esModule", { value: true }); class Timestamp { } exports.Timestamp = Timestamp; +let timeshift = 0; +function setDangerousTimetravel(dt) { + timeshift = dt; +} +exports.setDangerousTimetravel = setDangerousTimetravel; function getTimestampNow() { return { - t_ms: new Date().getTime(), + t_ms: new Date().getTime() + timeshift, }; } exports.getTimestampNow = getTimestampNow; @@ -1239,6 +1495,19 @@ function timestampMin(t1, t2) { return { t_ms: Math.min(t1.t_ms, t2.t_ms) }; } exports.timestampMin = timestampMin; +/** + * Truncate a timestamp so that that it represents a multiple + * of seconds. The timestamp is always rounded down. + */ +function timestampTruncateToSecond(t1) { + if (t1.t_ms === "never") { + return { t_ms: "never" }; + } + return { + t_ms: Math.floor(t1.t_ms / 1000) * 1000, + }; +} +exports.timestampTruncateToSecond = timestampTruncateToSecond; function durationMin(d1, d2) { if (d1.d_ms === "forever") { return { d_ms: d2.d_ms }; @@ -1302,6 +1571,16 @@ function timestampDifference(t1, t2) { return { d_ms: Math.abs(t1.t_ms - t2.t_ms) }; } exports.timestampDifference = timestampDifference; +function timestampIsBetween(t, start, end) { + if (timestampCmp(t, start) < 0) { + return false; + } + if (timestampCmp(t, end) > 0) { + return false; + } + return true; +} +exports.timestampIsBetween = timestampIsBetween; exports.codecForTimestamp = { decode(x, c) { const t_ms = x.t_ms; @@ -1337,17 +1616,20 @@ exports.codecForDuration = { unwrapExports(time); var time_1 = time.Timestamp; -var time_2 = time.getTimestampNow; -var time_3 = time.getDurationRemaining; -var time_4 = time.timestampMin; -var time_5 = time.durationMin; -var time_6 = time.timestampCmp; -var time_7 = time.timestampAddDuration; -var time_8 = time.timestampSubtractDuraction; -var time_9 = time.stringifyTimestamp; -var time_10 = time.timestampDifference; -var time_11 = time.codecForTimestamp; -var time_12 = time.codecForDuration; +var time_2 = time.setDangerousTimetravel; +var time_3 = time.getTimestampNow; +var time_4 = time.getDurationRemaining; +var time_5 = time.timestampMin; +var time_6 = time.timestampTruncateToSecond; +var time_7 = time.durationMin; +var time_8 = time.timestampCmp; +var time_9 = time.timestampAddDuration; +var time_10 = time.timestampSubtractDuraction; +var time_11 = time.stringifyTimestamp; +var time_12 = time.timestampDifference; +var time_13 = time.timestampIsBetween; +var time_14 = time.codecForTimestamp; +var time_15 = time.codecForDuration; var dbTypes = createCommonjsModule(function (module, exports) { /* @@ -1454,26 +1736,6 @@ var DenominationStatus; */ DenominationStatus[DenominationStatus["VerifiedBad"] = 2] = "VerifiedBad"; })(DenominationStatus = exports.DenominationStatus || (exports.DenominationStatus = {})); -/** - * Status of a coin. - */ -var CoinStatus; -(function (CoinStatus) { - /** - * Withdrawn and never shown to anybody. - */ - CoinStatus["Fresh"] = "fresh"; - /** - * A coin that has been spent and refreshed. - */ - CoinStatus["Dormant"] = "dormant"; -})(CoinStatus = exports.CoinStatus || (exports.CoinStatus = {})); -var CoinSource; -(function (CoinSource) { - CoinSource["Withdraw"] = "withdraw"; - CoinSource["Refresh"] = "refresh"; - CoinSource["Tip"] = "tip"; -})(CoinSource = exports.CoinSource || (exports.CoinSource = {})); /** * Record to keep track of data imported into the wallet. */ @@ -1484,6 +1746,7 @@ exports.WalletImportRecord = WalletImportRecord; /** * The stores and indices for the wallet database. */ +// eslint-disable-next-line @typescript-eslint/no-namespace var Stores; (function (Stores) { class ExchangesStore extends query.Store { @@ -1496,7 +1759,7 @@ var Stores; super("coins", { keyPath: "coinPub" }); this.exchangeBaseUrlIndex = new query.Index(this, "exchangeBaseUrl", "exchangeBaseUrl"); this.denomPubIndex = new query.Index(this, "denomPubIndex", "denomPub"); - this.byWithdrawalWithIdx = new query.Index(this, "planchetsByWithdrawalWithIdxIndex", ["withdrawSessionId", "coinIndex"]); + this.denomPubHashIndex = new query.Index(this, "denomPubHashIndex", "denomPubHash"); } } class ProposalsStore extends query.Store { @@ -1554,9 +1817,9 @@ var Stores; super("senderWires", { keyPath: "paytoUri" }); } } - class WithdrawalSessionsStore extends query.Store { + class WithdrawalGroupsStore extends query.Store { constructor() { - super("withdrawals", { keyPath: "withdrawSessionId" }); + super("withdrawals", { keyPath: "withdrawalGroupId" }); } } class RefundEventsStore extends query.Store { @@ -1601,11 +1864,14 @@ var Stores; Stores.refreshGroups = new query.Store("refreshGroups", { keyPath: "refreshGroupId", }); + Stores.recoupGroups = new query.Store("recoupGroups", { + keyPath: "recoupGroupId", + }); Stores.reserves = new ReservesStore(); Stores.purchases = new PurchasesStore(); Stores.tips = new TipsStore(); Stores.senderWires = new SenderWiresStore(); - Stores.withdrawalSession = new WithdrawalSessionsStore(); + Stores.withdrawalGroups = new WithdrawalGroupsStore(); Stores.bankWithdrawUris = new BankWithdrawUrisStore(); Stores.refundEvents = new RefundEventsStore(); Stores.payEvents = new PayEventsStore(); @@ -1622,10 +1888,8 @@ var dbTypes_1 = dbTypes.ReserveRecordStatus; var dbTypes_2 = dbTypes.updateRetryInfoTimeout; var dbTypes_3 = dbTypes.initRetryInfo; var dbTypes_4 = dbTypes.DenominationStatus; -var dbTypes_5 = dbTypes.CoinStatus; -var dbTypes_6 = dbTypes.CoinSource; -var dbTypes_7 = dbTypes.WalletImportRecord; -var dbTypes_8 = dbTypes.Stores; +var dbTypes_5 = dbTypes.WalletImportRecord; +var dbTypes_6 = dbTypes.Stores; var talerTypes = createCommonjsModule(function (module, exports) { /* @@ -1727,9 +1991,15 @@ exports.TipResponse = TipResponse; * Element of the payback list that the * exchange gives us in /keys. */ -class Payback { +class Recoup { } -exports.Payback = Payback; +exports.Recoup = Recoup; +/** + * Structure of one exchange signing key in the /keys response. + */ +class ExchangeSignKeyJson { +} +exports.ExchangeSignKeyJson = ExchangeSignKeyJson; /** * Structure that the exchange gives us in /keys. */ @@ -1772,7 +2042,10 @@ exports.WithdrawOperationStatusResponse = WithdrawOperationStatusResponse; class TipPickupGetResponse { } exports.TipPickupGetResponse = TipPickupGetResponse; -exports.codecForDenomination = () => codec.typecheckedCodec(codec.makeCodecForObject() +class WithdrawResponse { +} +exports.WithdrawResponse = WithdrawResponse; +exports.codecForDenomination = () => codec.makeCodecForObject() .property("value", codec.codecForString) .property("denom_pub", codec.codecForString) .property("fee_withdraw", codec.codecForString) @@ -1784,26 +2057,26 @@ exports.codecForDenomination = () => codec.typecheckedCodec(codec.makeCodecForOb .property("stamp_expire_legal", time.codecForTimestamp) .property("stamp_expire_deposit", time.codecForTimestamp) .property("master_sig", codec.codecForString) - .build("Denomination")); -exports.codecForAuditorDenomSig = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("Denomination"); +exports.codecForAuditorDenomSig = () => codec.makeCodecForObject() .property("denom_pub_h", codec.codecForString) .property("auditor_sig", codec.codecForString) - .build("AuditorDenomSig")); -exports.codecForAuditor = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("AuditorDenomSig"); +exports.codecForAuditor = () => codec.makeCodecForObject() .property("auditor_pub", codec.codecForString) .property("auditor_url", codec.codecForString) .property("denomination_keys", codec.makeCodecForList(exports.codecForAuditorDenomSig())) - .build("Auditor")); -exports.codecForExchangeHandle = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("Auditor"); +exports.codecForExchangeHandle = () => codec.makeCodecForObject() .property("master_pub", codec.codecForString) .property("url", codec.codecForString) - .build("ExchangeHandle")); -exports.codecForAuditorHandle = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("ExchangeHandle"); +exports.codecForAuditorHandle = () => codec.makeCodecForObject() .property("name", codec.codecForString) .property("master_pub", codec.codecForString) .property("url", codec.codecForString) - .build("AuditorHandle")); -exports.codecForContractTerms = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("AuditorHandle"); +exports.codecForContractTerms = () => codec.makeCodecForObject() .property("order_id", codec.codecForString) .property("fulfillment_url", codec.codecForString) .property("merchant_base_url", codec.codecForString) @@ -1826,66 +2099,73 @@ exports.codecForContractTerms = () => codec.typecheckedCodec(codec.makeCodecForO .property("exchanges", codec.makeCodecForList(exports.codecForExchangeHandle())) .property("products", codec.makeCodecOptional(codec.makeCodecForList(codec.codecForAny))) .property("extra", codec.codecForAny) - .build("ContractTerms")); -exports.codecForMerchantRefundPermission = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("ContractTerms"); +exports.codecForMerchantRefundPermission = () => codec.makeCodecForObject() .property("refund_amount", codec.codecForString) .property("refund_fee", codec.codecForString) .property("coin_pub", codec.codecForString) .property("rtransaction_id", codec.codecForNumber) .property("merchant_sig", codec.codecForString) - .build("MerchantRefundPermission")); -exports.codecForMerchantRefundResponse = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("MerchantRefundPermission"); +exports.codecForMerchantRefundResponse = () => codec.makeCodecForObject() .property("merchant_pub", codec.codecForString) .property("h_contract_terms", codec.codecForString) .property("refund_permissions", codec.makeCodecForList(exports.codecForMerchantRefundPermission())) - .build("MerchantRefundResponse")); -exports.codecForReserveSigSingleton = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("MerchantRefundResponse"); +exports.codecForReserveSigSingleton = () => codec.makeCodecForObject() .property("reserve_sig", codec.codecForString) - .build("ReserveSigSingleton")); -exports.codecForTipResponse = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("ReserveSigSingleton"); +exports.codecForTipResponse = () => codec.makeCodecForObject() .property("reserve_pub", codec.codecForString) .property("reserve_sigs", codec.makeCodecForList(exports.codecForReserveSigSingleton())) - .build("TipResponse")); -exports.codecForPayback = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("TipResponse"); +exports.codecForRecoup = () => codec.makeCodecForObject() .property("h_denom_pub", codec.codecForString) - .build("Payback")); -exports.codecForExchangeKeysJson = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("Recoup"); +exports.codecForExchangeSigningKey = () => codec.makeCodecForObject() + .property("key", codec.codecForString) + .property("master_sig", codec.codecForString) + .property("stamp_end", time.codecForTimestamp) + .property("stamp_start", time.codecForTimestamp) + .property("stamp_expire", time.codecForTimestamp) + .build("ExchangeSignKeyJson"); +exports.codecForExchangeKeysJson = () => codec.makeCodecForObject() .property("denoms", codec.makeCodecForList(exports.codecForDenomination())) .property("master_public_key", codec.codecForString) .property("auditors", codec.makeCodecForList(exports.codecForAuditor())) .property("list_issue_date", time.codecForTimestamp) - .property("payback", codec.makeCodecOptional(codec.makeCodecForList(exports.codecForPayback()))) - .property("signkeys", codec.codecForAny) + .property("recoup", codec.makeCodecOptional(codec.makeCodecForList(exports.codecForRecoup()))) + .property("signkeys", codec.makeCodecForList(exports.codecForExchangeSigningKey())) .property("version", codec.codecForString) - .build("KeysJson")); -exports.codecForWireFeesJson = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("KeysJson"); +exports.codecForWireFeesJson = () => codec.makeCodecForObject() .property("wire_fee", codec.codecForString) .property("closing_fee", codec.codecForString) .property("sig", codec.codecForString) .property("start_date", time.codecForTimestamp) .property("end_date", time.codecForTimestamp) - .build("WireFeesJson")); -exports.codecForAccountInfo = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("WireFeesJson"); +exports.codecForAccountInfo = () => codec.makeCodecForObject() .property("payto_uri", codec.codecForString) .property("master_sig", codec.codecForString) - .build("AccountInfo")); -exports.codecForExchangeWireJson = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("AccountInfo"); +exports.codecForExchangeWireJson = () => codec.makeCodecForObject() .property("accounts", codec.makeCodecForList(exports.codecForAccountInfo())) .property("fees", codec.makeCodecForMap(codec.makeCodecForList(exports.codecForWireFeesJson()))) - .build("ExchangeWireJson")); -exports.codecForProposal = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("ExchangeWireJson"); +exports.codecForProposal = () => codec.makeCodecForObject() .property("contract_terms", codec.codecForAny) .property("sig", codec.codecForString) - .build("Proposal")); -exports.codecForCheckPaymentResponse = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("Proposal"); +exports.codecForCheckPaymentResponse = () => codec.makeCodecForObject() .property("paid", codec.codecForBoolean) .property("refunded", codec.makeCodecOptional(codec.codecForBoolean)) .property("refunded_amount", codec.makeCodecOptional(codec.codecForString)) .property("contract_terms", codec.makeCodecOptional(codec.codecForAny)) .property("taler_pay_uri", codec.makeCodecOptional(codec.codecForString)) .property("contract_url", codec.makeCodecOptional(codec.codecForString)) - .build("CheckPaymentResponse")); -exports.codecForWithdrawOperationStatusResponse = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("CheckPaymentResponse"); +exports.codecForWithdrawOperationStatusResponse = () => codec.makeCodecForObject() .property("selection_done", codec.codecForBoolean) .property("transfer_done", codec.codecForBoolean) .property("amount", codec.codecForString) @@ -1893,22 +2173,22 @@ exports.codecForWithdrawOperationStatusResponse = () => codec.typecheckedCodec(c .property("suggested_exchange", codec.makeCodecOptional(codec.codecForString)) .property("confirm_transfer_url", codec.makeCodecOptional(codec.codecForString)) .property("wire_types", codec.makeCodecForList(codec.codecForString)) - .build("WithdrawOperationStatusResponse")); -exports.codecForTipPickupGetResponse = () => codec.typecheckedCodec(codec.makeCodecForObject() + .build("WithdrawOperationStatusResponse"); +exports.codecForTipPickupGetResponse = () => codec.makeCodecForObject() .property("extra", codec.codecForAny) .property("amount", codec.codecForString) .property("amount_left", codec.codecForString) .property("exchange_url", codec.codecForString) .property("stamp_expire", time.codecForTimestamp) .property("stamp_created", time.codecForTimestamp) - .build("TipPickupGetResponse")); -exports.codecForRecoupConfirmation = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("reserve_pub", codec.codecForString) - .property("amount", codec.codecForString) - .property("timestamp", time.codecForTimestamp) - .property("exchange_sig", codec.codecForString) - .property("exchange_pub", codec.codecForString) - .build("RecoupConfirmation")); + .build("TipPickupGetResponse"); +exports.codecForRecoupConfirmation = () => codec.makeCodecForObject() + .property("reserve_pub", codec.makeCodecOptional(codec.codecForString)) + .property("old_coin_pub", codec.makeCodecOptional(codec.codecForString)) + .build("RecoupConfirmation"); +exports.codecForWithdrawResponse = () => codec.makeCodecForObject() + .property("ev_sig", codec.codecForString) + .build("WithdrawResponse"); }); @@ -1924,35 +2204,39 @@ var talerTypes_8 = talerTypes.MerchantRefundPermission; var talerTypes_9 = talerTypes.MerchantRefundResponse; var talerTypes_10 = talerTypes.ReserveSigSingleton; var talerTypes_11 = talerTypes.TipResponse; -var talerTypes_12 = talerTypes.Payback; -var talerTypes_13 = talerTypes.ExchangeKeysJson; -var talerTypes_14 = talerTypes.WireFeesJson; -var talerTypes_15 = talerTypes.AccountInfo; -var talerTypes_16 = talerTypes.ExchangeWireJson; -var talerTypes_17 = talerTypes.Proposal; -var talerTypes_18 = talerTypes.CheckPaymentResponse; -var talerTypes_19 = talerTypes.WithdrawOperationStatusResponse; -var talerTypes_20 = talerTypes.TipPickupGetResponse; -var talerTypes_21 = talerTypes.codecForDenomination; -var talerTypes_22 = talerTypes.codecForAuditorDenomSig; -var talerTypes_23 = talerTypes.codecForAuditor; -var talerTypes_24 = talerTypes.codecForExchangeHandle; -var talerTypes_25 = talerTypes.codecForAuditorHandle; -var talerTypes_26 = talerTypes.codecForContractTerms; -var talerTypes_27 = talerTypes.codecForMerchantRefundPermission; -var talerTypes_28 = talerTypes.codecForMerchantRefundResponse; -var talerTypes_29 = talerTypes.codecForReserveSigSingleton; -var talerTypes_30 = talerTypes.codecForTipResponse; -var talerTypes_31 = talerTypes.codecForPayback; -var talerTypes_32 = talerTypes.codecForExchangeKeysJson; -var talerTypes_33 = talerTypes.codecForWireFeesJson; -var talerTypes_34 = talerTypes.codecForAccountInfo; -var talerTypes_35 = talerTypes.codecForExchangeWireJson; -var talerTypes_36 = talerTypes.codecForProposal; -var talerTypes_37 = talerTypes.codecForCheckPaymentResponse; -var talerTypes_38 = talerTypes.codecForWithdrawOperationStatusResponse; -var talerTypes_39 = talerTypes.codecForTipPickupGetResponse; -var talerTypes_40 = talerTypes.codecForRecoupConfirmation; +var talerTypes_12 = talerTypes.Recoup; +var talerTypes_13 = talerTypes.ExchangeSignKeyJson; +var talerTypes_14 = talerTypes.ExchangeKeysJson; +var talerTypes_15 = talerTypes.WireFeesJson; +var talerTypes_16 = talerTypes.AccountInfo; +var talerTypes_17 = talerTypes.ExchangeWireJson; +var talerTypes_18 = talerTypes.Proposal; +var talerTypes_19 = talerTypes.CheckPaymentResponse; +var talerTypes_20 = talerTypes.WithdrawOperationStatusResponse; +var talerTypes_21 = talerTypes.TipPickupGetResponse; +var talerTypes_22 = talerTypes.WithdrawResponse; +var talerTypes_23 = talerTypes.codecForDenomination; +var talerTypes_24 = talerTypes.codecForAuditorDenomSig; +var talerTypes_25 = talerTypes.codecForAuditor; +var talerTypes_26 = talerTypes.codecForExchangeHandle; +var talerTypes_27 = talerTypes.codecForAuditorHandle; +var talerTypes_28 = talerTypes.codecForContractTerms; +var talerTypes_29 = talerTypes.codecForMerchantRefundPermission; +var talerTypes_30 = talerTypes.codecForMerchantRefundResponse; +var talerTypes_31 = talerTypes.codecForReserveSigSingleton; +var talerTypes_32 = talerTypes.codecForTipResponse; +var talerTypes_33 = talerTypes.codecForRecoup; +var talerTypes_34 = talerTypes.codecForExchangeSigningKey; +var talerTypes_35 = talerTypes.codecForExchangeKeysJson; +var talerTypes_36 = talerTypes.codecForWireFeesJson; +var talerTypes_37 = talerTypes.codecForAccountInfo; +var talerTypes_38 = talerTypes.codecForExchangeWireJson; +var talerTypes_39 = talerTypes.codecForProposal; +var talerTypes_40 = talerTypes.codecForCheckPaymentResponse; +var talerTypes_41 = talerTypes.codecForWithdrawOperationStatusResponse; +var talerTypes_42 = talerTypes.codecForTipPickupGetResponse; +var talerTypes_43 = talerTypes.codecForRecoupConfirmation; +var talerTypes_44 = talerTypes.codecForWithdrawResponse; var taleruri = createCommonjsModule(function (module, exports) { /* @@ -2149,6 +2433,49 @@ var taleruri_4 = taleruri.parsePayUri; var taleruri_5 = taleruri.parseTipUri; var taleruri_6 = taleruri.parseRefundUri; +var compat = createCommonjsModule(function (module, exports) { +/* + This file is part of TALER + (C) 2017 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Compatibility helpers needed for browsers that don't implement + * WebExtension APIs consistently. + */ +function isFirefox() { + const rt = chrome.runtime; + if (typeof rt.getBrowserInfo === "function") { + return true; + } + return false; +} +exports.isFirefox = isFirefox; +/** + * Check if we are running under nodejs. + */ +function isNode() { + return typeof process !== "undefined" && process.release.name === "node"; +} +exports.isNode = isNode; + +}); + +unwrapExports(compat); +var compat_1 = compat.isFirefox; +var compat_2 = compat.isNode; + var logging = createCommonjsModule(function (module, exports) { /* This file is part of TALER @@ -2166,15 +2493,58 @@ var logging = createCommonjsModule(function (module, exports) { TALER; see the file COPYING. If not, see */ Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Imports. + */ + +function writeNodeLog(message, tag, level, args) { + process.stderr.write(`${new Date().toISOString()} ${tag} ${level} `); + process.stderr.write(message); + if (args.length != 0) { + process.stderr.write(" "); + process.stderr.write(JSON.stringify(args, undefined, 2)); + } + process.stderr.write("\n"); +} +/** + * Logger that writes to stderr when running under node, + * and uses the corresponding console.* method to log in the browser. + */ class Logger { constructor(tag) { this.tag = tag; } info(message, ...args) { - console.log(`${new Date().toISOString()} ${this.tag} INFO ` + message, ...args); + if (compat.isNode()) { + writeNodeLog(message, this.tag, "INFO", args); + } + else { + console.info(`${new Date().toISOString()} ${this.tag} INFO ` + message, ...args); + } + } + warn(message, ...args) { + if (compat.isNode()) { + writeNodeLog(message, this.tag, "WARN", args); + } + else { + console.warn(`${new Date().toISOString()} ${this.tag} INFO ` + message, ...args); + } + } + error(message, ...args) { + if (compat.isNode()) { + writeNodeLog(message, this.tag, "ERROR", args); + } + else { + console.info(`${new Date().toISOString()} ${this.tag} ERROR ` + message, ...args); + } } trace(message, ...args) { - console.log(`${new Date().toISOString()} ${this.tag} TRACE ` + message, ...args); + if (compat.isNode()) { + writeNodeLog(message, this.tag, "TRACE", args); + } + else { + console.info(`${new Date().toISOString()} ${this.tag} TRACE ` + message, ...args); + } } } exports.Logger = Logger; @@ -2200,15 +2570,9 @@ var helpers = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); + +const Amounts = tslib_1.__importStar(amounts); /** * Show an amount in a form suitable for the user. * FIXME: In the future, this should consider currency-specific @@ -2279,10 +2643,15 @@ function deepEquals(x, y) { return false; } const p = Object.keys(x); - return Object.keys(y).every((i) => p.indexOf(i) !== -1) && - p.every((i) => deepEquals(x[i], y[i])); + return (Object.keys(y).every((i) => p.indexOf(i) !== -1) && + p.every((i) => deepEquals(x[i], y[i]))); } exports.deepEquals = deepEquals; +function deepCopy(x) { + // FIXME: this has many issues ... + return JSON.parse(JSON.stringify(x)); +} +exports.deepCopy = deepCopy; /** * Map from a collection to a list or results and then * concatenate the results. @@ -2303,8 +2672,8 @@ function hash(val) { h = (h * 33) ^ str.charCodeAt(--i); } /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed - * integers. Since we want the results to be always positive, convert the - * signed int to an unsigned by doing an unsigned bitshift. */ + * integers. Since we want the results to be always positive, convert the + * signed int to an unsigned by doing an unsigned bitshift. */ return h >>> 0; } exports.hash = hash; @@ -2321,16 +2690,6 @@ function strcmp(s1, s2) { return 0; } exports.strcmp = strcmp; -/** - * Run a function and return its result. - * - * Used as a nicer-looking way to do immediately invoked function - * expressions (IFFEs). - */ -function runBlock(f) { - return f(); -} -exports.runBlock = runBlock; }); @@ -2339,10 +2698,10 @@ var helpers_1 = helpers.amountToPretty; var helpers_2 = helpers.canonicalizeBaseUrl; var helpers_3 = helpers.canonicalJson; var helpers_4 = helpers.deepEquals; -var helpers_5 = helpers.flatMap; -var helpers_6 = helpers.hash; -var helpers_7 = helpers.strcmp; -var helpers_8 = helpers.runBlock; +var helpers_5 = helpers.deepCopy; +var helpers_6 = helpers.flatMap; +var helpers_7 = helpers.hash; +var helpers_8 = helpers.strcmp; var payto = createCommonjsModule(function (module, exports) { /* @@ -2392,19 +2751,9 @@ unwrapExports(payto); var payto_1 = payto.parsePaytoUri; var errors = createCommonjsModule(function (module, exports) { -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); /* This file is part of GNU Taler - (C) 2019 GNUnet e.V. + (C) 2019-2020 Taler Systems SA GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -2417,13 +2766,16 @@ Object.defineProperty(exports, "__esModule", { value: true }); You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ +Object.defineProperty(exports, "__esModule", { value: true }); + /** * This exception is there to let the caller know that an error happened, * but the error has already been reported by writing it to the database. */ class OperationFailedAndReportedError extends Error { - constructor(message) { - super(message); + constructor(operationError) { + super(operationError.message); + this.operationError = operationError; // Set the prototype explicitly. Object.setPrototypeOf(this, OperationFailedAndReportedError.prototype); } @@ -2434,21 +2786,78 @@ exports.OperationFailedAndReportedError = OperationFailedAndReportedError; * responsible for recording the failure in the database. */ class OperationFailedError extends Error { - constructor(message, err) { - super(message); - this.err = err; + constructor(operationError) { + super(operationError.message); + this.operationError = operationError; // Set the prototype explicitly. Object.setPrototypeOf(this, OperationFailedError.prototype); } } exports.OperationFailedError = OperationFailedError; +/** + * Process an HTTP response that we expect to contain Taler-specific JSON. + * + * Depending on the status code, we throw an exception. This function + * will try to extract Taler-specific error information from the HTTP response + * if possible. + */ +function scrutinizeTalerJsonResponse(resp, codec) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + // FIXME: We should distinguish between different types of error status + // to react differently (throttle, report permanent failure) + // FIXME: Make sure that when we receive an error message, + // it looks like a Taler error message + if (resp.status !== 200) { + let exc = undefined; + try { + const errorJson = yield resp.json(); + const m = `received error response (status ${resp.status})`; + exc = new OperationFailedError({ + type: "protocol", + message: m, + details: { + httpStatusCode: resp.status, + errorResponse: errorJson, + }, + }); + } + catch (e) { + const m = "could not parse response JSON"; + exc = new OperationFailedError({ + type: "network", + message: m, + details: { + status: resp.status, + }, + }); + } + throw exc; + } + let json; + try { + json = yield resp.json(); + } + catch (e) { + const m = "could not parse response JSON"; + throw new OperationFailedError({ + type: "network", + message: m, + details: { + status: resp.status, + }, + }); + } + return codec.decode(json); + }); +} +exports.scrutinizeTalerJsonResponse = scrutinizeTalerJsonResponse; /** * Run an operation and call the onOpError callback * when there was an exception or operation error that must be reported. * The cause will be re-thrown to the caller. */ function guardOperationException(op, onOpError) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return yield op(); } @@ -2458,27 +2867,29 @@ function guardOperationException(op, onOpError) { throw e; } if (e instanceof OperationFailedError) { - yield onOpError(e.err); - throw new OperationFailedAndReportedError(e.message); + yield onOpError(e.operationError); + throw new OperationFailedAndReportedError(e.operationError); } if (e instanceof Error) { console.log("guard: caught Error"); - yield onOpError({ + const opErr = { type: "exception", message: e.message, details: {}, - }); - throw new OperationFailedAndReportedError(e.message); + }; + yield onOpError(opErr); + throw new OperationFailedAndReportedError(opErr); } console.log("guard: caught something else"); - yield onOpError({ + const opErr = { type: "exception", message: "non-error exception thrown", details: { value: e.toString(), }, - }); - throw new OperationFailedAndReportedError(e.message); + }; + yield onOpError(opErr); + throw new OperationFailedAndReportedError(opErr); } }); } @@ -2489,7 +2900,8 @@ exports.guardOperationException = guardOperationException; unwrapExports(errors); var errors_1 = errors.OperationFailedAndReportedError; var errors_2 = errors.OperationFailedError; -var errors_3 = errors.guardOperationException; +var errors_3 = errors.scrutinizeTalerJsonResponse; +var errors_4 = errors.guardOperationException; var versions = createCommonjsModule(function (module, exports) { /* @@ -2514,7 +2926,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); * * Uses libtool's current:revision:age versioning. */ -exports.WALLET_EXCHANGE_PROTOCOL_VERSION = "6"; +exports.WALLET_EXCHANGE_PROTOCOL_VERSION = "7:0:0"; /** * Cache breaker that is appended to queries such as /keys and /wire * to break through caching, if it has been accidentally/badly configured @@ -2530,7 +2942,64 @@ unwrapExports(versions); var versions_1 = versions.WALLET_EXCHANGE_PROTOCOL_VERSION; var versions_2 = versions.WALLET_CACHE_BREAKER_CLIENT_VERSION; -var exchanges = createCommonjsModule(function (module, exports) { +var libtoolVersion = createCommonjsModule(function (module, exports) { +/* + This file is part of TALER + (C) 2017 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Compare two libtool-style version strings. + */ +function compare(me, other) { + const meVer = parseVersion(me); + const otherVer = parseVersion(other); + if (!(meVer && otherVer)) { + return undefined; + } + const compatible = meVer.current - meVer.age <= otherVer.current && + meVer.current >= otherVer.current - otherVer.age; + const currentCmp = Math.sign(meVer.current - otherVer.current); + return { compatible, currentCmp }; +} +exports.compare = compare; +function parseVersion(v) { + const [currentStr, revisionStr, ageStr, ...rest] = v.split(":"); + if (rest.length !== 0) { + return undefined; + } + const current = Number.parseInt(currentStr); + const revision = Number.parseInt(revisionStr); + const age = Number.parseInt(ageStr); + if (Number.isNaN(current)) { + return undefined; + } + if (Number.isNaN(revision)) { + return undefined; + } + if (Number.isNaN(age)) { + return undefined; + } + return { current, revision, age }; +} + +}); + +unwrapExports(libtoolVersion); +var libtoolVersion_1 = libtoolVersion.compare; + +var assertUnreachable_1 = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler (C) 2019 GNUnet e.V. @@ -2546,5022 +3015,3123 @@ var exchanges = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); +function assertUnreachable(x) { + throw new Error("Didn't expect to get here"); +} +exports.assertUnreachable = assertUnreachable; +}); +unwrapExports(assertUnreachable_1); +var assertUnreachable_2 = assertUnreachable_1.assertUnreachable; -const Amounts = __importStar(amounts); - - - - -function denominationRecordFromKeys(ws, exchangeBaseUrl, denomIn) { - return __awaiter(this, void 0, void 0, function* () { - const denomPubHash = yield ws.cryptoApi.hashDenomPub(denomIn.denom_pub); - const d = { - denomPub: denomIn.denom_pub, - denomPubHash, - exchangeBaseUrl, - feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit), - feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh), - feeRefund: Amounts.parseOrThrow(denomIn.fee_refund), - feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw), - isOffered: true, - masterSig: denomIn.master_sig, - stampExpireDeposit: denomIn.stamp_expire_deposit, - stampExpireLegal: denomIn.stamp_expire_legal, - stampExpireWithdraw: denomIn.stamp_expire_withdraw, - stampStart: denomIn.stamp_start, - status: dbTypes.DenominationStatus.Unverified, - value: Amounts.parseOrThrow(denomIn.value), - }; - return d; - }); +var naclFast = createCommonjsModule(function (module, exports) { +// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. +// TypeScript port in 2019 by Florian Dold. +// Public domain. +// +// Implementation derived from TweetNaCl version 20140427. +// See for details: http://tweetnacl.cr.yp.to/ +Object.defineProperty(exports, "__esModule", { value: true }); +const gf = function (init = []) { + const r = new Float64Array(16); + if (init) + for (let i = 0; i < init.length; i++) + r[i] = init[i]; + return r; +}; +// Pluggable, initialized in high-level API below. +let randombytes = function (x, n) { + throw new Error("no PRNG"); +}; +const _9 = new Uint8Array(32); +_9[0] = 9; +// prettier-ignore +const gf0 = gf(); +const gf1 = gf([1]); +const _121665 = gf([0xdb41, 1]); +const D = gf([ + 0x78a3, + 0x1359, + 0x4dca, + 0x75eb, + 0xd8ab, + 0x4141, + 0x0a4d, + 0x0070, + 0xe898, + 0x7779, + 0x4079, + 0x8cc7, + 0xfe73, + 0x2b6f, + 0x6cee, + 0x5203, +]); +const D2 = gf([ + 0xf159, + 0x26b2, + 0x9b94, + 0xebd6, + 0xb156, + 0x8283, + 0x149a, + 0x00e0, + 0xd130, + 0xeef3, + 0x80f2, + 0x198e, + 0xfce7, + 0x56df, + 0xd9dc, + 0x2406, +]); +const X = gf([ + 0xd51a, + 0x8f25, + 0x2d60, + 0xc956, + 0xa7b2, + 0x9525, + 0xc760, + 0x692c, + 0xdc5c, + 0xfdd6, + 0xe231, + 0xc0a4, + 0x53fe, + 0xcd6e, + 0x36d3, + 0x2169, +]); +const Y = gf([ + 0x6658, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, + 0x6666, +]); +const I = gf([ + 0xa0b0, + 0x4a0e, + 0x1b27, + 0xc4ee, + 0xe478, + 0xad2f, + 0x1806, + 0x2f43, + 0xd7a7, + 0x3dfb, + 0x0099, + 0x2b4d, + 0xdf0b, + 0x4fc1, + 0x2480, + 0x2b83, +]); +function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i + 1] = (h >> 16) & 0xff; + x[i + 2] = (h >> 8) & 0xff; + x[i + 3] = h & 0xff; + x[i + 4] = (l >> 24) & 0xff; + x[i + 5] = (l >> 16) & 0xff; + x[i + 6] = (l >> 8) & 0xff; + x[i + 7] = l & 0xff; } -function setExchangeError(ws, baseUrl, err) { - return __awaiter(this, void 0, void 0, function* () { - const mut = (exchange) => { - exchange.lastError = err; - return exchange; - }; - yield ws.db.mutate(dbTypes.Stores.exchanges, baseUrl, mut); - }); +function vn(x, xi, y, yi, n) { + let i, d = 0; + for (i = 0; i < n; i++) + d |= x[xi + i] ^ y[yi + i]; + return (1 & ((d - 1) >>> 8)) - 1; } -/** - * Fetch the exchange's /keys and update our database accordingly. - * - * Exceptions thrown in this method must be caught and reported - * in the pending operations. - */ -function updateExchangeWithKeys(ws, baseUrl) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - const existingExchangeRecord = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); - if (((_a = existingExchangeRecord) === null || _a === void 0 ? void 0 : _a.updateStatus) != "fetch-keys" /* FetchKeys */) { - return; - } - const keysUrl = new URL("keys", baseUrl); - keysUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); - let keysResp; - try { - const r = yield ws.http.get(keysUrl.href); - if (r.status !== 200) { - throw Error(`unexpected status for keys: ${r.status}`); - } - keysResp = yield r.json(); - } - catch (e) { - const m = `Fetching keys failed: ${e.message}`; - yield setExchangeError(ws, baseUrl, { - type: "network", - details: { - requestUrl: (_b = e.config) === null || _b === void 0 ? void 0 : _b.url, - }, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); - } - let exchangeKeysJson; - try { - exchangeKeysJson = talerTypes.codecForExchangeKeysJson().decode(keysResp); - } - catch (e) { - const m = `Parsing /keys response failed: ${e.message}`; - yield setExchangeError(ws, baseUrl, { - type: "protocol-violation", - details: {}, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); - } - const lastUpdateTimestamp = exchangeKeysJson.list_issue_date; - if (!lastUpdateTimestamp) { - const m = `Parsing /keys response failed: invalid list_issue_date.`; - yield setExchangeError(ws, baseUrl, { - type: "protocol-violation", - details: {}, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); - } - if (exchangeKeysJson.denoms.length === 0) { - const m = "exchange doesn't offer any denominations"; - yield setExchangeError(ws, baseUrl, { - type: "protocol-violation", - details: {}, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); - } - const protocolVersion = exchangeKeysJson.version; - if (!protocolVersion) { - const m = "outdate exchange, no version in /keys response"; - yield setExchangeError(ws, baseUrl, { - type: "protocol-violation", - details: {}, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); - } - const currency = Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value) - .currency; - const newDenominations = yield Promise.all(exchangeKeysJson.denoms.map(d => denominationRecordFromKeys(ws, baseUrl, d))); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges, dbTypes.Stores.denominations], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.exchanges, baseUrl); - if (!r) { - console.warn(`exchange ${baseUrl} no longer present`); - return; - } - if (r.details) ; - r.details = { - auditors: exchangeKeysJson.auditors, - currency: currency, - lastUpdateTime: lastUpdateTimestamp, - masterPublicKey: exchangeKeysJson.master_public_key, - protocolVersion: protocolVersion, - }; - r.updateStatus = "fetch-wire" /* FetchWire */; - r.lastError = undefined; - yield tx.put(dbTypes.Stores.exchanges, r); - for (const newDenom of newDenominations) { - const oldDenom = yield tx.get(dbTypes.Stores.denominations, [ - baseUrl, - newDenom.denomPub, - ]); - if (oldDenom) ; - else { - yield tx.put(dbTypes.Stores.denominations, newDenom); - } - } - })); - }); +function crypto_verify_32(x, xi, y, yi) { + return vn(x, xi, y, yi, 32); } -function updateExchangeFinalize(ws, exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { - const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!exchange) { - return; - } - if (exchange.updateStatus != "finalize-update" /* FinalizeUpdate */) { - return; - } - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges, dbTypes.Stores.exchangeUpdatedEvents], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!r) { - return; - } - if (r.updateStatus != "finalize-update" /* FinalizeUpdate */) { - return; - } - r.updateStatus = "finished" /* Finished */; - yield tx.put(dbTypes.Stores.exchanges, r); - const updateEvent = { - exchangeBaseUrl: exchange.baseUrl, - timestamp: time.getTimestampNow(), - }; - yield tx.put(dbTypes.Stores.exchangeUpdatedEvents, updateEvent); - })); - }); +function set25519(r, a) { + let i; + for (i = 0; i < 16; i++) + r[i] = a[i] | 0; } -function updateExchangeWithTermsOfService(ws, exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { - const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!exchange) { - return; - } - if (exchange.updateStatus != "fetch-terms" /* FetchTerms */) { - return; - } - const reqUrl = new URL("terms", exchangeBaseUrl); - reqUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); - const headers = { - Accept: "text/plain", - }; - const resp = yield ws.http.get(reqUrl.href, { headers }); - if (resp.status !== 200) { - throw Error(`/terms response has unexpected status code (${resp.status})`); - } - const tosText = yield resp.text(); - const tosEtag = resp.headers.get("etag") || undefined; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!r) { - return; - } - if (r.updateStatus != "fetch-terms" /* FetchTerms */) { - return; - } - r.termsOfServiceText = tosText; - r.termsOfServiceLastEtag = tosEtag; - r.updateStatus = "finalize-update" /* FinalizeUpdate */; - yield tx.put(dbTypes.Stores.exchanges, r); - })); - }); +function car25519(o) { + let i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c - 1 + 37 * (c - 1); } -function acceptExchangeTermsOfService(ws, exchangeBaseUrl, etag) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!r) { - return; - } - r.termsOfServiceAcceptedEtag = etag; - r.termsOfServiceAcceptedTimestamp = time.getTimestampNow(); - yield tx.put(dbTypes.Stores.exchanges, r); - })); - }); +function sel25519(p, q, b) { + let t; + const c = ~(b - 1); + for (let i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } } -exports.acceptExchangeTermsOfService = acceptExchangeTermsOfService; -/** - * Fetch wire information for an exchange and store it in the database. - * - * @param exchangeBaseUrl Exchange base URL, assumed to be already normalized. - */ -function updateExchangeWithWireInfo(ws, exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { - const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!exchange) { - return; - } - if (exchange.updateStatus != "fetch-wire" /* FetchWire */) { - return; - } - const details = exchange.details; - if (!details) { - throw Error("invalid exchange state"); - } - const reqUrl = new URL("wire", exchangeBaseUrl); - reqUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); - const resp = yield ws.http.get(reqUrl.href); - if (resp.status !== 200) { - throw Error(`/wire response has unexpected status code (${resp.status})`); - } - const wiJson = yield resp.json(); - if (!wiJson) { - throw Error("/wire response malformed"); - } - const wireInfo = talerTypes.codecForExchangeWireJson().decode(wiJson); - for (const a of wireInfo.accounts) { - console.log("validating exchange acct"); - const isValid = yield ws.cryptoApi.isValidWireAccount(a.payto_uri, a.master_sig, details.masterPublicKey); - if (!isValid) { - throw Error("exchange acct signature invalid"); - } - } - const feesForType = {}; - for (const wireMethod of Object.keys(wireInfo.fees)) { - const feeList = []; - for (const x of wireInfo.fees[wireMethod]) { - const startStamp = x.start_date; - const endStamp = x.end_date; - const fee = { - closingFee: Amounts.parseOrThrow(x.closing_fee), - endStamp, - sig: x.sig, - startStamp, - wireFee: Amounts.parseOrThrow(x.wire_fee), - }; - const isValid = yield ws.cryptoApi.isValidWireFee(wireMethod, fee, details.masterPublicKey); - if (!isValid) { - throw Error("exchange wire fee signature invalid"); - } - feeList.push(fee); - } - feesForType[wireMethod] = feeList; +function pack25519(o, n) { + let i, j, b; + const m = gf(), t = gf(); + for (i = 0; i < 16; i++) + t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; } - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!r) { - return; - } - if (r.updateStatus != "fetch-wire" /* FetchWire */) { - return; - } - r.wireInfo = { - accounts: wireInfo.accounts, - feesForType: feesForType, - }; - r.updateStatus = "fetch-terms" /* FetchTerms */; - r.lastError = undefined; - yield tx.put(dbTypes.Stores.exchanges, r); - })); - }); + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1 - b); + } + for (i = 0; i < 16; i++) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } } -function updateExchangeFromUrl(ws, baseUrl, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => setExchangeError(ws, baseUrl, e); - return yield errors.guardOperationException(() => updateExchangeFromUrlImpl(ws, baseUrl, forceNow), onOpErr); - }); +function neq25519(a, b) { + const c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); } -exports.updateExchangeFromUrl = updateExchangeFromUrl; -/** - * Update or add exchange DB entry by fetching the /keys and /wire information. - * Optionally link the reserve entry to the new or existing - * exchange entry in then DB. - */ -function updateExchangeFromUrlImpl(ws, baseUrl, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const now = time.getTimestampNow(); - baseUrl = helpers.canonicalizeBaseUrl(baseUrl); - const r = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); - if (!r) { - const newExchangeRecord = { - builtIn: false, - baseUrl: baseUrl, - details: undefined, - wireInfo: undefined, - updateStatus: "fetch-keys" /* FetchKeys */, - updateStarted: now, - updateReason: "initial" /* Initial */, - timestampAdded: time.getTimestampNow(), - termsOfServiceAcceptedEtag: undefined, - termsOfServiceAcceptedTimestamp: undefined, - termsOfServiceLastEtag: undefined, - termsOfServiceText: undefined, - updateDiff: undefined, - }; - yield ws.db.put(dbTypes.Stores.exchanges, newExchangeRecord); - } - else { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (t) => __awaiter(this, void 0, void 0, function* () { - const rec = yield t.get(dbTypes.Stores.exchanges, baseUrl); - if (!rec) { - return; - } - if (rec.updateStatus != "fetch-keys" /* FetchKeys */ && !forceNow) { - return; - } - if (rec.updateStatus != "fetch-keys" /* FetchKeys */ && forceNow) { - rec.updateReason = "forced" /* Forced */; - } - rec.updateStarted = now; - rec.updateStatus = "fetch-keys" /* FetchKeys */; - rec.lastError = undefined; - t.put(dbTypes.Stores.exchanges, rec); - })); - } - yield updateExchangeWithKeys(ws, baseUrl); - yield updateExchangeWithWireInfo(ws, baseUrl); - yield updateExchangeWithTermsOfService(ws, baseUrl); - yield updateExchangeFinalize(ws, baseUrl); - const updatedExchange = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); - if (!updatedExchange) { - // This should practically never happen - throw Error("exchange not found"); - } - return updatedExchange; - }); +function par25519(a) { + const d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; } -/** - * Check if and how an exchange is trusted and/or audited. - */ -function getExchangeTrust(ws, exchangeInfo) { - return __awaiter(this, void 0, void 0, function* () { - let isTrusted = false; - let isAudited = false; - const exchangeDetails = exchangeInfo.details; - if (!exchangeDetails) { - throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); - } - const currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, exchangeDetails.currency); - if (currencyRecord) { - for (const trustedExchange of currencyRecord.exchanges) { - if (trustedExchange.exchangePub === exchangeDetails.masterPublicKey) { - isTrusted = true; - break; - } - } - for (const trustedAuditor of currencyRecord.auditors) { - for (const exchangeAuditor of exchangeDetails.auditors) { - if (trustedAuditor.auditorPub === exchangeAuditor.auditor_pub) { - isAudited = true; - break; - } - } - } - } - return { isTrusted, isAudited }; - }); -} -exports.getExchangeTrust = getExchangeTrust; -function getExchangePaytoUri(ws, exchangeBaseUrl, supportedTargetTypes) { - return __awaiter(this, void 0, void 0, function* () { - // We do the update here, since the exchange might not even exist - // yet in our database. - const exchangeRecord = yield updateExchangeFromUrl(ws, exchangeBaseUrl); - if (!exchangeRecord) { - throw Error(`Exchange '${exchangeBaseUrl}' not found.`); - } - const exchangeWireInfo = exchangeRecord.wireInfo; - if (!exchangeWireInfo) { - throw Error(`Exchange wire info for '${exchangeBaseUrl}' not found.`); - } - for (let account of exchangeWireInfo.accounts) { - const res = payto.parsePaytoUri(account.payto_uri); - if (!res) { - continue; - } - if (supportedTargetTypes.includes(res.targetType)) { - return account.payto_uri; - } - } - throw Error("no matching exchange account found"); - }); -} -exports.getExchangePaytoUri = getExchangePaytoUri; - -}); - -unwrapExports(exchanges); -var exchanges_1 = exchanges.acceptExchangeTermsOfService; -var exchanges_2 = exchanges.updateExchangeFromUrl; -var exchanges_3 = exchanges.getExchangeTrust; -var exchanges_4 = exchanges.getExchangePaytoUri; - -var libtoolVersion = createCommonjsModule(function (module, exports) { -/* - This file is part of TALER - (C) 2017 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Compare two libtool-style version strings. - */ -function compare(me, other) { - const meVer = parseVersion(me); - const otherVer = parseVersion(other); - if (!(meVer && otherVer)) { - return undefined; - } - const compatible = (meVer.current - meVer.age <= otherVer.current && - meVer.current >= (otherVer.current - otherVer.age)); - const currentCmp = Math.sign(meVer.current - otherVer.current); - return { compatible, currentCmp }; -} -exports.compare = compare; -function parseVersion(v) { - const [currentStr, revisionStr, ageStr, ...rest] = v.split(":"); - if (rest.length !== 0) { - return undefined; - } - const current = Number.parseInt(currentStr); - const revision = Number.parseInt(revisionStr); - const age = Number.parseInt(ageStr); - if (Number.isNaN(current)) { - return undefined; - } - if (Number.isNaN(revision)) { - return undefined; - } - if (Number.isNaN(age)) { - return undefined; - } - return { current, revision, age }; -} - -}); - -unwrapExports(libtoolVersion); -var libtoolVersion_1 = libtoolVersion.compare; - -var withdraw = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); - -const Amounts = __importStar(amounts); - - - - - -const LibtoolVersion = __importStar(libtoolVersion); - - -const logger = new logging.Logger("withdraw.ts"); -function isWithdrawableDenom(d) { - const now = time.getTimestampNow(); - const started = time.timestampCmp(now, d.stampStart) >= 0; - const lastPossibleWithdraw = time.timestampSubtractDuraction(d.stampExpireWithdraw, { d_ms: 50 * 1000 }); - const remaining = time.getDurationRemaining(lastPossibleWithdraw, now); - const stillOkay = remaining.d_ms !== 0; - return started && stillOkay; -} -/** - * Get a list of denominations (with repetitions possible) - * whose total value is as close as possible to the available - * amount, but never larger. - */ -function getWithdrawDenomList(amountAvailable, denoms) { - let remaining = Amounts.copy(amountAvailable); - const ds = []; - denoms = denoms.filter(isWithdrawableDenom); - denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); - // This is an arbitrary number of coins - // we can withdraw in one go. It's not clear if this limit - // is useful ... - for (let i = 0; i < 1000; i++) { - let found = false; - for (const d of denoms) { - const cost = Amounts.add(d.value, d.feeWithdraw).amount; - if (Amounts.cmp(remaining, cost) < 0) { - continue; - } - found = true; - remaining = Amounts.sub(remaining, cost).amount; - ds.push(d); - break; - } - if (!found) { - break; - } - } - return ds; +function unpack25519(o, n) { + let i; + for (i = 0; i < 16; i++) + o[i] = n[2 * i] + (n[2 * i + 1] << 8); + o[15] &= 0x7fff; } -exports.getWithdrawDenomList = getWithdrawDenomList; -/** - * Get information about a withdrawal from - * a taler://withdraw URI by asking the bank. - */ -function getBankWithdrawalInfo(ws, talerWithdrawUri) { - return __awaiter(this, void 0, void 0, function* () { - const uriResult = taleruri.parseWithdrawUri(talerWithdrawUri); - if (!uriResult) { - throw Error(`can't parse URL ${talerWithdrawUri}`); - } - const resp = yield ws.http.get(uriResult.statusUrl); - if (resp.status !== 200) { - throw Error(`unexpected status (${resp.status}) from bank for ${uriResult.statusUrl}`); - } - const respJson = yield resp.json(); - console.log("resp:", respJson); - const status = talerTypes.codecForWithdrawOperationStatusResponse().decode(respJson); - return { - amount: Amounts.parseOrThrow(status.amount), - confirmTransferUrl: status.confirm_transfer_url, - extractedStatusUrl: uriResult.statusUrl, - selectionDone: status.selection_done, - senderWire: status.sender_wire, - suggestedExchange: status.suggested_exchange, - transferDone: status.transfer_done, - wireTypes: status.wire_types, - }; - }); +function A(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] + b[i]; } -exports.getBankWithdrawalInfo = getBankWithdrawalInfo; -function getPossibleDenoms(ws, exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { - return yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl) - .filter(d => { - return (d.status === dbTypes.DenominationStatus.Unverified || - d.status === dbTypes.DenominationStatus.VerifiedGood); - }); - }); +function Z(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] - b[i]; } -/** - * Given a planchet, withdraw a coin from the exchange. - */ -function processPlanchet(ws, withdrawalSessionId, coinIdx) { - return __awaiter(this, void 0, void 0, function* () { - const withdrawalSession = yield ws.db.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!withdrawalSession) { - return; - } - if (withdrawalSession.withdrawn[coinIdx]) { - return; - } - if (withdrawalSession.source.type === "reserve") ; - const planchet = withdrawalSession.planchets[coinIdx]; - if (!planchet) { - console.log("processPlanchet: planchet not found"); - return; - } - const exchange = yield ws.db.get(dbTypes.Stores.exchanges, withdrawalSession.exchangeBaseUrl); - if (!exchange) { - console.error("db inconsistent: exchange for planchet not found"); - return; - } - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - withdrawalSession.exchangeBaseUrl, - planchet.denomPub, - ]); - if (!denom) { - console.error("db inconsistent: denom for planchet not found"); - return; - } - const wd = {}; - wd.denom_pub_hash = planchet.denomPubHash; - wd.reserve_pub = planchet.reservePub; - wd.reserve_sig = planchet.withdrawSig; - wd.coin_ev = planchet.coinEv; - const reqUrl = new URL("reserve/withdraw", exchange.baseUrl).href; - const resp = yield ws.http.postJson(reqUrl, wd); - if (resp.status !== 200) { - throw Error(`unexpected status ${resp.status} for withdraw`); - } - const r = yield resp.json(); - const denomSig = yield ws.cryptoApi.rsaUnblind(r.ev_sig, planchet.blindingKey, planchet.denomPub); - const isValid = yield ws.cryptoApi.rsaVerify(planchet.coinPub, denomSig, planchet.denomPub); - if (!isValid) { - throw Error("invalid RSA signature by the exchange"); - } - const coin = { - blindingKey: planchet.blindingKey, - coinPriv: planchet.coinPriv, - coinPub: planchet.coinPub, - currentAmount: planchet.coinValue, - denomPub: planchet.denomPub, - denomPubHash: planchet.denomPubHash, - denomSig, - exchangeBaseUrl: withdrawalSession.exchangeBaseUrl, - reservePub: planchet.reservePub, - status: dbTypes.CoinStatus.Fresh, - coinIndex: coinIdx, - withdrawSessionId: withdrawalSessionId, - }; - let withdrawSessionFinished = false; - let reserveDepleted = false; - const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.withdrawalSession, dbTypes.Stores.reserves], (tx) => __awaiter(this, void 0, void 0, function* () { - const ws = yield tx.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!ws) { - return false; - } - if (ws.withdrawn[coinIdx]) { - // Already withdrawn - return false; - } - ws.withdrawn[coinIdx] = true; - delete ws.lastErrorPerCoin[coinIdx]; - let numDone = 0; - for (let i = 0; i < ws.withdrawn.length; i++) { - if (ws.withdrawn[i]) { - numDone++; - } - } - if (numDone === ws.denoms.length) { - ws.timestampFinish = time.getTimestampNow(); - ws.lastError = undefined; - ws.retryInfo = dbTypes.initRetryInfo(false); - withdrawSessionFinished = true; - } - yield tx.put(dbTypes.Stores.withdrawalSession, ws); - if (!planchet.isFromTip) { - const r = yield tx.get(dbTypes.Stores.reserves, planchet.reservePub); - if (r) { - r.amountWithdrawCompleted = Amounts.add(r.amountWithdrawCompleted, Amounts.add(denom.value, denom.feeWithdraw).amount).amount; - if (Amounts.cmp(r.amountWithdrawCompleted, r.amountWithdrawAllocated) == - 0) { - reserveDepleted = true; - } - yield tx.put(dbTypes.Stores.reserves, r); - } - } - yield tx.add(dbTypes.Stores.coins, coin); - return true; - })); - if (success) { - ws.notify({ - type: "coin-withdrawn" /* CoinWithdrawn */, - }); - } - if (withdrawSessionFinished) { - ws.notify({ - type: "withdraw-session-finished" /* WithdrawSessionFinished */, - withdrawSessionId: withdrawalSessionId, - }); - } - if (reserveDepleted && withdrawalSession.source.type === "reserve") { - ws.notify({ - type: "reserve-depleted" /* ReserveDepleted */, - reservePub: withdrawalSession.source.reservePub, - }); - } - }); -} -/** - * Get a list of denominations to withdraw from the given exchange for the - * given amount, making sure that all denominations' signatures are verified. - * - * Writes to the DB in order to record the result from verifying - * denominations. - */ -function getVerifiedWithdrawDenomList(ws, exchangeBaseUrl, amount) { - return __awaiter(this, void 0, void 0, function* () { - const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); - if (!exchange) { - console.log("exchange not found"); - throw Error(`exchange ${exchangeBaseUrl} not found`); - } - const exchangeDetails = exchange.details; - if (!exchangeDetails) { - console.log("exchange details not available"); - throw Error(`exchange ${exchangeBaseUrl} details not available`); - } - console.log("getting possible denoms"); - const possibleDenoms = yield getPossibleDenoms(ws, exchange.baseUrl); - console.log("got possible denoms"); - let allValid = false; - let selectedDenoms; - do { - allValid = true; - selectedDenoms = getWithdrawDenomList(amount, possibleDenoms); - console.log("got withdraw denom list"); - for (const denom of selectedDenoms || []) { - if (denom.status === dbTypes.DenominationStatus.Unverified) { - console.log("checking validity", denom, exchangeDetails.masterPublicKey); - const valid = yield ws.cryptoApi.isValidDenom(denom, exchangeDetails.masterPublicKey); - console.log("done checking validity"); - if (!valid) { - denom.status = dbTypes.DenominationStatus.VerifiedBad; - allValid = false; - } - else { - denom.status = dbTypes.DenominationStatus.VerifiedGood; - } - yield ws.db.put(dbTypes.Stores.denominations, denom); - } - } - } while (selectedDenoms.length > 0 && !allValid); - console.log("returning denoms"); - return selectedDenoms; - }); -} -exports.getVerifiedWithdrawDenomList = getVerifiedWithdrawDenomList; -function makePlanchet(ws, withdrawalSessionId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - const withdrawalSession = yield ws.db.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!withdrawalSession) { - return; - } - const src = withdrawalSession.source; - if (src.type !== "reserve") { - throw Error("invalid state"); - } - const reserve = yield ws.db.get(dbTypes.Stores.reserves, src.reservePub); - if (!reserve) { - return; - } - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - withdrawalSession.exchangeBaseUrl, - withdrawalSession.denoms[coinIndex], - ]); - if (!denom) { - return; - } - const r = yield ws.cryptoApi.createPlanchet({ - denomPub: denom.denomPub, - feeWithdraw: denom.feeWithdraw, - reservePriv: reserve.reservePriv, - reservePub: reserve.reservePub, - value: denom.value, - }); - const newPlanchet = { - blindingKey: r.blindingKey, - coinEv: r.coinEv, - coinPriv: r.coinPriv, - coinPub: r.coinPub, - coinValue: r.coinValue, - denomPub: r.denomPub, - denomPubHash: r.denomPubHash, - isFromTip: false, - reservePub: r.reservePub, - withdrawSig: r.withdrawSig, - }; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.withdrawalSession], (tx) => __awaiter(this, void 0, void 0, function* () { - const myWs = yield tx.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!myWs) { - return; - } - if (myWs.planchets[coinIndex]) { - return; - } - myWs.planchets[coinIndex] = newPlanchet; - yield tx.put(dbTypes.Stores.withdrawalSession, myWs); - })); - }); -} -function processWithdrawCoin(ws, withdrawalSessionId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace("starting withdraw for coin", coinIndex); - const withdrawalSession = yield ws.db.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!withdrawalSession) { - console.log("ws doesn't exist"); - return; - } - const coin = yield ws.db.getIndexed(dbTypes.Stores.coins.byWithdrawalWithIdx, [ - withdrawalSessionId, - coinIndex, - ]); - if (coin) { - console.log("coin already exists"); - return; - } - if (!withdrawalSession.planchets[coinIndex]) { - const key = `${withdrawalSessionId}-${coinIndex}`; - yield ws.memoMakePlanchet.memo(key, () => __awaiter(this, void 0, void 0, function* () { - logger.trace("creating planchet for coin", coinIndex); - return makePlanchet(ws, withdrawalSessionId, coinIndex); - })); - } - yield processPlanchet(ws, withdrawalSessionId, coinIndex); - }); -} -function incrementWithdrawalRetry(ws, withdrawalSessionId, err) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.withdrawalSession], (tx) => __awaiter(this, void 0, void 0, function* () { - const wsr = yield tx.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!wsr) { - return; - } - if (!wsr.retryInfo) { - return; - } - wsr.retryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(wsr.retryInfo); - wsr.lastError = err; - yield tx.put(dbTypes.Stores.withdrawalSession, wsr); - })); - ws.notify({ type: "withdraw-error" /* WithdrawOperationError */ }); - }); -} -function processWithdrawSession(ws, withdrawalSessionId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => incrementWithdrawalRetry(ws, withdrawalSessionId, e); - yield errors.guardOperationException(() => processWithdrawSessionImpl(ws, withdrawalSessionId, forceNow), onOpErr); - }); -} -exports.processWithdrawSession = processWithdrawSession; -function resetWithdrawSessionRetry(ws, withdrawalSessionId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.withdrawalSession, withdrawalSessionId, x => { - if (x.retryInfo.active) { - x.retryInfo = dbTypes.initRetryInfo(); - } - return x; - }); - }); -} -function processWithdrawSessionImpl(ws, withdrawalSessionId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace("processing withdraw session", withdrawalSessionId); - if (forceNow) { - yield resetWithdrawSessionRetry(ws, withdrawalSessionId); - } - const withdrawalSession = yield ws.db.get(dbTypes.Stores.withdrawalSession, withdrawalSessionId); - if (!withdrawalSession) { - logger.trace("withdraw session doesn't exist"); - return; - } - const ps = withdrawalSession.denoms.map((d, i) => processWithdrawCoin(ws, withdrawalSessionId, i)); - yield Promise.all(ps); - return; - }); -} -function getExchangeWithdrawalInfo(ws, baseUrl, amount) { - return __awaiter(this, void 0, void 0, function* () { - const exchangeInfo = yield exchanges.updateExchangeFromUrl(ws, baseUrl); - const exchangeDetails = exchangeInfo.details; - if (!exchangeDetails) { - throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); - } - const exchangeWireInfo = exchangeInfo.wireInfo; - if (!exchangeWireInfo) { - throw Error(`exchange ${exchangeInfo.baseUrl} wire details not available`); - } - const selectedDenoms = yield getVerifiedWithdrawDenomList(ws, baseUrl, amount); - let acc = Amounts.getZero(amount.currency); - for (const d of selectedDenoms) { - acc = Amounts.add(acc, d.feeWithdraw).amount; - } - const actualCoinCost = selectedDenoms - .map((d) => Amounts.add(d.value, d.feeWithdraw).amount) - .reduce((a, b) => Amounts.add(a, b).amount); - const exchangeWireAccounts = []; - for (let account of exchangeWireInfo.accounts) { - exchangeWireAccounts.push(account.payto_uri); - } - const { isTrusted, isAudited } = yield exchanges.getExchangeTrust(ws, exchangeInfo); - let earliestDepositExpiration = selectedDenoms[0].stampExpireDeposit; - for (let i = 1; i < selectedDenoms.length; i++) { - const expireDeposit = selectedDenoms[i].stampExpireDeposit; - if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) { - earliestDepositExpiration = expireDeposit; - } - } - const possibleDenoms = yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, baseUrl) - .filter(d => d.isOffered); - const trustedAuditorPubs = []; - const currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, amount.currency); - if (currencyRecord) { - trustedAuditorPubs.push(...currencyRecord.auditors.map(a => a.auditorPub)); - } - let versionMatch; - if (exchangeDetails.protocolVersion) { - versionMatch = LibtoolVersion.compare(versions.WALLET_EXCHANGE_PROTOCOL_VERSION, exchangeDetails.protocolVersion); - if (versionMatch && - !versionMatch.compatible && - versionMatch.currentCmp === -1) { - console.warn(`wallet's support for exchange protocol version ${versions.WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + - `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`); - } - } - let tosAccepted = false; - if (exchangeInfo.termsOfServiceAcceptedTimestamp) { - if (exchangeInfo.termsOfServiceAcceptedEtag == - exchangeInfo.termsOfServiceLastEtag) { - tosAccepted = true; - } - } - const ret = { - earliestDepositExpiration, - exchangeInfo, - exchangeWireAccounts, - exchangeVersion: exchangeDetails.protocolVersion || "unknown", - isAudited, - isTrusted, - numOfferedDenoms: possibleDenoms.length, - overhead: Amounts.sub(amount, actualCoinCost).amount, - selectedDenoms, - trustedAuditorPubs, - versionMatch, - walletVersion: versions.WALLET_EXCHANGE_PROTOCOL_VERSION, - wireFees: exchangeWireInfo, - withdrawFee: acc, - termsOfServiceAccepted: tosAccepted, - }; - return ret; - }); -} -exports.getExchangeWithdrawalInfo = getExchangeWithdrawalInfo; -function getWithdrawDetailsForUri(ws, talerWithdrawUri, maybeSelectedExchange) { - return __awaiter(this, void 0, void 0, function* () { - const info = yield getBankWithdrawalInfo(ws, talerWithdrawUri); - let rci = undefined; - if (maybeSelectedExchange) { - rci = yield getExchangeWithdrawalInfo(ws, maybeSelectedExchange, info.amount); - } - return { - bankWithdrawDetails: info, - exchangeWithdrawDetails: rci, - }; - }); -} -exports.getWithdrawDetailsForUri = getWithdrawDetailsForUri; - -}); - -unwrapExports(withdraw); -var withdraw_1 = withdraw.getWithdrawDenomList; -var withdraw_2 = withdraw.getBankWithdrawalInfo; -var withdraw_3 = withdraw.getVerifiedWithdrawDenomList; -var withdraw_4 = withdraw.processWithdrawSession; -var withdraw_5 = withdraw.getExchangeWithdrawalInfo; -var withdraw_6 = withdraw.getWithdrawDetailsForUri; - -var naclFast = createCommonjsModule(function (module, exports) { -// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. -// TypeScript port in 2019 by Florian Dold. -// Public domain. -// -// Implementation derived from TweetNaCl version 20140427. -// See for details: http://tweetnacl.cr.yp.to/ -Object.defineProperty(exports, "__esModule", { value: true }); -const gf = function (init = []) { - const r = new Float64Array(16); - if (init) - for (let i = 0; i < init.length; i++) - r[i] = init[i]; - return r; -}; -// Pluggable, initialized in high-level API below. -let randombytes = function (x, n) { - throw new Error("no PRNG"); -}; -const _0 = new Uint8Array(16); -const _9 = new Uint8Array(32); -_9[0] = 9; -// prettier-ignore -const gf0 = gf(); -const gf1 = gf([1]); -const _121665 = gf([0xdb41, 1]); -const D = gf([ - 0x78a3, - 0x1359, - 0x4dca, - 0x75eb, - 0xd8ab, - 0x4141, - 0x0a4d, - 0x0070, - 0xe898, - 0x7779, - 0x4079, - 0x8cc7, - 0xfe73, - 0x2b6f, - 0x6cee, - 0x5203, -]); -const D2 = gf([ - 0xf159, - 0x26b2, - 0x9b94, - 0xebd6, - 0xb156, - 0x8283, - 0x149a, - 0x00e0, - 0xd130, - 0xeef3, - 0x80f2, - 0x198e, - 0xfce7, - 0x56df, - 0xd9dc, - 0x2406, -]); -const X = gf([ - 0xd51a, - 0x8f25, - 0x2d60, - 0xc956, - 0xa7b2, - 0x9525, - 0xc760, - 0x692c, - 0xdc5c, - 0xfdd6, - 0xe231, - 0xc0a4, - 0x53fe, - 0xcd6e, - 0x36d3, - 0x2169, -]); -const Y = gf([ - 0x6658, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, - 0x6666, -]); -const I = gf([ - 0xa0b0, - 0x4a0e, - 0x1b27, - 0xc4ee, - 0xe478, - 0xad2f, - 0x1806, - 0x2f43, - 0xd7a7, - 0x3dfb, - 0x0099, - 0x2b4d, - 0xdf0b, - 0x4fc1, - 0x2480, - 0x2b83, -]); -function ts64(x, i, h, l) { - x[i] = (h >> 24) & 0xff; - x[i + 1] = (h >> 16) & 0xff; - x[i + 2] = (h >> 8) & 0xff; - x[i + 3] = h & 0xff; - x[i + 4] = (l >> 24) & 0xff; - x[i + 5] = (l >> 16) & 0xff; - x[i + 6] = (l >> 8) & 0xff; - x[i + 7] = l & 0xff; -} -function vn(x, xi, y, yi, n) { - var i, d = 0; - for (i = 0; i < n; i++) - d |= x[xi + i] ^ y[yi + i]; - return (1 & ((d - 1) >>> 8)) - 1; -} -function crypto_verify_16(x, xi, y, yi) { - return vn(x, xi, y, yi, 16); -} -function crypto_verify_32(x, xi, y, yi) { - return vn(x, xi, y, yi, 32); -} -// prettier-ignore -function core_salsa20(o, p, k, c) { - var j0 = c[0] & 0xff | (c[1] & 0xff) << 8 | (c[2] & 0xff) << 16 | (c[3] & 0xff) << 24, j1 = k[0] & 0xff | (k[1] & 0xff) << 8 | (k[2] & 0xff) << 16 | (k[3] & 0xff) << 24, j2 = k[4] & 0xff | (k[5] & 0xff) << 8 | (k[6] & 0xff) << 16 | (k[7] & 0xff) << 24, j3 = k[8] & 0xff | (k[9] & 0xff) << 8 | (k[10] & 0xff) << 16 | (k[11] & 0xff) << 24, j4 = k[12] & 0xff | (k[13] & 0xff) << 8 | (k[14] & 0xff) << 16 | (k[15] & 0xff) << 24, j5 = c[4] & 0xff | (c[5] & 0xff) << 8 | (c[6] & 0xff) << 16 | (c[7] & 0xff) << 24, j6 = p[0] & 0xff | (p[1] & 0xff) << 8 | (p[2] & 0xff) << 16 | (p[3] & 0xff) << 24, j7 = p[4] & 0xff | (p[5] & 0xff) << 8 | (p[6] & 0xff) << 16 | (p[7] & 0xff) << 24, j8 = p[8] & 0xff | (p[9] & 0xff) << 8 | (p[10] & 0xff) << 16 | (p[11] & 0xff) << 24, j9 = p[12] & 0xff | (p[13] & 0xff) << 8 | (p[14] & 0xff) << 16 | (p[15] & 0xff) << 24, j10 = c[8] & 0xff | (c[9] & 0xff) << 8 | (c[10] & 0xff) << 16 | (c[11] & 0xff) << 24, j11 = k[16] & 0xff | (k[17] & 0xff) << 8 | (k[18] & 0xff) << 16 | (k[19] & 0xff) << 24, j12 = k[20] & 0xff | (k[21] & 0xff) << 8 | (k[22] & 0xff) << 16 | (k[23] & 0xff) << 24, j13 = k[24] & 0xff | (k[25] & 0xff) << 8 | (k[26] & 0xff) << 16 | (k[27] & 0xff) << 24, j14 = k[28] & 0xff | (k[29] & 0xff) << 8 | (k[30] & 0xff) << 16 | (k[31] & 0xff) << 24, j15 = c[12] & 0xff | (c[13] & 0xff) << 8 | (c[14] & 0xff) << 16 | (c[15] & 0xff) << 24; - var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15, u; - for (var i = 0; i < 20; i += 2) { - u = x0 + x12 | 0; - x4 ^= u << 7 | u >>> (32 - 7); - u = x4 + x0 | 0; - x8 ^= u << 9 | u >>> (32 - 9); - u = x8 + x4 | 0; - x12 ^= u << 13 | u >>> (32 - 13); - u = x12 + x8 | 0; - x0 ^= u << 18 | u >>> (32 - 18); - u = x5 + x1 | 0; - x9 ^= u << 7 | u >>> (32 - 7); - u = x9 + x5 | 0; - x13 ^= u << 9 | u >>> (32 - 9); - u = x13 + x9 | 0; - x1 ^= u << 13 | u >>> (32 - 13); - u = x1 + x13 | 0; - x5 ^= u << 18 | u >>> (32 - 18); - u = x10 + x6 | 0; - x14 ^= u << 7 | u >>> (32 - 7); - u = x14 + x10 | 0; - x2 ^= u << 9 | u >>> (32 - 9); - u = x2 + x14 | 0; - x6 ^= u << 13 | u >>> (32 - 13); - u = x6 + x2 | 0; - x10 ^= u << 18 | u >>> (32 - 18); - u = x15 + x11 | 0; - x3 ^= u << 7 | u >>> (32 - 7); - u = x3 + x15 | 0; - x7 ^= u << 9 | u >>> (32 - 9); - u = x7 + x3 | 0; - x11 ^= u << 13 | u >>> (32 - 13); - u = x11 + x7 | 0; - x15 ^= u << 18 | u >>> (32 - 18); - u = x0 + x3 | 0; - x1 ^= u << 7 | u >>> (32 - 7); - u = x1 + x0 | 0; - x2 ^= u << 9 | u >>> (32 - 9); - u = x2 + x1 | 0; - x3 ^= u << 13 | u >>> (32 - 13); - u = x3 + x2 | 0; - x0 ^= u << 18 | u >>> (32 - 18); - u = x5 + x4 | 0; - x6 ^= u << 7 | u >>> (32 - 7); - u = x6 + x5 | 0; - x7 ^= u << 9 | u >>> (32 - 9); - u = x7 + x6 | 0; - x4 ^= u << 13 | u >>> (32 - 13); - u = x4 + x7 | 0; - x5 ^= u << 18 | u >>> (32 - 18); - u = x10 + x9 | 0; - x11 ^= u << 7 | u >>> (32 - 7); - u = x11 + x10 | 0; - x8 ^= u << 9 | u >>> (32 - 9); - u = x8 + x11 | 0; - x9 ^= u << 13 | u >>> (32 - 13); - u = x9 + x8 | 0; - x10 ^= u << 18 | u >>> (32 - 18); - u = x15 + x14 | 0; - x12 ^= u << 7 | u >>> (32 - 7); - u = x12 + x15 | 0; - x13 ^= u << 9 | u >>> (32 - 9); - u = x13 + x12 | 0; - x14 ^= u << 13 | u >>> (32 - 13); - u = x14 + x13 | 0; - x15 ^= u << 18 | u >>> (32 - 18); - } - x0 = x0 + j0 | 0; - x1 = x1 + j1 | 0; - x2 = x2 + j2 | 0; - x3 = x3 + j3 | 0; - x4 = x4 + j4 | 0; - x5 = x5 + j5 | 0; - x6 = x6 + j6 | 0; - x7 = x7 + j7 | 0; - x8 = x8 + j8 | 0; - x9 = x9 + j9 | 0; - x10 = x10 + j10 | 0; - x11 = x11 + j11 | 0; - x12 = x12 + j12 | 0; - x13 = x13 + j13 | 0; - x14 = x14 + j14 | 0; - x15 = x15 + j15 | 0; - o[0] = x0 >>> 0 & 0xff; - o[1] = x0 >>> 8 & 0xff; - o[2] = x0 >>> 16 & 0xff; - o[3] = x0 >>> 24 & 0xff; - o[4] = x1 >>> 0 & 0xff; - o[5] = x1 >>> 8 & 0xff; - o[6] = x1 >>> 16 & 0xff; - o[7] = x1 >>> 24 & 0xff; - o[8] = x2 >>> 0 & 0xff; - o[9] = x2 >>> 8 & 0xff; - o[10] = x2 >>> 16 & 0xff; - o[11] = x2 >>> 24 & 0xff; - o[12] = x3 >>> 0 & 0xff; - o[13] = x3 >>> 8 & 0xff; - o[14] = x3 >>> 16 & 0xff; - o[15] = x3 >>> 24 & 0xff; - o[16] = x4 >>> 0 & 0xff; - o[17] = x4 >>> 8 & 0xff; - o[18] = x4 >>> 16 & 0xff; - o[19] = x4 >>> 24 & 0xff; - o[20] = x5 >>> 0 & 0xff; - o[21] = x5 >>> 8 & 0xff; - o[22] = x5 >>> 16 & 0xff; - o[23] = x5 >>> 24 & 0xff; - o[24] = x6 >>> 0 & 0xff; - o[25] = x6 >>> 8 & 0xff; - o[26] = x6 >>> 16 & 0xff; - o[27] = x6 >>> 24 & 0xff; - o[28] = x7 >>> 0 & 0xff; - o[29] = x7 >>> 8 & 0xff; - o[30] = x7 >>> 16 & 0xff; - o[31] = x7 >>> 24 & 0xff; - o[32] = x8 >>> 0 & 0xff; - o[33] = x8 >>> 8 & 0xff; - o[34] = x8 >>> 16 & 0xff; - o[35] = x8 >>> 24 & 0xff; - o[36] = x9 >>> 0 & 0xff; - o[37] = x9 >>> 8 & 0xff; - o[38] = x9 >>> 16 & 0xff; - o[39] = x9 >>> 24 & 0xff; - o[40] = x10 >>> 0 & 0xff; - o[41] = x10 >>> 8 & 0xff; - o[42] = x10 >>> 16 & 0xff; - o[43] = x10 >>> 24 & 0xff; - o[44] = x11 >>> 0 & 0xff; - o[45] = x11 >>> 8 & 0xff; - o[46] = x11 >>> 16 & 0xff; - o[47] = x11 >>> 24 & 0xff; - o[48] = x12 >>> 0 & 0xff; - o[49] = x12 >>> 8 & 0xff; - o[50] = x12 >>> 16 & 0xff; - o[51] = x12 >>> 24 & 0xff; - o[52] = x13 >>> 0 & 0xff; - o[53] = x13 >>> 8 & 0xff; - o[54] = x13 >>> 16 & 0xff; - o[55] = x13 >>> 24 & 0xff; - o[56] = x14 >>> 0 & 0xff; - o[57] = x14 >>> 8 & 0xff; - o[58] = x14 >>> 16 & 0xff; - o[59] = x14 >>> 24 & 0xff; - o[60] = x15 >>> 0 & 0xff; - o[61] = x15 >>> 8 & 0xff; - o[62] = x15 >>> 16 & 0xff; - o[63] = x15 >>> 24 & 0xff; -} -function core_hsalsa20(o, p, k, c) { - var j0 = (c[0] & 0xff) | - ((c[1] & 0xff) << 8) | - ((c[2] & 0xff) << 16) | - ((c[3] & 0xff) << 24), j1 = (k[0] & 0xff) | - ((k[1] & 0xff) << 8) | - ((k[2] & 0xff) << 16) | - ((k[3] & 0xff) << 24), j2 = (k[4] & 0xff) | - ((k[5] & 0xff) << 8) | - ((k[6] & 0xff) << 16) | - ((k[7] & 0xff) << 24), j3 = (k[8] & 0xff) | - ((k[9] & 0xff) << 8) | - ((k[10] & 0xff) << 16) | - ((k[11] & 0xff) << 24), j4 = (k[12] & 0xff) | - ((k[13] & 0xff) << 8) | - ((k[14] & 0xff) << 16) | - ((k[15] & 0xff) << 24), j5 = (c[4] & 0xff) | - ((c[5] & 0xff) << 8) | - ((c[6] & 0xff) << 16) | - ((c[7] & 0xff) << 24), j6 = (p[0] & 0xff) | - ((p[1] & 0xff) << 8) | - ((p[2] & 0xff) << 16) | - ((p[3] & 0xff) << 24), j7 = (p[4] & 0xff) | - ((p[5] & 0xff) << 8) | - ((p[6] & 0xff) << 16) | - ((p[7] & 0xff) << 24), j8 = (p[8] & 0xff) | - ((p[9] & 0xff) << 8) | - ((p[10] & 0xff) << 16) | - ((p[11] & 0xff) << 24), j9 = (p[12] & 0xff) | - ((p[13] & 0xff) << 8) | - ((p[14] & 0xff) << 16) | - ((p[15] & 0xff) << 24), j10 = (c[8] & 0xff) | - ((c[9] & 0xff) << 8) | - ((c[10] & 0xff) << 16) | - ((c[11] & 0xff) << 24), j11 = (k[16] & 0xff) | - ((k[17] & 0xff) << 8) | - ((k[18] & 0xff) << 16) | - ((k[19] & 0xff) << 24), j12 = (k[20] & 0xff) | - ((k[21] & 0xff) << 8) | - ((k[22] & 0xff) << 16) | - ((k[23] & 0xff) << 24), j13 = (k[24] & 0xff) | - ((k[25] & 0xff) << 8) | - ((k[26] & 0xff) << 16) | - ((k[27] & 0xff) << 24), j14 = (k[28] & 0xff) | - ((k[29] & 0xff) << 8) | - ((k[30] & 0xff) << 16) | - ((k[31] & 0xff) << 24), j15 = (c[12] & 0xff) | - ((c[13] & 0xff) << 8) | - ((c[14] & 0xff) << 16) | - ((c[15] & 0xff) << 24); - var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15, u; - for (var i = 0; i < 20; i += 2) { - u = (x0 + x12) | 0; - x4 ^= (u << 7) | (u >>> (32 - 7)); - u = (x4 + x0) | 0; - x8 ^= (u << 9) | (u >>> (32 - 9)); - u = (x8 + x4) | 0; - x12 ^= (u << 13) | (u >>> (32 - 13)); - u = (x12 + x8) | 0; - x0 ^= (u << 18) | (u >>> (32 - 18)); - u = (x5 + x1) | 0; - x9 ^= (u << 7) | (u >>> (32 - 7)); - u = (x9 + x5) | 0; - x13 ^= (u << 9) | (u >>> (32 - 9)); - u = (x13 + x9) | 0; - x1 ^= (u << 13) | (u >>> (32 - 13)); - u = (x1 + x13) | 0; - x5 ^= (u << 18) | (u >>> (32 - 18)); - u = (x10 + x6) | 0; - x14 ^= (u << 7) | (u >>> (32 - 7)); - u = (x14 + x10) | 0; - x2 ^= (u << 9) | (u >>> (32 - 9)); - u = (x2 + x14) | 0; - x6 ^= (u << 13) | (u >>> (32 - 13)); - u = (x6 + x2) | 0; - x10 ^= (u << 18) | (u >>> (32 - 18)); - u = (x15 + x11) | 0; - x3 ^= (u << 7) | (u >>> (32 - 7)); - u = (x3 + x15) | 0; - x7 ^= (u << 9) | (u >>> (32 - 9)); - u = (x7 + x3) | 0; - x11 ^= (u << 13) | (u >>> (32 - 13)); - u = (x11 + x7) | 0; - x15 ^= (u << 18) | (u >>> (32 - 18)); - u = (x0 + x3) | 0; - x1 ^= (u << 7) | (u >>> (32 - 7)); - u = (x1 + x0) | 0; - x2 ^= (u << 9) | (u >>> (32 - 9)); - u = (x2 + x1) | 0; - x3 ^= (u << 13) | (u >>> (32 - 13)); - u = (x3 + x2) | 0; - x0 ^= (u << 18) | (u >>> (32 - 18)); - u = (x5 + x4) | 0; - x6 ^= (u << 7) | (u >>> (32 - 7)); - u = (x6 + x5) | 0; - x7 ^= (u << 9) | (u >>> (32 - 9)); - u = (x7 + x6) | 0; - x4 ^= (u << 13) | (u >>> (32 - 13)); - u = (x4 + x7) | 0; - x5 ^= (u << 18) | (u >>> (32 - 18)); - u = (x10 + x9) | 0; - x11 ^= (u << 7) | (u >>> (32 - 7)); - u = (x11 + x10) | 0; - x8 ^= (u << 9) | (u >>> (32 - 9)); - u = (x8 + x11) | 0; - x9 ^= (u << 13) | (u >>> (32 - 13)); - u = (x9 + x8) | 0; - x10 ^= (u << 18) | (u >>> (32 - 18)); - u = (x15 + x14) | 0; - x12 ^= (u << 7) | (u >>> (32 - 7)); - u = (x12 + x15) | 0; - x13 ^= (u << 9) | (u >>> (32 - 9)); - u = (x13 + x12) | 0; - x14 ^= (u << 13) | (u >>> (32 - 13)); - u = (x14 + x13) | 0; - x15 ^= (u << 18) | (u >>> (32 - 18)); - } - o[0] = (x0 >>> 0) & 0xff; - o[1] = (x0 >>> 8) & 0xff; - o[2] = (x0 >>> 16) & 0xff; - o[3] = (x0 >>> 24) & 0xff; - o[4] = (x5 >>> 0) & 0xff; - o[5] = (x5 >>> 8) & 0xff; - o[6] = (x5 >>> 16) & 0xff; - o[7] = (x5 >>> 24) & 0xff; - o[8] = (x10 >>> 0) & 0xff; - o[9] = (x10 >>> 8) & 0xff; - o[10] = (x10 >>> 16) & 0xff; - o[11] = (x10 >>> 24) & 0xff; - o[12] = (x15 >>> 0) & 0xff; - o[13] = (x15 >>> 8) & 0xff; - o[14] = (x15 >>> 16) & 0xff; - o[15] = (x15 >>> 24) & 0xff; - o[16] = (x6 >>> 0) & 0xff; - o[17] = (x6 >>> 8) & 0xff; - o[18] = (x6 >>> 16) & 0xff; - o[19] = (x6 >>> 24) & 0xff; - o[20] = (x7 >>> 0) & 0xff; - o[21] = (x7 >>> 8) & 0xff; - o[22] = (x7 >>> 16) & 0xff; - o[23] = (x7 >>> 24) & 0xff; - o[24] = (x8 >>> 0) & 0xff; - o[25] = (x8 >>> 8) & 0xff; - o[26] = (x8 >>> 16) & 0xff; - o[27] = (x8 >>> 24) & 0xff; - o[28] = (x9 >>> 0) & 0xff; - o[29] = (x9 >>> 8) & 0xff; - o[30] = (x9 >>> 16) & 0xff; - o[31] = (x9 >>> 24) & 0xff; -} -function crypto_core_salsa20(out, inp, k, c) { - core_salsa20(out, inp, k, c); -} -function crypto_core_hsalsa20(out, inp, k, c) { - core_hsalsa20(out, inp, k, c); -} -var sigma = new Uint8Array([ - 101, - 120, - 112, - 97, - 110, - 100, - 32, - 51, - 50, - 45, - 98, - 121, - 116, - 101, - 32, - 107, -]); -// "expand 32-byte k" -function crypto_stream_salsa20_xor(c, cpos, m, mpos, b, n, k) { - var z = new Uint8Array(16), x = new Uint8Array(64); - var u, i; - for (i = 0; i < 16; i++) - z[i] = 0; - for (i = 0; i < 8; i++) - z[i] = n[i]; - while (b >= 64) { - crypto_core_salsa20(x, z, k, sigma); - for (i = 0; i < 64; i++) - c[cpos + i] = m[mpos + i] ^ x[i]; - u = 1; - for (i = 8; i < 16; i++) { - u = (u + (z[i] & 0xff)) | 0; - z[i] = u & 0xff; - u >>>= 8; - } - b -= 64; - cpos += 64; - mpos += 64; - } - if (b > 0) { - crypto_core_salsa20(x, z, k, sigma); - for (i = 0; i < b; i++) - c[cpos + i] = m[mpos + i] ^ x[i]; - } - return 0; -} -function crypto_stream_salsa20(c, cpos, b, n, k) { - var z = new Uint8Array(16), x = new Uint8Array(64); - var u, i; - for (i = 0; i < 16; i++) - z[i] = 0; - for (i = 0; i < 8; i++) - z[i] = n[i]; - while (b >= 64) { - crypto_core_salsa20(x, z, k, sigma); - for (i = 0; i < 64; i++) - c[cpos + i] = x[i]; - u = 1; - for (i = 8; i < 16; i++) { - u = (u + (z[i] & 0xff)) | 0; - z[i] = u & 0xff; - u >>>= 8; - } - b -= 64; - cpos += 64; - } - if (b > 0) { - crypto_core_salsa20(x, z, k, sigma); - for (i = 0; i < b; i++) - c[cpos + i] = x[i]; - } - return 0; -} -function crypto_stream(c, cpos, d, n, k) { - var s = new Uint8Array(32); - crypto_core_hsalsa20(s, n, k, sigma); - var sn = new Uint8Array(8); - for (var i = 0; i < 8; i++) - sn[i] = n[i + 16]; - return crypto_stream_salsa20(c, cpos, d, sn, s); -} -function crypto_stream_xor(c, cpos, m, mpos, d, n, k) { - var s = new Uint8Array(32); - crypto_core_hsalsa20(s, n, k, sigma); - var sn = new Uint8Array(8); - for (var i = 0; i < 8; i++) - sn[i] = n[i + 16]; - return crypto_stream_salsa20_xor(c, cpos, m, mpos, d, sn, s); -} -/* - * Port of Andrew Moon's Poly1305-donna-16. Public domain. - * https://github.com/floodyberry/poly1305-donna - */ -class poly1305 { - constructor(key) { - this.buffer = new Uint8Array(16); - this.r = new Uint16Array(10); - this.h = new Uint16Array(10); - this.pad = new Uint16Array(8); - this.leftover = 0; - this.fin = 0; - var t0, t1, t2, t3, t4, t5, t6, t7; - t0 = (key[0] & 0xff) | ((key[1] & 0xff) << 8); - this.r[0] = t0 & 0x1fff; - t1 = (key[2] & 0xff) | ((key[3] & 0xff) << 8); - this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; - t2 = (key[4] & 0xff) | ((key[5] & 0xff) << 8); - this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; - t3 = (key[6] & 0xff) | ((key[7] & 0xff) << 8); - this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; - t4 = (key[8] & 0xff) | ((key[9] & 0xff) << 8); - this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; - this.r[5] = (t4 >>> 1) & 0x1ffe; - t5 = (key[10] & 0xff) | ((key[11] & 0xff) << 8); - this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; - t6 = (key[12] & 0xff) | ((key[13] & 0xff) << 8); - this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; - t7 = (key[14] & 0xff) | ((key[15] & 0xff) << 8); - this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; - this.r[9] = (t7 >>> 5) & 0x007f; - this.pad[0] = (key[16] & 0xff) | ((key[17] & 0xff) << 8); - this.pad[1] = (key[18] & 0xff) | ((key[19] & 0xff) << 8); - this.pad[2] = (key[20] & 0xff) | ((key[21] & 0xff) << 8); - this.pad[3] = (key[22] & 0xff) | ((key[23] & 0xff) << 8); - this.pad[4] = (key[24] & 0xff) | ((key[25] & 0xff) << 8); - this.pad[5] = (key[26] & 0xff) | ((key[27] & 0xff) << 8); - this.pad[6] = (key[28] & 0xff) | ((key[29] & 0xff) << 8); - this.pad[7] = (key[30] & 0xff) | ((key[31] & 0xff) << 8); - } - blocks(m, mpos, bytes) { - var hibit = this.fin ? 0 : 1 << 11; - var t0, t1, t2, t3, t4, t5, t6, t7, c; - var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; - var h0 = this.h[0], h1 = this.h[1], h2 = this.h[2], h3 = this.h[3], h4 = this.h[4], h5 = this.h[5], h6 = this.h[6], h7 = this.h[7], h8 = this.h[8], h9 = this.h[9]; - var r0 = this.r[0], r1 = this.r[1], r2 = this.r[2], r3 = this.r[3], r4 = this.r[4], r5 = this.r[5], r6 = this.r[6], r7 = this.r[7], r8 = this.r[8], r9 = this.r[9]; - while (bytes >= 16) { - t0 = (m[mpos + 0] & 0xff) | ((m[mpos + 1] & 0xff) << 8); - h0 += t0 & 0x1fff; - t1 = (m[mpos + 2] & 0xff) | ((m[mpos + 3] & 0xff) << 8); - h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; - t2 = (m[mpos + 4] & 0xff) | ((m[mpos + 5] & 0xff) << 8); - h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; - t3 = (m[mpos + 6] & 0xff) | ((m[mpos + 7] & 0xff) << 8); - h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; - t4 = (m[mpos + 8] & 0xff) | ((m[mpos + 9] & 0xff) << 8); - h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; - h5 += (t4 >>> 1) & 0x1fff; - t5 = (m[mpos + 10] & 0xff) | ((m[mpos + 11] & 0xff) << 8); - h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; - t6 = (m[mpos + 12] & 0xff) | ((m[mpos + 13] & 0xff) << 8); - h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; - t7 = (m[mpos + 14] & 0xff) | ((m[mpos + 15] & 0xff) << 8); - h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; - h9 += (t7 >>> 5) | hibit; - c = 0; - d0 = c; - d0 += h0 * r0; - d0 += h1 * (5 * r9); - d0 += h2 * (5 * r8); - d0 += h3 * (5 * r7); - d0 += h4 * (5 * r6); - c = d0 >>> 13; - d0 &= 0x1fff; - d0 += h5 * (5 * r5); - d0 += h6 * (5 * r4); - d0 += h7 * (5 * r3); - d0 += h8 * (5 * r2); - d0 += h9 * (5 * r1); - c += d0 >>> 13; - d0 &= 0x1fff; - d1 = c; - d1 += h0 * r1; - d1 += h1 * r0; - d1 += h2 * (5 * r9); - d1 += h3 * (5 * r8); - d1 += h4 * (5 * r7); - c = d1 >>> 13; - d1 &= 0x1fff; - d1 += h5 * (5 * r6); - d1 += h6 * (5 * r5); - d1 += h7 * (5 * r4); - d1 += h8 * (5 * r3); - d1 += h9 * (5 * r2); - c += d1 >>> 13; - d1 &= 0x1fff; - d2 = c; - d2 += h0 * r2; - d2 += h1 * r1; - d2 += h2 * r0; - d2 += h3 * (5 * r9); - d2 += h4 * (5 * r8); - c = d2 >>> 13; - d2 &= 0x1fff; - d2 += h5 * (5 * r7); - d2 += h6 * (5 * r6); - d2 += h7 * (5 * r5); - d2 += h8 * (5 * r4); - d2 += h9 * (5 * r3); - c += d2 >>> 13; - d2 &= 0x1fff; - d3 = c; - d3 += h0 * r3; - d3 += h1 * r2; - d3 += h2 * r1; - d3 += h3 * r0; - d3 += h4 * (5 * r9); - c = d3 >>> 13; - d3 &= 0x1fff; - d3 += h5 * (5 * r8); - d3 += h6 * (5 * r7); - d3 += h7 * (5 * r6); - d3 += h8 * (5 * r5); - d3 += h9 * (5 * r4); - c += d3 >>> 13; - d3 &= 0x1fff; - d4 = c; - d4 += h0 * r4; - d4 += h1 * r3; - d4 += h2 * r2; - d4 += h3 * r1; - d4 += h4 * r0; - c = d4 >>> 13; - d4 &= 0x1fff; - d4 += h5 * (5 * r9); - d4 += h6 * (5 * r8); - d4 += h7 * (5 * r7); - d4 += h8 * (5 * r6); - d4 += h9 * (5 * r5); - c += d4 >>> 13; - d4 &= 0x1fff; - d5 = c; - d5 += h0 * r5; - d5 += h1 * r4; - d5 += h2 * r3; - d5 += h3 * r2; - d5 += h4 * r1; - c = d5 >>> 13; - d5 &= 0x1fff; - d5 += h5 * r0; - d5 += h6 * (5 * r9); - d5 += h7 * (5 * r8); - d5 += h8 * (5 * r7); - d5 += h9 * (5 * r6); - c += d5 >>> 13; - d5 &= 0x1fff; - d6 = c; - d6 += h0 * r6; - d6 += h1 * r5; - d6 += h2 * r4; - d6 += h3 * r3; - d6 += h4 * r2; - c = d6 >>> 13; - d6 &= 0x1fff; - d6 += h5 * r1; - d6 += h6 * r0; - d6 += h7 * (5 * r9); - d6 += h8 * (5 * r8); - d6 += h9 * (5 * r7); - c += d6 >>> 13; - d6 &= 0x1fff; - d7 = c; - d7 += h0 * r7; - d7 += h1 * r6; - d7 += h2 * r5; - d7 += h3 * r4; - d7 += h4 * r3; - c = d7 >>> 13; - d7 &= 0x1fff; - d7 += h5 * r2; - d7 += h6 * r1; - d7 += h7 * r0; - d7 += h8 * (5 * r9); - d7 += h9 * (5 * r8); - c += d7 >>> 13; - d7 &= 0x1fff; - d8 = c; - d8 += h0 * r8; - d8 += h1 * r7; - d8 += h2 * r6; - d8 += h3 * r5; - d8 += h4 * r4; - c = d8 >>> 13; - d8 &= 0x1fff; - d8 += h5 * r3; - d8 += h6 * r2; - d8 += h7 * r1; - d8 += h8 * r0; - d8 += h9 * (5 * r9); - c += d8 >>> 13; - d8 &= 0x1fff; - d9 = c; - d9 += h0 * r9; - d9 += h1 * r8; - d9 += h2 * r7; - d9 += h3 * r6; - d9 += h4 * r5; - c = d9 >>> 13; - d9 &= 0x1fff; - d9 += h5 * r4; - d9 += h6 * r3; - d9 += h7 * r2; - d9 += h8 * r1; - d9 += h9 * r0; - c += d9 >>> 13; - d9 &= 0x1fff; - c = ((c << 2) + c) | 0; - c = (c + d0) | 0; - d0 = c & 0x1fff; - c = c >>> 13; - d1 += c; - h0 = d0; - h1 = d1; - h2 = d2; - h3 = d3; - h4 = d4; - h5 = d5; - h6 = d6; - h7 = d7; - h8 = d8; - h9 = d9; - mpos += 16; - bytes -= 16; - } - this.h[0] = h0; - this.h[1] = h1; - this.h[2] = h2; - this.h[3] = h3; - this.h[4] = h4; - this.h[5] = h5; - this.h[6] = h6; - this.h[7] = h7; - this.h[8] = h8; - this.h[9] = h9; - } - finish(mac, macpos) { - var g = new Uint16Array(10); - var c, mask, f, i; - if (this.leftover) { - i = this.leftover; - this.buffer[i++] = 1; - for (; i < 16; i++) - this.buffer[i] = 0; - this.fin = 1; - this.blocks(this.buffer, 0, 16); - } - c = this.h[1] >>> 13; - this.h[1] &= 0x1fff; - for (i = 2; i < 10; i++) { - this.h[i] += c; - c = this.h[i] >>> 13; - this.h[i] &= 0x1fff; - } - this.h[0] += c * 5; - c = this.h[0] >>> 13; - this.h[0] &= 0x1fff; - this.h[1] += c; - c = this.h[1] >>> 13; - this.h[1] &= 0x1fff; - this.h[2] += c; - g[0] = this.h[0] + 5; - c = g[0] >>> 13; - g[0] &= 0x1fff; - for (i = 1; i < 10; i++) { - g[i] = this.h[i] + c; - c = g[i] >>> 13; - g[i] &= 0x1fff; - } - g[9] -= 1 << 13; - mask = (c ^ 1) - 1; - for (i = 0; i < 10; i++) - g[i] &= mask; - mask = ~mask; - for (i = 0; i < 10; i++) - this.h[i] = (this.h[i] & mask) | g[i]; - this.h[0] = (this.h[0] | (this.h[1] << 13)) & 0xffff; - this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10)) & 0xffff; - this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7)) & 0xffff; - this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4)) & 0xffff; - this.h[4] = - ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; - this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11)) & 0xffff; - this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8)) & 0xffff; - this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5)) & 0xffff; - f = this.h[0] + this.pad[0]; - this.h[0] = f & 0xffff; - for (i = 1; i < 8; i++) { - f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; - this.h[i] = f & 0xffff; - } - mac[macpos + 0] = (this.h[0] >>> 0) & 0xff; - mac[macpos + 1] = (this.h[0] >>> 8) & 0xff; - mac[macpos + 2] = (this.h[1] >>> 0) & 0xff; - mac[macpos + 3] = (this.h[1] >>> 8) & 0xff; - mac[macpos + 4] = (this.h[2] >>> 0) & 0xff; - mac[macpos + 5] = (this.h[2] >>> 8) & 0xff; - mac[macpos + 6] = (this.h[3] >>> 0) & 0xff; - mac[macpos + 7] = (this.h[3] >>> 8) & 0xff; - mac[macpos + 8] = (this.h[4] >>> 0) & 0xff; - mac[macpos + 9] = (this.h[4] >>> 8) & 0xff; - mac[macpos + 10] = (this.h[5] >>> 0) & 0xff; - mac[macpos + 11] = (this.h[5] >>> 8) & 0xff; - mac[macpos + 12] = (this.h[6] >>> 0) & 0xff; - mac[macpos + 13] = (this.h[6] >>> 8) & 0xff; - mac[macpos + 14] = (this.h[7] >>> 0) & 0xff; - mac[macpos + 15] = (this.h[7] >>> 8) & 0xff; - } - update(m, mpos, bytes) { - var i, want; - if (this.leftover) { - want = 16 - this.leftover; - if (want > bytes) - want = bytes; - for (i = 0; i < want; i++) - this.buffer[this.leftover + i] = m[mpos + i]; - bytes -= want; - mpos += want; - this.leftover += want; - if (this.leftover < 16) - return; - this.blocks(this.buffer, 0, 16); - this.leftover = 0; - } - if (bytes >= 16) { - want = bytes - (bytes % 16); - this.blocks(m, mpos, want); - mpos += want; - bytes -= want; - } - if (bytes) { - for (i = 0; i < bytes; i++) - this.buffer[this.leftover + i] = m[mpos + i]; - this.leftover += bytes; - } - } -} -function crypto_onetimeauth(out, outpos, m, mpos, n, k) { - var s = new poly1305(k); - s.update(m, mpos, n); - s.finish(out, outpos); - return 0; -} -function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) { - var x = new Uint8Array(16); - crypto_onetimeauth(x, 0, m, mpos, n, k); - return crypto_verify_16(h, hpos, x, 0); -} -function crypto_secretbox(c, m, d, n, k) { - var i; - if (d < 32) - return -1; - crypto_stream_xor(c, 0, m, 0, d, n, k); - crypto_onetimeauth(c, 16, c, 32, d - 32, c); - for (i = 0; i < 16; i++) - c[i] = 0; - return 0; -} -function crypto_secretbox_open(m, c, d, n, k) { - var i; - var x = new Uint8Array(32); - if (d < 32) - return -1; - crypto_stream(x, 0, 32, n, k); - if (crypto_onetimeauth_verify(c, 16, c, 32, d - 32, x) !== 0) - return -1; - crypto_stream_xor(m, 0, c, 0, d, n, k); - for (i = 0; i < 32; i++) - m[i] = 0; - return 0; -} -function set25519(r, a) { - var i; - for (i = 0; i < 16; i++) - r[i] = a[i] | 0; -} -function car25519(o) { - var i, v, c = 1; - for (i = 0; i < 16; i++) { - v = o[i] + c + 65535; - c = Math.floor(v / 65536); - o[i] = v - c * 65536; - } - o[0] += c - 1 + 37 * (c - 1); -} -function sel25519(p, q, b) { - var t, c = ~(b - 1); - for (var i = 0; i < 16; i++) { - t = c & (p[i] ^ q[i]); - p[i] ^= t; - q[i] ^= t; - } -} -function pack25519(o, n) { - var i, j, b; - var m = gf(), t = gf(); - for (i = 0; i < 16; i++) - t[i] = n[i]; - car25519(t); - car25519(t); - car25519(t); - for (j = 0; j < 2; j++) { - m[0] = t[0] - 0xffed; - for (i = 1; i < 15; i++) { - m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); - m[i - 1] &= 0xffff; - } - m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); - b = (m[15] >> 16) & 1; - m[14] &= 0xffff; - sel25519(t, m, 1 - b); - } - for (i = 0; i < 16; i++) { - o[2 * i] = t[i] & 0xff; - o[2 * i + 1] = t[i] >> 8; - } -} -function neq25519(a, b) { - var c = new Uint8Array(32), d = new Uint8Array(32); - pack25519(c, a); - pack25519(d, b); - return crypto_verify_32(c, 0, d, 0); -} -function par25519(a) { - var d = new Uint8Array(32); - pack25519(d, a); - return d[0] & 1; -} -function unpack25519(o, n) { - var i; - for (i = 0; i < 16; i++) - o[i] = n[2 * i] + (n[2 * i + 1] << 8); - o[15] &= 0x7fff; -} -function A(o, a, b) { - for (var i = 0; i < 16; i++) - o[i] = a[i] + b[i]; -} -function Z(o, a, b) { - for (var i = 0; i < 16; i++) - o[i] = a[i] - b[i]; -} -function M(o, a, b) { - var v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; - v = a[0]; - t0 += v * b0; - t1 += v * b1; - t2 += v * b2; - t3 += v * b3; - t4 += v * b4; - t5 += v * b5; - t6 += v * b6; - t7 += v * b7; - t8 += v * b8; - t9 += v * b9; - t10 += v * b10; - t11 += v * b11; - t12 += v * b12; - t13 += v * b13; - t14 += v * b14; - t15 += v * b15; - v = a[1]; - t1 += v * b0; - t2 += v * b1; - t3 += v * b2; - t4 += v * b3; - t5 += v * b4; - t6 += v * b5; - t7 += v * b6; - t8 += v * b7; - t9 += v * b8; - t10 += v * b9; - t11 += v * b10; - t12 += v * b11; - t13 += v * b12; - t14 += v * b13; - t15 += v * b14; - t16 += v * b15; - v = a[2]; - t2 += v * b0; - t3 += v * b1; - t4 += v * b2; - t5 += v * b3; - t6 += v * b4; - t7 += v * b5; - t8 += v * b6; - t9 += v * b7; - t10 += v * b8; - t11 += v * b9; - t12 += v * b10; - t13 += v * b11; - t14 += v * b12; - t15 += v * b13; - t16 += v * b14; - t17 += v * b15; - v = a[3]; - t3 += v * b0; - t4 += v * b1; - t5 += v * b2; - t6 += v * b3; - t7 += v * b4; - t8 += v * b5; - t9 += v * b6; - t10 += v * b7; - t11 += v * b8; - t12 += v * b9; - t13 += v * b10; - t14 += v * b11; - t15 += v * b12; - t16 += v * b13; - t17 += v * b14; - t18 += v * b15; - v = a[4]; - t4 += v * b0; - t5 += v * b1; - t6 += v * b2; - t7 += v * b3; - t8 += v * b4; - t9 += v * b5; - t10 += v * b6; - t11 += v * b7; - t12 += v * b8; - t13 += v * b9; - t14 += v * b10; - t15 += v * b11; - t16 += v * b12; - t17 += v * b13; - t18 += v * b14; - t19 += v * b15; - v = a[5]; - t5 += v * b0; - t6 += v * b1; - t7 += v * b2; - t8 += v * b3; - t9 += v * b4; - t10 += v * b5; - t11 += v * b6; - t12 += v * b7; - t13 += v * b8; - t14 += v * b9; - t15 += v * b10; - t16 += v * b11; - t17 += v * b12; - t18 += v * b13; - t19 += v * b14; - t20 += v * b15; - v = a[6]; - t6 += v * b0; - t7 += v * b1; - t8 += v * b2; - t9 += v * b3; - t10 += v * b4; - t11 += v * b5; - t12 += v * b6; - t13 += v * b7; - t14 += v * b8; - t15 += v * b9; - t16 += v * b10; - t17 += v * b11; - t18 += v * b12; - t19 += v * b13; - t20 += v * b14; - t21 += v * b15; - v = a[7]; - t7 += v * b0; - t8 += v * b1; - t9 += v * b2; - t10 += v * b3; - t11 += v * b4; - t12 += v * b5; - t13 += v * b6; - t14 += v * b7; - t15 += v * b8; - t16 += v * b9; - t17 += v * b10; - t18 += v * b11; - t19 += v * b12; - t20 += v * b13; - t21 += v * b14; - t22 += v * b15; - v = a[8]; - t8 += v * b0; - t9 += v * b1; - t10 += v * b2; - t11 += v * b3; - t12 += v * b4; - t13 += v * b5; - t14 += v * b6; - t15 += v * b7; - t16 += v * b8; - t17 += v * b9; - t18 += v * b10; - t19 += v * b11; - t20 += v * b12; - t21 += v * b13; - t22 += v * b14; - t23 += v * b15; - v = a[9]; - t9 += v * b0; - t10 += v * b1; - t11 += v * b2; - t12 += v * b3; - t13 += v * b4; - t14 += v * b5; - t15 += v * b6; - t16 += v * b7; - t17 += v * b8; - t18 += v * b9; - t19 += v * b10; - t20 += v * b11; - t21 += v * b12; - t22 += v * b13; - t23 += v * b14; - t24 += v * b15; - v = a[10]; - t10 += v * b0; - t11 += v * b1; - t12 += v * b2; - t13 += v * b3; - t14 += v * b4; - t15 += v * b5; - t16 += v * b6; - t17 += v * b7; - t18 += v * b8; - t19 += v * b9; - t20 += v * b10; - t21 += v * b11; - t22 += v * b12; - t23 += v * b13; - t24 += v * b14; - t25 += v * b15; - v = a[11]; - t11 += v * b0; - t12 += v * b1; - t13 += v * b2; - t14 += v * b3; - t15 += v * b4; - t16 += v * b5; - t17 += v * b6; - t18 += v * b7; - t19 += v * b8; - t20 += v * b9; - t21 += v * b10; - t22 += v * b11; - t23 += v * b12; - t24 += v * b13; - t25 += v * b14; - t26 += v * b15; - v = a[12]; - t12 += v * b0; - t13 += v * b1; - t14 += v * b2; - t15 += v * b3; - t16 += v * b4; - t17 += v * b5; - t18 += v * b6; - t19 += v * b7; - t20 += v * b8; - t21 += v * b9; - t22 += v * b10; - t23 += v * b11; - t24 += v * b12; - t25 += v * b13; - t26 += v * b14; - t27 += v * b15; - v = a[13]; - t13 += v * b0; - t14 += v * b1; - t15 += v * b2; - t16 += v * b3; - t17 += v * b4; - t18 += v * b5; - t19 += v * b6; - t20 += v * b7; - t21 += v * b8; - t22 += v * b9; - t23 += v * b10; - t24 += v * b11; - t25 += v * b12; - t26 += v * b13; - t27 += v * b14; - t28 += v * b15; - v = a[14]; - t14 += v * b0; - t15 += v * b1; - t16 += v * b2; - t17 += v * b3; - t18 += v * b4; - t19 += v * b5; - t20 += v * b6; - t21 += v * b7; - t22 += v * b8; - t23 += v * b9; - t24 += v * b10; - t25 += v * b11; - t26 += v * b12; - t27 += v * b13; - t28 += v * b14; - t29 += v * b15; - v = a[15]; - t15 += v * b0; - t16 += v * b1; - t17 += v * b2; - t18 += v * b3; - t19 += v * b4; - t20 += v * b5; - t21 += v * b6; - t22 += v * b7; - t23 += v * b8; - t24 += v * b9; - t25 += v * b10; - t26 += v * b11; - t27 += v * b12; - t28 += v * b13; - t29 += v * b14; - t30 += v * b15; - t0 += 38 * t16; - t1 += 38 * t17; - t2 += 38 * t18; - t3 += 38 * t19; - t4 += 38 * t20; - t5 += 38 * t21; - t6 += 38 * t22; - t7 += 38 * t23; - t8 += 38 * t24; - t9 += 38 * t25; - t10 += 38 * t26; - t11 += 38 * t27; - t12 += 38 * t28; - t13 += 38 * t29; - t14 += 38 * t30; - // t15 left as is - // first car - c = 1; - v = t0 + c + 65535; - c = Math.floor(v / 65536); - t0 = v - c * 65536; - v = t1 + c + 65535; - c = Math.floor(v / 65536); - t1 = v - c * 65536; - v = t2 + c + 65535; - c = Math.floor(v / 65536); - t2 = v - c * 65536; - v = t3 + c + 65535; - c = Math.floor(v / 65536); - t3 = v - c * 65536; - v = t4 + c + 65535; - c = Math.floor(v / 65536); - t4 = v - c * 65536; - v = t5 + c + 65535; - c = Math.floor(v / 65536); - t5 = v - c * 65536; - v = t6 + c + 65535; - c = Math.floor(v / 65536); - t6 = v - c * 65536; - v = t7 + c + 65535; - c = Math.floor(v / 65536); - t7 = v - c * 65536; - v = t8 + c + 65535; - c = Math.floor(v / 65536); - t8 = v - c * 65536; - v = t9 + c + 65535; - c = Math.floor(v / 65536); - t9 = v - c * 65536; - v = t10 + c + 65535; - c = Math.floor(v / 65536); - t10 = v - c * 65536; - v = t11 + c + 65535; - c = Math.floor(v / 65536); - t11 = v - c * 65536; - v = t12 + c + 65535; - c = Math.floor(v / 65536); - t12 = v - c * 65536; - v = t13 + c + 65535; - c = Math.floor(v / 65536); - t13 = v - c * 65536; - v = t14 + c + 65535; - c = Math.floor(v / 65536); - t14 = v - c * 65536; - v = t15 + c + 65535; - c = Math.floor(v / 65536); - t15 = v - c * 65536; - t0 += c - 1 + 37 * (c - 1); - // second car - c = 1; - v = t0 + c + 65535; - c = Math.floor(v / 65536); - t0 = v - c * 65536; - v = t1 + c + 65535; - c = Math.floor(v / 65536); - t1 = v - c * 65536; - v = t2 + c + 65535; - c = Math.floor(v / 65536); - t2 = v - c * 65536; - v = t3 + c + 65535; - c = Math.floor(v / 65536); - t3 = v - c * 65536; - v = t4 + c + 65535; - c = Math.floor(v / 65536); - t4 = v - c * 65536; - v = t5 + c + 65535; - c = Math.floor(v / 65536); - t5 = v - c * 65536; - v = t6 + c + 65535; - c = Math.floor(v / 65536); - t6 = v - c * 65536; - v = t7 + c + 65535; - c = Math.floor(v / 65536); - t7 = v - c * 65536; - v = t8 + c + 65535; - c = Math.floor(v / 65536); - t8 = v - c * 65536; - v = t9 + c + 65535; - c = Math.floor(v / 65536); - t9 = v - c * 65536; - v = t10 + c + 65535; - c = Math.floor(v / 65536); - t10 = v - c * 65536; - v = t11 + c + 65535; - c = Math.floor(v / 65536); - t11 = v - c * 65536; - v = t12 + c + 65535; - c = Math.floor(v / 65536); - t12 = v - c * 65536; - v = t13 + c + 65535; - c = Math.floor(v / 65536); - t13 = v - c * 65536; - v = t14 + c + 65535; - c = Math.floor(v / 65536); - t14 = v - c * 65536; - v = t15 + c + 65535; - c = Math.floor(v / 65536); - t15 = v - c * 65536; - t0 += c - 1 + 37 * (c - 1); - o[0] = t0; - o[1] = t1; - o[2] = t2; - o[3] = t3; - o[4] = t4; - o[5] = t5; - o[6] = t6; - o[7] = t7; - o[8] = t8; - o[9] = t9; - o[10] = t10; - o[11] = t11; - o[12] = t12; - o[13] = t13; - o[14] = t14; - o[15] = t15; -} -function S(o, a) { - M(o, a, a); -} -function inv25519(o, i) { - var c = gf(); - var a; - for (a = 0; a < 16; a++) - c[a] = i[a]; - for (a = 253; a >= 0; a--) { - S(c, c); - if (a !== 2 && a !== 4) - M(c, c, i); - } - for (a = 0; a < 16; a++) - o[a] = c[a]; -} -function pow2523(o, i) { - var c = gf(); - var a; - for (a = 0; a < 16; a++) - c[a] = i[a]; - for (a = 250; a >= 0; a--) { - S(c, c); - if (a !== 1) - M(c, c, i); - } - for (a = 0; a < 16; a++) - o[a] = c[a]; -} -function crypto_scalarmult(q, n, p) { - var z = new Uint8Array(32); - var x = new Float64Array(80), r, i; - var a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(); - for (i = 0; i < 31; i++) - z[i] = n[i]; - z[31] = (n[31] & 127) | 64; - z[0] &= 248; - unpack25519(x, p); - for (i = 0; i < 16; i++) { - b[i] = x[i]; - d[i] = a[i] = c[i] = 0; - } - a[0] = d[0] = 1; - for (i = 254; i >= 0; --i) { - r = (z[i >>> 3] >>> (i & 7)) & 1; - sel25519(a, b, r); - sel25519(c, d, r); - A(e, a, c); - Z(a, a, c); - A(c, b, d); - Z(b, b, d); - S(d, e); - S(f, a); - M(a, c, a); - M(c, b, e); - A(e, a, c); - Z(a, a, c); - S(b, a); - Z(c, d, f); - M(a, c, _121665); - A(a, a, d); - M(c, c, a); - M(a, d, f); - M(d, b, x); - S(b, e); - sel25519(a, b, r); - sel25519(c, d, r); - } - for (i = 0; i < 16; i++) { - x[i + 16] = a[i]; - x[i + 32] = c[i]; - x[i + 48] = b[i]; - x[i + 64] = d[i]; - } - var x32 = x.subarray(32); - var x16 = x.subarray(16); - inv25519(x32, x32); - M(x16, x16, x32); - pack25519(q, x16); - return 0; -} -function crypto_scalarmult_base(q, n) { - return crypto_scalarmult(q, n, _9); -} -function crypto_box_keypair(y, x) { - randombytes(x, 32); - return crypto_scalarmult_base(y, x); -} -function crypto_box_beforenm(k, y, x) { - var s = new Uint8Array(32); - crypto_scalarmult(s, x, y); - return crypto_core_hsalsa20(k, _0, s, sigma); -} -// prettier-ignore -var K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, - 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, - 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, - 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, - 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, - 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, - 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, - 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, - 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, - 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, - 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, - 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, - 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, - 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, - 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, - 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, - 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, - 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, - 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, - 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, - 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, - 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, - 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, - 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, - 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, - 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, - 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, - 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, - 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, - 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, - 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, - 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, - 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, - 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, - 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, - 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -]; -function crypto_hashblocks_hl(hh, hl, m, n) { - var wh = new Int32Array(16), wl = new Int32Array(16), bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, th, tl, i, j, h, l, a, b, c, d; - var ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7]; - var pos = 0; - while (n >= 128) { - for (i = 0; i < 16; i++) { - j = 8 * i + pos; - wh[i] = (m[j + 0] << 24) | (m[j + 1] << 16) | (m[j + 2] << 8) | m[j + 3]; - wl[i] = (m[j + 4] << 24) | (m[j + 5] << 16) | (m[j + 6] << 8) | m[j + 7]; - } - for (i = 0; i < 80; i++) { - bh0 = ah0; - bh1 = ah1; - bh2 = ah2; - bh3 = ah3; - bh4 = ah4; - bh5 = ah5; - bh6 = ah6; - bh7 = ah7; - bl0 = al0; - bl1 = al1; - bl2 = al2; - bl3 = al3; - bl4 = al4; - bl5 = al5; - bl6 = al6; - bl7 = al7; - // add - h = ah7; - l = al7; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - // Sigma1 - h = - ((ah4 >>> 14) | (al4 << (32 - 14))) ^ - ((ah4 >>> 18) | (al4 << (32 - 18))) ^ - ((al4 >>> (41 - 32)) | (ah4 << (32 - (41 - 32)))); - l = - ((al4 >>> 14) | (ah4 << (32 - 14))) ^ - ((al4 >>> 18) | (ah4 << (32 - 18))) ^ - ((ah4 >>> (41 - 32)) | (al4 << (32 - (41 - 32)))); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // Ch - h = (ah4 & ah5) ^ (~ah4 & ah6); - l = (al4 & al5) ^ (~al4 & al6); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // K - h = K[i * 2]; - l = K[i * 2 + 1]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // w - h = wh[i % 16]; - l = wl[i % 16]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - th = (c & 0xffff) | (d << 16); - tl = (a & 0xffff) | (b << 16); - // add - h = th; - l = tl; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - // Sigma0 - h = - ((ah0 >>> 28) | (al0 << (32 - 28))) ^ - ((al0 >>> (34 - 32)) | (ah0 << (32 - (34 - 32)))) ^ - ((al0 >>> (39 - 32)) | (ah0 << (32 - (39 - 32)))); - l = - ((al0 >>> 28) | (ah0 << (32 - 28))) ^ - ((ah0 >>> (34 - 32)) | (al0 << (32 - (34 - 32)))) ^ - ((ah0 >>> (39 - 32)) | (al0 << (32 - (39 - 32)))); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // Maj - h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); - l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - bh7 = (c & 0xffff) | (d << 16); - bl7 = (a & 0xffff) | (b << 16); - // add - h = bh3; - l = bl3; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = th; - l = tl; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - bh3 = (c & 0xffff) | (d << 16); - bl3 = (a & 0xffff) | (b << 16); - ah1 = bh0; - ah2 = bh1; - ah3 = bh2; - ah4 = bh3; - ah5 = bh4; - ah6 = bh5; - ah7 = bh6; - ah0 = bh7; - al1 = bl0; - al2 = bl1; - al3 = bl2; - al4 = bl3; - al5 = bl4; - al6 = bl5; - al7 = bl6; - al0 = bl7; - if (i % 16 === 15) { - for (j = 0; j < 16; j++) { - // add - h = wh[j]; - l = wl[j]; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = wh[(j + 9) % 16]; - l = wl[(j + 9) % 16]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // sigma0 - th = wh[(j + 1) % 16]; - tl = wl[(j + 1) % 16]; - h = - ((th >>> 1) | (tl << (32 - 1))) ^ - ((th >>> 8) | (tl << (32 - 8))) ^ - (th >>> 7); - l = - ((tl >>> 1) | (th << (32 - 1))) ^ - ((tl >>> 8) | (th << (32 - 8))) ^ - ((tl >>> 7) | (th << (32 - 7))); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - // sigma1 - th = wh[(j + 14) % 16]; - tl = wl[(j + 14) % 16]; - h = - ((th >>> 19) | (tl << (32 - 19))) ^ - ((tl >>> (61 - 32)) | (th << (32 - (61 - 32)))) ^ - (th >>> 6); - l = - ((tl >>> 19) | (th << (32 - 19))) ^ - ((th >>> (61 - 32)) | (tl << (32 - (61 - 32)))) ^ - ((tl >>> 6) | (th << (32 - 6))); - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - wh[j] = (c & 0xffff) | (d << 16); - wl[j] = (a & 0xffff) | (b << 16); - } - } - } - // add - h = ah0; - l = al0; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[0]; - l = hl[0]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[0] = ah0 = (c & 0xffff) | (d << 16); - hl[0] = al0 = (a & 0xffff) | (b << 16); - h = ah1; - l = al1; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[1]; - l = hl[1]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[1] = ah1 = (c & 0xffff) | (d << 16); - hl[1] = al1 = (a & 0xffff) | (b << 16); - h = ah2; - l = al2; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[2]; - l = hl[2]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[2] = ah2 = (c & 0xffff) | (d << 16); - hl[2] = al2 = (a & 0xffff) | (b << 16); - h = ah3; - l = al3; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[3]; - l = hl[3]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[3] = ah3 = (c & 0xffff) | (d << 16); - hl[3] = al3 = (a & 0xffff) | (b << 16); - h = ah4; - l = al4; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[4]; - l = hl[4]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[4] = ah4 = (c & 0xffff) | (d << 16); - hl[4] = al4 = (a & 0xffff) | (b << 16); - h = ah5; - l = al5; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[5]; - l = hl[5]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[5] = ah5 = (c & 0xffff) | (d << 16); - hl[5] = al5 = (a & 0xffff) | (b << 16); - h = ah6; - l = al6; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[6]; - l = hl[6]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[6] = ah6 = (c & 0xffff) | (d << 16); - hl[6] = al6 = (a & 0xffff) | (b << 16); - h = ah7; - l = al7; - a = l & 0xffff; - b = l >>> 16; - c = h & 0xffff; - d = h >>> 16; - h = hh[7]; - l = hl[7]; - a += l & 0xffff; - b += l >>> 16; - c += h & 0xffff; - d += h >>> 16; - b += a >>> 16; - c += b >>> 16; - d += c >>> 16; - hh[7] = ah7 = (c & 0xffff) | (d << 16); - hl[7] = al7 = (a & 0xffff) | (b << 16); - pos += 128; - n -= 128; - } - return n; -} -function crypto_hash(out, m, n) { - const hh = new Int32Array(8); - const hl = new Int32Array(8); - const x = new Uint8Array(256); - let b = n; - hh[0] = 0x6a09e667; - hh[1] = 0xbb67ae85; - hh[2] = 0x3c6ef372; - hh[3] = 0xa54ff53a; - hh[4] = 0x510e527f; - hh[5] = 0x9b05688c; - hh[6] = 0x1f83d9ab; - hh[7] = 0x5be0cd19; - hl[0] = 0xf3bcc908; - hl[1] = 0x84caa73b; - hl[2] = 0xfe94f82b; - hl[3] = 0x5f1d36f1; - hl[4] = 0xade682d1; - hl[5] = 0x2b3e6c1f; - hl[6] = 0xfb41bd6b; - hl[7] = 0x137e2179; - crypto_hashblocks_hl(hh, hl, m, n); - n %= 128; - for (let i = 0; i < n; i++) - x[i] = m[b - n + i]; - x[n] = 128; - n = 256 - 128 * (n < 112 ? 1 : 0); - x[n - 9] = 0; - ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); - crypto_hashblocks_hl(hh, hl, x, n); - for (let i = 0; i < 8; i++) - ts64(out, 8 * i, hh[i], hl[i]); - return 0; -} -/** - * Incremental version of crypto_hash. - */ -class HashState { - constructor() { - this.hh = new Int32Array(8); - this.hl = new Int32Array(8); - this.next = new Uint8Array(128); - this.p = 0; - this.total = 0; - this.hh[0] = 0x6a09e667; - this.hh[1] = 0xbb67ae85; - this.hh[2] = 0x3c6ef372; - this.hh[3] = 0xa54ff53a; - this.hh[4] = 0x510e527f; - this.hh[5] = 0x9b05688c; - this.hh[6] = 0x1f83d9ab; - this.hh[7] = 0x5be0cd19; - this.hl[0] = 0xf3bcc908; - this.hl[1] = 0x84caa73b; - this.hl[2] = 0xfe94f82b; - this.hl[3] = 0x5f1d36f1; - this.hl[4] = 0xade682d1; - this.hl[5] = 0x2b3e6c1f; - this.hl[6] = 0xfb41bd6b; - this.hl[7] = 0x137e2179; - } - update(data) { - this.total += data.length; - let i = 0; - while (i < data.length) { - const r = 128 - this.p; - if (r > (data.length - i)) { - for (let j = 0; i + j < data.length; j++) { - this.next[this.p + j] = data[i + j]; - } - this.p += data.length - i; - break; - } - else { - for (let j = 0; this.p + j < 128; j++) { - this.next[this.p + j] = data[i + j]; - } - crypto_hashblocks_hl(this.hh, this.hl, this.next, 128); - i += 128 - this.p; - this.p = 0; - } - } - return this; - } - finish() { - const out = new Uint8Array(64); - let n = this.p; - const x = new Uint8Array(256); - let b = this.total; - for (let i = 0; i < n; i++) - x[i] = this.next[i]; - x[n] = 128; - n = 256 - 128 * (n < 112 ? 1 : 0); - x[n - 9] = 0; - ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); - crypto_hashblocks_hl(this.hh, this.hl, x, n); - for (let i = 0; i < 8; i++) - ts64(out, 8 * i, this.hh[i], this.hl[i]); - return out; - } -} -exports.HashState = HashState; -function add(p, q) { - var a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = gf(), h = gf(), t = gf(); - Z(a, p[1], p[0]); - Z(t, q[1], q[0]); - M(a, a, t); - A(b, p[0], p[1]); - A(t, q[0], q[1]); - M(b, b, t); - M(c, p[3], q[3]); - M(c, c, D2); - M(d, p[2], q[2]); - A(d, d, d); - Z(e, b, a); - Z(f, d, c); - A(g, d, c); - A(h, b, a); - M(p[0], e, f); - M(p[1], h, g); - M(p[2], g, f); - M(p[3], e, h); -} -function cswap(p, q, b) { - var i; - for (i = 0; i < 4; i++) { - sel25519(p[i], q[i], b); - } -} -function pack(r, p) { - var tx = gf(), ty = gf(), zi = gf(); - inv25519(zi, p[2]); - M(tx, p[0], zi); - M(ty, p[1], zi); - pack25519(r, ty); - r[31] ^= par25519(tx) << 7; -} -function scalarmult(p, q, s) { - var b, i; - set25519(p[0], gf0); - set25519(p[1], gf1); - set25519(p[2], gf1); - set25519(p[3], gf0); - for (i = 255; i >= 0; --i) { - b = (s[(i / 8) | 0] >> (i & 7)) & 1; - cswap(p, q, b); - add(q, p); - add(p, p); - cswap(p, q, b); - } -} -function scalarbase(p, s) { - const q = [gf(), gf(), gf(), gf()]; - set25519(q[0], X); - set25519(q[1], Y); - set25519(q[2], gf1); - M(q[3], X, Y); - scalarmult(p, q, s); -} -function crypto_sign_keypair(pk, sk, seeded) { - const d = new Uint8Array(64); - const p = [gf(), gf(), gf(), gf()]; - if (!seeded) - randombytes(sk, 32); - crypto_hash(d, sk, 32); - d[0] &= 248; - d[31] &= 127; - d[31] |= 64; - scalarbase(p, d); - pack(pk, p); - for (let i = 0; i < 32; i++) - sk[i + 32] = pk[i]; - return 0; -} -var L = new Float64Array([ - 0xed, - 0xd3, - 0xf5, - 0x5c, - 0x1a, - 0x63, - 0x12, - 0x58, - 0xd6, - 0x9c, - 0xf7, - 0xa2, - 0xde, - 0xf9, - 0xde, - 0x14, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0x10, -]); -function modL(r, x) { - var carry, i, j, k; - for (i = 63; i >= 32; --i) { - carry = 0; - for (j = i - 32, k = i - 12; j < k; ++j) { - x[j] += carry - 16 * x[i] * L[j - (i - 32)]; - carry = (x[j] + 128) >> 8; - x[j] -= carry * 256; - } - x[j] += carry; - x[i] = 0; - } - carry = 0; - for (j = 0; j < 32; j++) { - x[j] += carry - (x[31] >> 4) * L[j]; - carry = x[j] >> 8; - x[j] &= 255; - } - for (j = 0; j < 32; j++) - x[j] -= carry * L[j]; - for (i = 0; i < 32; i++) { - x[i + 1] += x[i] >> 8; - r[i] = x[i] & 255; - } -} -function reduce(r) { - const x = new Float64Array(64); - for (let i = 0; i < 64; i++) - x[i] = r[i]; - for (let i = 0; i < 64; i++) - r[i] = 0; - modL(r, x); -} -// Note: difference from C - smlen returned, not passed as argument. -function crypto_sign(sm, m, n, sk) { - var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); - var i, j, x = new Float64Array(64); - var p = [gf(), gf(), gf(), gf()]; - crypto_hash(d, sk, 32); - d[0] &= 248; - d[31] &= 127; - d[31] |= 64; - var smlen = n + 64; - for (i = 0; i < n; i++) - sm[64 + i] = m[i]; - for (i = 0; i < 32; i++) - sm[32 + i] = d[32 + i]; - crypto_hash(r, sm.subarray(32), n + 32); - reduce(r); - scalarbase(p, r); - pack(sm, p); - for (i = 32; i < 64; i++) - sm[i] = sk[i]; - crypto_hash(h, sm, n + 64); - reduce(h); - for (i = 0; i < 64; i++) - x[i] = 0; - for (i = 0; i < 32; i++) - x[i] = r[i]; - for (i = 0; i < 32; i++) { - for (j = 0; j < 32; j++) { - x[i + j] += h[i] * d[j]; - } - } - modL(sm.subarray(32), x); - return smlen; -} -function unpackneg(r, p) { - const t = gf(); - const chk = gf(); - const num = gf(); - const den = gf(); - const den2 = gf(); - const den4 = gf(); - const den6 = gf(); - set25519(r[2], gf1); - unpack25519(r[1], p); - S(num, r[1]); - M(den, num, D); - Z(num, num, r[2]); - A(den, r[2], den); - S(den2, den); - S(den4, den2); - M(den6, den4, den2); - M(t, den6, num); - M(t, t, den); - pow2523(t, t); - M(t, t, num); - M(t, t, den); - M(t, t, den); - M(r[0], t, den); - S(chk, r[0]); - M(chk, chk, den); - if (neq25519(chk, num)) - M(r[0], r[0], I); - S(chk, r[0]); - M(chk, chk, den); - if (neq25519(chk, num)) - return -1; - if (par25519(r[0]) === p[31] >> 7) - Z(r[0], gf0, r[0]); - M(r[3], r[0], r[1]); - return 0; -} -function crypto_sign_open(m, sm, n, pk) { - var i, mlen; - var t = new Uint8Array(32), h = new Uint8Array(64); - var p = [gf(), gf(), gf(), gf()], q = [gf(), gf(), gf(), gf()]; - mlen = -1; - if (n < 64) - return -1; - if (unpackneg(q, pk)) - return -1; - for (i = 0; i < n; i++) - m[i] = sm[i]; - for (i = 0; i < 32; i++) - m[i + 32] = pk[i]; - crypto_hash(h, m, n); - reduce(h); - scalarmult(p, q, h); - scalarbase(q, sm.subarray(32)); - add(p, q); - pack(t, p); - n -= 64; - if (crypto_verify_32(sm, 0, t, 0)) { - for (i = 0; i < n; i++) - m[i] = 0; - return -1; - } - for (i = 0; i < n; i++) - m[i] = sm[i + 64]; - mlen = n; - return mlen; -} -var crypto_secretbox_KEYBYTES = 32, crypto_secretbox_NONCEBYTES = 24, crypto_secretbox_ZEROBYTES = 32, crypto_secretbox_BOXZEROBYTES = 16, crypto_scalarmult_BYTES = 32, crypto_scalarmult_SCALARBYTES = 32, crypto_box_PUBLICKEYBYTES = 32, crypto_box_SECRETKEYBYTES = 32, crypto_box_BEFORENMBYTES = 32, crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, crypto_sign_BYTES = 64, crypto_sign_PUBLICKEYBYTES = 32, crypto_sign_SECRETKEYBYTES = 64, crypto_sign_SEEDBYTES = 32, crypto_hash_BYTES = 64; -/* High-level API */ -function checkLengths(k, n) { - if (k.length !== crypto_secretbox_KEYBYTES) - throw new Error("bad key size"); - if (n.length !== crypto_secretbox_NONCEBYTES) - throw new Error("bad nonce size"); -} -function checkBoxLengths(pk, sk) { - if (pk.length !== crypto_box_PUBLICKEYBYTES) - throw new Error("bad public key size"); - if (sk.length !== crypto_box_SECRETKEYBYTES) - throw new Error("bad secret key size"); -} -function checkArrayTypes(...args) { - for (var i = 0; i < args.length; i++) { - if (!(args[i] instanceof Uint8Array)) - throw new TypeError("unexpected type, use Uint8Array"); - } -} -function cleanup(arr) { - for (var i = 0; i < arr.length; i++) - arr[i] = 0; -} -function randomBytes(n) { - var b = new Uint8Array(n); - randombytes(b, n); - return b; -} -exports.randomBytes = randomBytes; -function secretbox(msg, nonce, key) { - checkArrayTypes(msg, nonce, key); - checkLengths(key, nonce); - var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length); - var c = new Uint8Array(m.length); - for (var i = 0; i < msg.length; i++) - m[i + crypto_secretbox_ZEROBYTES] = msg[i]; - crypto_secretbox(c, m, m.length, nonce, key); - return c.subarray(crypto_secretbox_BOXZEROBYTES); -} -exports.secretbox = secretbox; -function secretbox_open(box, nonce, key) { - checkArrayTypes(box, nonce, key); - checkLengths(key, nonce); - var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length); - var m = new Uint8Array(c.length); - for (var i = 0; i < box.length; i++) - c[i + crypto_secretbox_BOXZEROBYTES] = box[i]; - if (c.length < 32) - return null; - if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) - return null; - return m.subarray(crypto_secretbox_ZEROBYTES); -} -exports.secretbox_open = secretbox_open; -exports.secretbox_keyLength = crypto_secretbox_KEYBYTES; -exports.secretbox_nonceLength = crypto_secretbox_NONCEBYTES; -exports.secretbox_overheadLength = crypto_secretbox_BOXZEROBYTES; -function scalarMult(n, p) { - checkArrayTypes(n, p); - if (n.length !== crypto_scalarmult_SCALARBYTES) - throw new Error("bad n size"); - if (p.length !== crypto_scalarmult_BYTES) - throw new Error("bad p size"); - var q = new Uint8Array(crypto_scalarmult_BYTES); - crypto_scalarmult(q, n, p); - return q; -} -exports.scalarMult = scalarMult; -function scalarMult_base(n) { - checkArrayTypes(n); - if (n.length !== crypto_scalarmult_SCALARBYTES) - throw new Error("bad n size"); - var q = new Uint8Array(crypto_scalarmult_BYTES); - crypto_scalarmult_base(q, n); - return q; -} -exports.scalarMult_base = scalarMult_base; -exports.scalarMult_scalarLength = crypto_scalarmult_SCALARBYTES; -exports.scalarMult_groupElementLength = crypto_scalarmult_BYTES; -function box(msg, nonce, publicKey, secretKey) { - var k = box_before(publicKey, secretKey); - return secretbox(msg, nonce, k); -} -exports.box = box; -function box_before(publicKey, secretKey) { - checkArrayTypes(publicKey, secretKey); - checkBoxLengths(publicKey, secretKey); - var k = new Uint8Array(crypto_box_BEFORENMBYTES); - crypto_box_beforenm(k, publicKey, secretKey); - return k; -} -exports.box_before = box_before; -exports.box_after = secretbox; -function box_open(msg, nonce, publicKey, secretKey) { - var k = box_before(publicKey, secretKey); - return secretbox_open(msg, nonce, k); -} -exports.box_open = box_open; -exports.box_open_after = secretbox_open; -function box_keyPair() { - var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); - var sk = new Uint8Array(crypto_box_SECRETKEYBYTES); - crypto_box_keypair(pk, sk); - return { publicKey: pk, secretKey: sk }; -} -exports.box_keyPair = box_keyPair; -function box_keyPair_fromSecretKey(secretKey) { - checkArrayTypes(secretKey); - if (secretKey.length !== crypto_box_SECRETKEYBYTES) - throw new Error("bad secret key size"); - var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); - crypto_scalarmult_base(pk, secretKey); - return { publicKey: pk, secretKey: new Uint8Array(secretKey) }; -} -exports.box_keyPair_fromSecretKey = box_keyPair_fromSecretKey; -exports.box_publicKeyLength = crypto_box_PUBLICKEYBYTES; -exports.box_secretKeyLength = crypto_box_SECRETKEYBYTES; -exports.box_sharedKeyLength = crypto_box_BEFORENMBYTES; -exports.box_nonceLength = crypto_box_NONCEBYTES; -exports.box_overheadLength = exports.secretbox_overheadLength; -function sign(msg, secretKey) { - checkArrayTypes(msg, secretKey); - if (secretKey.length !== crypto_sign_SECRETKEYBYTES) - throw new Error("bad secret key size"); - var signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length); - crypto_sign(signedMsg, msg, msg.length, secretKey); - return signedMsg; -} -exports.sign = sign; -function sign_open(signedMsg, publicKey) { - checkArrayTypes(signedMsg, publicKey); - if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) - throw new Error("bad public key size"); - var tmp = new Uint8Array(signedMsg.length); - var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); - if (mlen < 0) - return null; - var m = new Uint8Array(mlen); - for (var i = 0; i < m.length; i++) - m[i] = tmp[i]; - return m; -} -exports.sign_open = sign_open; -function sign_detached(msg, secretKey) { - var signedMsg = sign(msg, secretKey); - var sig = new Uint8Array(crypto_sign_BYTES); - for (var i = 0; i < sig.length; i++) - sig[i] = signedMsg[i]; - return sig; -} -exports.sign_detached = sign_detached; -function sign_detached_verify(msg, sig, publicKey) { - checkArrayTypes(msg, sig, publicKey); - if (sig.length !== crypto_sign_BYTES) - throw new Error("bad signature size"); - if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) - throw new Error("bad public key size"); - var sm = new Uint8Array(crypto_sign_BYTES + msg.length); - var m = new Uint8Array(crypto_sign_BYTES + msg.length); - var i; - for (i = 0; i < crypto_sign_BYTES; i++) - sm[i] = sig[i]; - for (i = 0; i < msg.length; i++) - sm[i + crypto_sign_BYTES] = msg[i]; - return crypto_sign_open(m, sm, sm.length, publicKey) >= 0; -} -exports.sign_detached_verify = sign_detached_verify; -function sign_keyPair() { - var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); - var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); - crypto_sign_keypair(pk, sk, false); - return { publicKey: pk, secretKey: sk }; -} -exports.sign_keyPair = sign_keyPair; -function x25519_edwards_keyPair_fromSecretKey(secretKey) { - const p = [gf(), gf(), gf(), gf()]; - const pk = new Uint8Array(32); - const d = new Uint8Array(64); - if (secretKey.length != 32) { - throw new Error("bad secret key size"); - } - d.set(secretKey, 0); - //crypto_hash(d, secretKey, 32); - d[0] &= 248; - d[31] &= 127; - d[31] |= 64; - scalarbase(p, d); - pack(pk, p); - return pk; -} -exports.x25519_edwards_keyPair_fromSecretKey = x25519_edwards_keyPair_fromSecretKey; -function sign_keyPair_fromSecretKey(secretKey) { - checkArrayTypes(secretKey); - if (secretKey.length !== crypto_sign_SECRETKEYBYTES) - throw new Error("bad secret key size"); - var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); - for (var i = 0; i < pk.length; i++) - pk[i] = secretKey[32 + i]; - return { publicKey: pk, secretKey: new Uint8Array(secretKey) }; -} -exports.sign_keyPair_fromSecretKey = sign_keyPair_fromSecretKey; -function sign_keyPair_fromSeed(seed) { - checkArrayTypes(seed); - if (seed.length !== crypto_sign_SEEDBYTES) - throw new Error("bad seed size"); - var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); - var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); - for (var i = 0; i < 32; i++) - sk[i] = seed[i]; - crypto_sign_keypair(pk, sk, true); - return { publicKey: pk, secretKey: sk }; -} -exports.sign_keyPair_fromSeed = sign_keyPair_fromSeed; -exports.sign_publicKeyLength = crypto_sign_PUBLICKEYBYTES; -exports.sign_secretKeyLength = crypto_sign_SECRETKEYBYTES; -exports.sign_seedLength = crypto_sign_SEEDBYTES; -exports.sign_signatureLength = crypto_sign_BYTES; -function hash(msg) { - checkArrayTypes(msg); - var h = new Uint8Array(crypto_hash_BYTES); - crypto_hash(h, msg, msg.length); - return h; -} -exports.hash = hash; -exports.hash_hashLength = crypto_hash_BYTES; -function verify(x, y) { - checkArrayTypes(x, y); - // Zero length arguments are considered not equal. - if (x.length === 0 || y.length === 0) - return false; - if (x.length !== y.length) - return false; - return vn(x, 0, y, 0, x.length) === 0 ? true : false; +function M(o, a, b) { + let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0; + const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + // first car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + // second car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + o[0] = t0; + o[1] = t1; + o[2] = t2; + o[3] = t3; + o[4] = t4; + o[5] = t5; + o[6] = t6; + o[7] = t7; + o[8] = t8; + o[9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; } -exports.verify = verify; -function setPRNG(fn) { - randombytes = fn; +function S(o, a) { + M(o, a, a); } -exports.setPRNG = setPRNG; -function sign_ed25519_pk_to_curve25519(ed25519_pk) { - const ge_a = [gf(), gf(), gf(), gf()]; - const x = gf(); - const one_minus_y = gf(); - const x25519_pk = new Uint8Array(32); - if (unpackneg(ge_a, ed25519_pk)) { - throw Error("invalid public key"); +function inv25519(o, i) { + const c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if (a !== 2 && a !== 4) + M(c, c, i); } - set25519(one_minus_y, gf1); - Z(one_minus_y, one_minus_y, ge_a[1]); - set25519(x, gf1); - A(x, x, ge_a[1]); - inv25519(one_minus_y, one_minus_y); - M(x, x, one_minus_y); - pack25519(x25519_pk, x); - return x25519_pk; + for (a = 0; a < 16; a++) + o[a] = c[a]; } -exports.sign_ed25519_pk_to_curve25519 = sign_ed25519_pk_to_curve25519; -(function () { - // Initialize PRNG if environment provides CSPRNG. - // If not, methods calling randombytes will throw. - const crypto$1 = typeof self !== "undefined" ? self.crypto || self.msCrypto : null; - if (crypto$1 && crypto$1.getRandomValues) { - // Browsers. - var QUOTA = 65536; - setPRNG(function (x, n) { - var i, v = new Uint8Array(n); - for (i = 0; i < n; i += QUOTA) { - crypto$1.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); - } - for (i = 0; i < n; i++) - x[i] = v[i]; - cleanup(v); - }); - } - else if (typeof require !== "undefined") { - // Node.js. - const cr = crypto; - if (cr && cr.randomBytes) { - setPRNG(function (x, n) { - var i, v = cr.randomBytes(n); - for (i = 0; i < n; i++) - x[i] = v[i]; - cleanup(v); - }); - } - } -})(); - -}); - -unwrapExports(naclFast); -var naclFast_1 = naclFast.HashState; -var naclFast_2 = naclFast.randomBytes; -var naclFast_3 = naclFast.secretbox; -var naclFast_4 = naclFast.secretbox_open; -var naclFast_5 = naclFast.secretbox_keyLength; -var naclFast_6 = naclFast.secretbox_nonceLength; -var naclFast_7 = naclFast.secretbox_overheadLength; -var naclFast_8 = naclFast.scalarMult; -var naclFast_9 = naclFast.scalarMult_base; -var naclFast_10 = naclFast.scalarMult_scalarLength; -var naclFast_11 = naclFast.scalarMult_groupElementLength; -var naclFast_12 = naclFast.box; -var naclFast_13 = naclFast.box_before; -var naclFast_14 = naclFast.box_after; -var naclFast_15 = naclFast.box_open; -var naclFast_16 = naclFast.box_open_after; -var naclFast_17 = naclFast.box_keyPair; -var naclFast_18 = naclFast.box_keyPair_fromSecretKey; -var naclFast_19 = naclFast.box_publicKeyLength; -var naclFast_20 = naclFast.box_secretKeyLength; -var naclFast_21 = naclFast.box_sharedKeyLength; -var naclFast_22 = naclFast.box_nonceLength; -var naclFast_23 = naclFast.box_overheadLength; -var naclFast_24 = naclFast.sign; -var naclFast_25 = naclFast.sign_open; -var naclFast_26 = naclFast.sign_detached; -var naclFast_27 = naclFast.sign_detached_verify; -var naclFast_28 = naclFast.sign_keyPair; -var naclFast_29 = naclFast.x25519_edwards_keyPair_fromSecretKey; -var naclFast_30 = naclFast.sign_keyPair_fromSecretKey; -var naclFast_31 = naclFast.sign_keyPair_fromSeed; -var naclFast_32 = naclFast.sign_publicKeyLength; -var naclFast_33 = naclFast.sign_secretKeyLength; -var naclFast_34 = naclFast.sign_seedLength; -var naclFast_35 = naclFast.sign_signatureLength; -var naclFast_36 = naclFast.hash; -var naclFast_37 = naclFast.hash_hashLength; -var naclFast_38 = naclFast.verify; -var naclFast_39 = naclFast.setPRNG; -var naclFast_40 = naclFast.sign_ed25519_pk_to_curve25519; - -var BigInteger = createCommonjsModule(function (module) { -var bigInt = (function (undefined$1) { - - var BASE = 1e7, - LOG_BASE = 7, - MAX_INT = 9007199254740992, - MAX_INT_ARR = smallToArray(MAX_INT), - DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; - - var supportsNativeBigInt = typeof BigInt === "function"; - - function Integer(v, radix, alphabet, caseSensitive) { - if (typeof v === "undefined") return Integer[0]; - if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); - return parseValue(v); - } - - function BigInteger(value, sign) { - this.value = value; - this.sign = sign; - this.isSmall = false; - } - BigInteger.prototype = Object.create(Integer.prototype); - - function SmallInteger(value) { - this.value = value; - this.sign = value < 0; - this.isSmall = true; - } - SmallInteger.prototype = Object.create(Integer.prototype); - - function NativeBigInt(value) { - this.value = value; - } - NativeBigInt.prototype = Object.create(Integer.prototype); - - function isPrecise(n) { - return -MAX_INT < n && n < MAX_INT; - } - - function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes - if (n < 1e7) - return [n]; - if (n < 1e14) - return [n % 1e7, Math.floor(n / 1e7)]; - return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; - } - - function arrayToSmall(arr) { // If BASE changes this function may need to change - trim(arr); - var length = arr.length; - if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { - switch (length) { - case 0: return 0; - case 1: return arr[0]; - case 2: return arr[0] + arr[1] * BASE; - default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; - } - } - return arr; - } - - function trim(v) { - var i = v.length; - while (v[--i] === 0); - v.length = i + 1; - } - - function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger - var x = new Array(length); - var i = -1; - while (++i < length) { - x[i] = 0; - } - return x; - } - - function truncate(n) { - if (n > 0) return Math.floor(n); - return Math.ceil(n); - } - - function add(a, b) { // assumes a and b are arrays with a.length >= b.length - var l_a = a.length, - l_b = b.length, - r = new Array(l_a), - carry = 0, - base = BASE, - sum, i; - for (i = 0; i < l_b; i++) { - sum = a[i] + b[i] + carry; - carry = sum >= base ? 1 : 0; - r[i] = sum - carry * base; - } - while (i < l_a) { - sum = a[i] + carry; - carry = sum === base ? 1 : 0; - r[i++] = sum - carry * base; - } - if (carry > 0) r.push(carry); - return r; - } - - function addAny(a, b) { - if (a.length >= b.length) return add(a, b); - return add(b, a); - } - - function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT - var l = a.length, - r = new Array(l), - base = BASE, - sum, i; - for (i = 0; i < l; i++) { - sum = a[i] - base + carry; - carry = Math.floor(sum / base); - r[i] = sum - carry * base; - carry += 1; - } - while (carry > 0) { - r[i++] = carry % base; - carry = Math.floor(carry / base); - } - return r; - } - - BigInteger.prototype.add = function (v) { - var n = parseValue(v); - if (this.sign !== n.sign) { - return this.subtract(n.negate()); - } - var a = this.value, b = n.value; - if (n.isSmall) { - return new BigInteger(addSmall(a, Math.abs(b)), this.sign); - } - return new BigInteger(addAny(a, b), this.sign); - }; - BigInteger.prototype.plus = BigInteger.prototype.add; - - SmallInteger.prototype.add = function (v) { - var n = parseValue(v); - var a = this.value; - if (a < 0 !== n.sign) { - return this.subtract(n.negate()); - } - var b = n.value; - if (n.isSmall) { - if (isPrecise(a + b)) return new SmallInteger(a + b); - b = smallToArray(Math.abs(b)); - } - return new BigInteger(addSmall(b, Math.abs(a)), a < 0); - }; - SmallInteger.prototype.plus = SmallInteger.prototype.add; - - NativeBigInt.prototype.add = function (v) { - return new NativeBigInt(this.value + parseValue(v).value); - }; - NativeBigInt.prototype.plus = NativeBigInt.prototype.add; - - function subtract(a, b) { // assumes a and b are arrays with a >= b - var a_l = a.length, - b_l = b.length, - r = new Array(a_l), - borrow = 0, - base = BASE, - i, difference; - for (i = 0; i < b_l; i++) { - difference = a[i] - borrow - b[i]; - if (difference < 0) { - difference += base; - borrow = 1; - } else borrow = 0; - r[i] = difference; - } - for (i = b_l; i < a_l; i++) { - difference = a[i] - borrow; - if (difference < 0) difference += base; - else { - r[i++] = difference; - break; - } - r[i] = difference; - } - for (; i < a_l; i++) { - r[i] = a[i]; - } - trim(r); - return r; - } - - function subtractAny(a, b, sign) { - var value; - if (compareAbs(a, b) >= 0) { - value = subtract(a, b); - } else { - value = subtract(b, a); - sign = !sign; - } - value = arrayToSmall(value); - if (typeof value === "number") { - if (sign) value = -value; - return new SmallInteger(value); - } - return new BigInteger(value, sign); - } - - function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT - var l = a.length, - r = new Array(l), - carry = -b, - base = BASE, - i, difference; - for (i = 0; i < l; i++) { - difference = a[i] + carry; - carry = Math.floor(difference / base); - difference %= base; - r[i] = difference < 0 ? difference + base : difference; - } - r = arrayToSmall(r); - if (typeof r === "number") { - if (sign) r = -r; - return new SmallInteger(r); - } return new BigInteger(r, sign); - } - - BigInteger.prototype.subtract = function (v) { - var n = parseValue(v); - if (this.sign !== n.sign) { - return this.add(n.negate()); - } - var a = this.value, b = n.value; - if (n.isSmall) - return subtractSmall(a, Math.abs(b), this.sign); - return subtractAny(a, b, this.sign); - }; - BigInteger.prototype.minus = BigInteger.prototype.subtract; - - SmallInteger.prototype.subtract = function (v) { - var n = parseValue(v); - var a = this.value; - if (a < 0 !== n.sign) { - return this.add(n.negate()); - } - var b = n.value; - if (n.isSmall) { - return new SmallInteger(a - b); - } - return subtractSmall(b, Math.abs(a), a >= 0); - }; - SmallInteger.prototype.minus = SmallInteger.prototype.subtract; - - NativeBigInt.prototype.subtract = function (v) { - return new NativeBigInt(this.value - parseValue(v).value); - }; - NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract; - - BigInteger.prototype.negate = function () { - return new BigInteger(this.value, !this.sign); - }; - SmallInteger.prototype.negate = function () { - var sign = this.sign; - var small = new SmallInteger(-this.value); - small.sign = !sign; - return small; - }; - NativeBigInt.prototype.negate = function () { - return new NativeBigInt(-this.value); - }; - - BigInteger.prototype.abs = function () { - return new BigInteger(this.value, false); - }; - SmallInteger.prototype.abs = function () { - return new SmallInteger(Math.abs(this.value)); - }; - NativeBigInt.prototype.abs = function () { - return new NativeBigInt(this.value >= 0 ? this.value : -this.value); - }; - - - function multiplyLong(a, b) { - var a_l = a.length, - b_l = b.length, - l = a_l + b_l, - r = createArray(l), - base = BASE, - product, carry, i, a_i, b_j; - for (i = 0; i < a_l; ++i) { - a_i = a[i]; - for (var j = 0; j < b_l; ++j) { - b_j = b[j]; - product = a_i * b_j + r[i + j]; - carry = Math.floor(product / base); - r[i + j] = product - carry * base; - r[i + j + 1] += carry; - } - } - trim(r); - return r; - } - - function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE - var l = a.length, - r = new Array(l), - base = BASE, - carry = 0, - product, i; - for (i = 0; i < l; i++) { - product = a[i] * b + carry; - carry = Math.floor(product / base); - r[i] = product - carry * base; - } - while (carry > 0) { - r[i++] = carry % base; - carry = Math.floor(carry / base); - } - return r; - } - - function shiftLeft(x, n) { - var r = []; - while (n-- > 0) r.push(0); - return r.concat(x); - } - - function multiplyKaratsuba(x, y) { - var n = Math.max(x.length, y.length); - - if (n <= 30) return multiplyLong(x, y); - n = Math.ceil(n / 2); - - var b = x.slice(n), - a = x.slice(0, n), - d = y.slice(n), - c = y.slice(0, n); - - var ac = multiplyKaratsuba(a, c), - bd = multiplyKaratsuba(b, d), - abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); - - var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); - trim(product); - return product; - } - - // The following function is derived from a surface fit of a graph plotting the performance difference - // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. - function useKaratsuba(l1, l2) { - return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; - } - - BigInteger.prototype.multiply = function (v) { - var n = parseValue(v), - a = this.value, b = n.value, - sign = this.sign !== n.sign, - abs; - if (n.isSmall) { - if (b === 0) return Integer[0]; - if (b === 1) return this; - if (b === -1) return this.negate(); - abs = Math.abs(b); - if (abs < BASE) { - return new BigInteger(multiplySmall(a, abs), sign); - } - b = smallToArray(abs); - } - if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes - return new BigInteger(multiplyKaratsuba(a, b), sign); - return new BigInteger(multiplyLong(a, b), sign); - }; - - BigInteger.prototype.times = BigInteger.prototype.multiply; - - function multiplySmallAndArray(a, b, sign) { // a >= 0 - if (a < BASE) { - return new BigInteger(multiplySmall(b, a), sign); - } - return new BigInteger(multiplyLong(b, smallToArray(a)), sign); +function pow2523(o, i) { + const c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if (a !== 1) + M(c, c, i); } - SmallInteger.prototype._multiplyBySmall = function (a) { - if (isPrecise(a.value * this.value)) { - return new SmallInteger(a.value * this.value); - } - return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); - }; - BigInteger.prototype._multiplyBySmall = function (a) { - if (a.value === 0) return Integer[0]; - if (a.value === 1) return this; - if (a.value === -1) return this.negate(); - return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); - }; - SmallInteger.prototype.multiply = function (v) { - return parseValue(v)._multiplyBySmall(this); - }; - SmallInteger.prototype.times = SmallInteger.prototype.multiply; - - NativeBigInt.prototype.multiply = function (v) { - return new NativeBigInt(this.value * parseValue(v).value); - }; - NativeBigInt.prototype.times = NativeBigInt.prototype.multiply; - - function square(a) { - //console.assert(2 * BASE * BASE < MAX_INT); - var l = a.length, - r = createArray(l + l), - base = BASE, - product, carry, i, a_i, a_j; - for (i = 0; i < l; i++) { - a_i = a[i]; - carry = 0 - a_i * a_i; - for (var j = i; j < l; j++) { - a_j = a[j]; - product = 2 * (a_i * a_j) + r[i + j] + carry; - carry = Math.floor(product / base); - r[i + j] = product - carry * base; - } - r[i + l] = carry; - } - trim(r); - return r; + for (a = 0; a < 16; a++) + o[a] = c[a]; +} +function crypto_scalarmult(q, n, p) { + const z = new Uint8Array(32); + const x = new Float64Array(80); + let r; + let i; + const a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(); + for (i = 0; i < 31; i++) + z[i] = n[i]; + z[31] = (n[31] & 127) | 64; + z[0] &= 248; + unpack25519(x, p); + for (i = 0; i < 16; i++) { + b[i] = x[i]; + d[i] = a[i] = c[i] = 0; } - - BigInteger.prototype.square = function () { - return new BigInteger(square(this.value), false); - }; - - SmallInteger.prototype.square = function () { - var value = this.value * this.value; - if (isPrecise(value)) return new SmallInteger(value); - return new BigInteger(square(smallToArray(Math.abs(this.value))), false); - }; - - NativeBigInt.prototype.square = function (v) { - return new NativeBigInt(this.value * this.value); - }; - - function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. - var a_l = a.length, - b_l = b.length, - base = BASE, - result = createArray(b.length), - divisorMostSignificantDigit = b[b_l - 1], - // normalization - lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), - remainder = multiplySmall(a, lambda), - divisor = multiplySmall(b, lambda), - quotientDigit, shift, carry, borrow, i, l, q; - if (remainder.length <= a_l) remainder.push(0); - divisor.push(0); - divisorMostSignificantDigit = divisor[b_l - 1]; - for (shift = a_l - b_l; shift >= 0; shift--) { - quotientDigit = base - 1; - if (remainder[shift + b_l] !== divisorMostSignificantDigit) { - quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); - } - // quotientDigit <= base - 1 - carry = 0; - borrow = 0; - l = divisor.length; - for (i = 0; i < l; i++) { - carry += quotientDigit * divisor[i]; - q = Math.floor(carry / base); - borrow += remainder[shift + i] - (carry - q * base); - carry = q; - if (borrow < 0) { - remainder[shift + i] = borrow + base; - borrow = -1; - } else { - remainder[shift + i] = borrow; - borrow = 0; - } - } - while (borrow !== 0) { - quotientDigit -= 1; - carry = 0; - for (i = 0; i < l; i++) { - carry += remainder[shift + i] - base + divisor[i]; - if (carry < 0) { - remainder[shift + i] = carry + base; - carry = 0; - } else { - remainder[shift + i] = carry; - carry = 1; - } - } - borrow += carry; - } - result[shift] = quotientDigit; - } - // denormalization - remainder = divModSmall(remainder, lambda)[0]; - return [arrayToSmall(result), arrayToSmall(remainder)]; - } - - function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ - // Performs faster than divMod1 on larger input sizes. - var a_l = a.length, - b_l = b.length, - result = [], - part = [], - base = BASE, - guess, xlen, highx, highy, check; - while (a_l) { - part.unshift(a[--a_l]); - trim(part); - if (compareAbs(part, b) < 0) { - result.push(0); - continue; - } - xlen = part.length; - highx = part[xlen - 1] * base + part[xlen - 2]; - highy = b[b_l - 1] * base + b[b_l - 2]; - if (xlen > b_l) { - highx = (highx + 1) * base; - } - guess = Math.ceil(highx / highy); - do { - check = multiplySmall(b, guess); - if (compareAbs(check, part) <= 0) break; - guess--; - } while (guess); - result.push(guess); - part = subtract(part, check); - } - result.reverse(); - return [arrayToSmall(result), arrayToSmall(part)]; - } - - function divModSmall(value, lambda) { - var length = value.length, - quotient = createArray(length), - base = BASE, - i, q, remainder, divisor; - remainder = 0; - for (i = length - 1; i >= 0; --i) { - divisor = remainder * base + value[i]; - q = truncate(divisor / lambda); - remainder = divisor - q * lambda; - quotient[i] = q | 0; - } - return [quotient, remainder | 0]; - } - - function divModAny(self, v) { - var value, n = parseValue(v); - if (supportsNativeBigInt) { - return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)]; - } - var a = self.value, b = n.value; - var quotient; - if (b === 0) throw new Error("Cannot divide by zero"); - if (self.isSmall) { - if (n.isSmall) { - return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; - } - return [Integer[0], self]; - } - if (n.isSmall) { - if (b === 1) return [self, Integer[0]]; - if (b == -1) return [self.negate(), Integer[0]]; - var abs = Math.abs(b); - if (abs < BASE) { - value = divModSmall(a, abs); - quotient = arrayToSmall(value[0]); - var remainder = value[1]; - if (self.sign) remainder = -remainder; - if (typeof quotient === "number") { - if (self.sign !== n.sign) quotient = -quotient; - return [new SmallInteger(quotient), new SmallInteger(remainder)]; - } - return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; - } - b = smallToArray(abs); - } - var comparison = compareAbs(a, b); - if (comparison === -1) return [Integer[0], self]; - if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; - - // divMod1 is faster on smaller input sizes - if (a.length + b.length <= 200) - value = divMod1(a, b); - else value = divMod2(a, b); - - quotient = value[0]; - var qSign = self.sign !== n.sign, - mod = value[1], - mSign = self.sign; - if (typeof quotient === "number") { - if (qSign) quotient = -quotient; - quotient = new SmallInteger(quotient); - } else quotient = new BigInteger(quotient, qSign); - if (typeof mod === "number") { - if (mSign) mod = -mod; - mod = new SmallInteger(mod); - } else mod = new BigInteger(mod, mSign); - return [quotient, mod]; - } - - BigInteger.prototype.divmod = function (v) { - var result = divModAny(this, v); - return { - quotient: result[0], - remainder: result[1] - }; - }; - NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod; - - - BigInteger.prototype.divide = function (v) { - return divModAny(this, v)[0]; - }; - NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) { - return new NativeBigInt(this.value / parseValue(v).value); - }; - SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; - - BigInteger.prototype.mod = function (v) { - return divModAny(this, v)[1]; - }; - NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) { - return new NativeBigInt(this.value % parseValue(v).value); - }; - SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; - - BigInteger.prototype.pow = function (v) { - var n = parseValue(v), - a = this.value, - b = n.value, - value, x, y; - if (b === 0) return Integer[1]; - if (a === 0) return Integer[0]; - if (a === 1) return Integer[1]; - if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; - if (n.sign) { - return Integer[0]; - } - if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); - if (this.isSmall) { - if (isPrecise(value = Math.pow(a, b))) - return new SmallInteger(truncate(value)); - } - x = this; - y = Integer[1]; - while (true) { - if (b & 1 === 1) { - y = y.times(x); - --b; - } - if (b === 0) break; - b /= 2; - x = x.square(); - } - return y; - }; - SmallInteger.prototype.pow = BigInteger.prototype.pow; - - NativeBigInt.prototype.pow = function (v) { - var n = parseValue(v); - var a = this.value, b = n.value; - var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2); - if (b === _0) return Integer[1]; - if (a === _0) return Integer[0]; - if (a === _1) return Integer[1]; - if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1]; - if (n.isNegative()) return new NativeBigInt(_0); - var x = this; - var y = Integer[1]; - while (true) { - if ((b & _1) === _1) { - y = y.times(x); - --b; - } - if (b === _0) break; - b /= _2; - x = x.square(); - } - return y; - }; - - BigInteger.prototype.modPow = function (exp, mod) { - exp = parseValue(exp); - mod = parseValue(mod); - if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); - var r = Integer[1], - base = this.mod(mod); - if (exp.isNegative()) { - exp = exp.multiply(Integer[-1]); - base = base.modInv(mod); - } - while (exp.isPositive()) { - if (base.isZero()) return Integer[0]; - if (exp.isOdd()) r = r.multiply(base).mod(mod); - exp = exp.divide(2); - base = base.square().mod(mod); - } - return r; - }; - NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow; - - function compareAbs(a, b) { - if (a.length !== b.length) { - return a.length > b.length ? 1 : -1; - } - for (var i = a.length - 1; i >= 0; i--) { - if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; - } - return 0; + a[0] = d[0] = 1; + for (i = 254; i >= 0; --i) { + r = (z[i >>> 3] >>> (i & 7)) & 1; + sel25519(a, b, r); + sel25519(c, d, r); + A(e, a, c); + Z(a, a, c); + A(c, b, d); + Z(b, b, d); + S(d, e); + S(f, a); + M(a, c, a); + M(c, b, e); + A(e, a, c); + Z(a, a, c); + S(b, a); + Z(c, d, f); + M(a, c, _121665); + A(a, a, d); + M(c, c, a); + M(a, d, f); + M(d, b, x); + S(b, e); + sel25519(a, b, r); + sel25519(c, d, r); } - - BigInteger.prototype.compareAbs = function (v) { - var n = parseValue(v), - a = this.value, - b = n.value; - if (n.isSmall) return 1; - return compareAbs(a, b); - }; - SmallInteger.prototype.compareAbs = function (v) { - var n = parseValue(v), - a = Math.abs(this.value), - b = n.value; - if (n.isSmall) { - b = Math.abs(b); - return a === b ? 0 : a > b ? 1 : -1; - } - return -1; - }; - NativeBigInt.prototype.compareAbs = function (v) { - var a = this.value; - var b = parseValue(v).value; - a = a >= 0 ? a : -a; - b = b >= 0 ? b : -b; - return a === b ? 0 : a > b ? 1 : -1; - }; - - BigInteger.prototype.compare = function (v) { - // See discussion about comparison with Infinity: - // https://github.com/peterolson/BigInteger.js/issues/61 - if (v === Infinity) { - return -1; - } - if (v === -Infinity) { - return 1; - } - - var n = parseValue(v), - a = this.value, - b = n.value; - if (this.sign !== n.sign) { - return n.sign ? 1 : -1; - } - if (n.isSmall) { - return this.sign ? -1 : 1; - } - return compareAbs(a, b) * (this.sign ? -1 : 1); - }; - BigInteger.prototype.compareTo = BigInteger.prototype.compare; - - SmallInteger.prototype.compare = function (v) { - if (v === Infinity) { - return -1; - } - if (v === -Infinity) { - return 1; - } - - var n = parseValue(v), - a = this.value, - b = n.value; - if (n.isSmall) { - return a == b ? 0 : a > b ? 1 : -1; - } - if (a < 0 !== n.sign) { - return a < 0 ? -1 : 1; - } - return a < 0 ? 1 : -1; - }; - SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; - - NativeBigInt.prototype.compare = function (v) { - if (v === Infinity) { - return -1; + for (i = 0; i < 16; i++) { + x[i + 16] = a[i]; + x[i + 32] = c[i]; + x[i + 48] = b[i]; + x[i + 64] = d[i]; + } + const x32 = x.subarray(32); + const x16 = x.subarray(16); + inv25519(x32, x32); + M(x16, x16, x32); + pack25519(q, x16); + return 0; +} +function crypto_scalarmult_base(q, n) { + return crypto_scalarmult(q, n, _9); +} +// prettier-ignore +const K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]; +function crypto_hashblocks_hl(hh, hl, m, n) { + const wh = new Int32Array(16), wl = new Int32Array(16); + let bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, th, tl, i, j, h, l, a, b, c, d; + let ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7]; + let pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j + 0] << 24) | (m[j + 1] << 16) | (m[j + 2] << 8) | m[j + 3]; + wl[i] = (m[j + 4] << 24) | (m[j + 5] << 16) | (m[j + 6] << 8) | m[j + 7]; } - if (v === -Infinity) { - return 1; + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + // add + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma1 + h = + ((ah4 >>> 14) | (al4 << (32 - 14))) ^ + ((ah4 >>> 18) | (al4 << (32 - 18))) ^ + ((al4 >>> (41 - 32)) | (ah4 << (32 - (41 - 32)))); + l = + ((al4 >>> 14) | (ah4 << (32 - 14))) ^ + ((al4 >>> 18) | (ah4 << (32 - 18))) ^ + ((ah4 >>> (41 - 32)) | (al4 << (32 - (41 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // K + h = K[i * 2]; + l = K[i * 2 + 1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // w + h = wh[i % 16]; + l = wl[i % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + th = (c & 0xffff) | (d << 16); + tl = (a & 0xffff) | (b << 16); + // add + h = th; + l = tl; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma0 + h = + ((ah0 >>> 28) | (al0 << (32 - 28))) ^ + ((al0 >>> (34 - 32)) | (ah0 << (32 - (34 - 32)))) ^ + ((al0 >>> (39 - 32)) | (ah0 << (32 - (39 - 32)))); + l = + ((al0 >>> 28) | (ah0 << (32 - 28))) ^ + ((ah0 >>> (34 - 32)) | (al0 << (32 - (34 - 32)))) ^ + ((ah0 >>> (39 - 32)) | (al0 << (32 - (39 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + // add + h = bh3; + l = bl3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = th; + l = tl; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + if (i % 16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = wh[(j + 9) % 16]; + l = wl[(j + 9) % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma0 + th = wh[(j + 1) % 16]; + tl = wl[(j + 1) % 16]; + h = + ((th >>> 1) | (tl << (32 - 1))) ^ + ((th >>> 8) | (tl << (32 - 8))) ^ + (th >>> 7); + l = + ((tl >>> 1) | (th << (32 - 1))) ^ + ((tl >>> 8) | (th << (32 - 8))) ^ + ((tl >>> 7) | (th << (32 - 7))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma1 + th = wh[(j + 14) % 16]; + tl = wl[(j + 14) % 16]; + h = + ((th >>> 19) | (tl << (32 - 19))) ^ + ((tl >>> (61 - 32)) | (th << (32 - (61 - 32)))) ^ + (th >>> 6); + l = + ((tl >>> 19) | (th << (32 - 19))) ^ + ((th >>> (61 - 32)) | (tl << (32 - (61 - 32)))) ^ + ((tl >>> 6) | (th << (32 - 6))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } } - var a = this.value; - var b = parseValue(v).value; - return a === b ? 0 : a > b ? 1 : -1; - }; - NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare; - - BigInteger.prototype.equals = function (v) { - return this.compare(v) === 0; - }; - NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; - - BigInteger.prototype.notEquals = function (v) { - return this.compare(v) !== 0; - }; - NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; - - BigInteger.prototype.greater = function (v) { - return this.compare(v) > 0; - }; - NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; - - BigInteger.prototype.lesser = function (v) { - return this.compare(v) < 0; - }; - NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; - - BigInteger.prototype.greaterOrEquals = function (v) { - return this.compare(v) >= 0; - }; - NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; - - BigInteger.prototype.lesserOrEquals = function (v) { - return this.compare(v) <= 0; - }; - NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; - - BigInteger.prototype.isEven = function () { - return (this.value[0] & 1) === 0; - }; - SmallInteger.prototype.isEven = function () { - return (this.value & 1) === 0; - }; - NativeBigInt.prototype.isEven = function () { - return (this.value & BigInt(1)) === BigInt(0); - }; - - BigInteger.prototype.isOdd = function () { - return (this.value[0] & 1) === 1; - }; - SmallInteger.prototype.isOdd = function () { - return (this.value & 1) === 1; - }; - NativeBigInt.prototype.isOdd = function () { - return (this.value & BigInt(1)) === BigInt(1); - }; - - BigInteger.prototype.isPositive = function () { - return !this.sign; - }; - SmallInteger.prototype.isPositive = function () { - return this.value > 0; - }; - NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive; - - BigInteger.prototype.isNegative = function () { - return this.sign; - }; - SmallInteger.prototype.isNegative = function () { - return this.value < 0; - }; - NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative; - - BigInteger.prototype.isUnit = function () { - return false; - }; - SmallInteger.prototype.isUnit = function () { - return Math.abs(this.value) === 1; - }; - NativeBigInt.prototype.isUnit = function () { - return this.abs().value === BigInt(1); - }; - - BigInteger.prototype.isZero = function () { - return false; - }; - SmallInteger.prototype.isZero = function () { - return this.value === 0; - }; - NativeBigInt.prototype.isZero = function () { - return this.value === BigInt(0); - }; - - BigInteger.prototype.isDivisibleBy = function (v) { - var n = parseValue(v); - if (n.isZero()) return false; - if (n.isUnit()) return true; - if (n.compareAbs(2) === 0) return this.isEven(); - return this.mod(n).isZero(); - }; - NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; - - function isBasicPrime(v) { - var n = v.abs(); - if (n.isUnit()) return false; - if (n.equals(2) || n.equals(3) || n.equals(5)) return true; - if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; - if (n.lesser(49)) return true; - // we don't know if it's prime: let the other functions figure it out - } - - function millerRabinTest(n, a) { - var nPrev = n.prev(), - b = nPrev, - r = 0, - d, i, x; - while (b.isEven()) b = b.divide(2), r++; - next: for (i = 0; i < a.length; i++) { - if (n.lesser(a[i])) continue; - x = bigInt(a[i]).modPow(b, n); - if (x.isUnit() || x.equals(nPrev)) continue; - for (d = r - 1; d != 0; d--) { - x = x.square().mod(n); - if (x.isUnit()) return false; - if (x.equals(nPrev)) continue next; + // add + h = ah0; + l = al0; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[0]; + l = hl[0]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + h = ah1; + l = al1; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[1]; + l = hl[1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + h = ah2; + l = al2; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[2]; + l = hl[2]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + h = ah3; + l = al3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[3]; + l = hl[3]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + h = ah4; + l = al4; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[4]; + l = hl[4]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + h = ah5; + l = al5; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[5]; + l = hl[5]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + h = ah6; + l = al6; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[6]; + l = hl[6]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[7]; + l = hl[7]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + pos += 128; + n -= 128; + } + return n; +} +function crypto_hash(out, m, n) { + const hh = new Int32Array(8); + const hl = new Int32Array(8); + const x = new Uint8Array(256); + const b = n; + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + for (let i = 0; i < n; i++) + x[i] = m[b - n + i]; + x[n] = 128; + n = 256 - 128 * (n < 112 ? 1 : 0); + x[n - 9] = 0; + ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + for (let i = 0; i < 8; i++) + ts64(out, 8 * i, hh[i], hl[i]); + return 0; +} +/** + * Incremental version of crypto_hash. + */ +class HashState { + constructor() { + this.hh = new Int32Array(8); + this.hl = new Int32Array(8); + this.next = new Uint8Array(128); + this.p = 0; + this.total = 0; + this.hh[0] = 0x6a09e667; + this.hh[1] = 0xbb67ae85; + this.hh[2] = 0x3c6ef372; + this.hh[3] = 0xa54ff53a; + this.hh[4] = 0x510e527f; + this.hh[5] = 0x9b05688c; + this.hh[6] = 0x1f83d9ab; + this.hh[7] = 0x5be0cd19; + this.hl[0] = 0xf3bcc908; + this.hl[1] = 0x84caa73b; + this.hl[2] = 0xfe94f82b; + this.hl[3] = 0x5f1d36f1; + this.hl[4] = 0xade682d1; + this.hl[5] = 0x2b3e6c1f; + this.hl[6] = 0xfb41bd6b; + this.hl[7] = 0x137e2179; + } + update(data) { + this.total += data.length; + let i = 0; + while (i < data.length) { + const r = 128 - this.p; + if (r > data.length - i) { + for (let j = 0; i + j < data.length; j++) { + this.next[this.p + j] = data[i + j]; + } + this.p += data.length - i; + break; + } + else { + for (let j = 0; this.p + j < 128; j++) { + this.next[this.p + j] = data[i + j]; + } + crypto_hashblocks_hl(this.hh, this.hl, this.next, 128); + i += 128 - this.p; + this.p = 0; } - return false; } - return true; + return this; } - - // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2 - BigInteger.prototype.isPrime = function (strict) { - var isPrime = isBasicPrime(this); - if (isPrime !== undefined$1) return isPrime; - var n = this.abs(); - var bits = n.bitLength(); - if (bits <= 64) - return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); - var logN = Math.log(2) * bits.toJSNumber(); - var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN); - for (var a = [], i = 0; i < t; i++) { - a.push(bigInt(i + 2)); - } - return millerRabinTest(n, a); - }; - NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; - - BigInteger.prototype.isProbablePrime = function (iterations, rng) { - var isPrime = isBasicPrime(this); - if (isPrime !== undefined$1) return isPrime; - var n = this.abs(); - var t = iterations === undefined$1 ? 5 : iterations; - for (var a = [], i = 0; i < t; i++) { - a.push(bigInt.randBetween(2, n.minus(2), rng)); - } - return millerRabinTest(n, a); - }; - NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; - - BigInteger.prototype.modInv = function (n) { - var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; - while (!newR.isZero()) { - q = r.divide(newR); - lastT = t; - lastR = r; - t = newT; - r = newR; - newT = lastT.subtract(q.multiply(newT)); - newR = lastR.subtract(q.multiply(newR)); - } - if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); - if (t.compare(0) === -1) { - t = t.add(n); - } - if (this.isNegative()) { - return t.negate(); - } - return t; - }; - - NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv; - - BigInteger.prototype.next = function () { - var value = this.value; - if (this.sign) { - return subtractSmall(value, 1, this.sign); - } - return new BigInteger(addSmall(value, 1), this.sign); - }; - SmallInteger.prototype.next = function () { - var value = this.value; - if (value + 1 < MAX_INT) return new SmallInteger(value + 1); - return new BigInteger(MAX_INT_ARR, false); - }; - NativeBigInt.prototype.next = function () { - return new NativeBigInt(this.value + BigInt(1)); - }; - - BigInteger.prototype.prev = function () { - var value = this.value; - if (this.sign) { - return new BigInteger(addSmall(value, 1), true); - } - return subtractSmall(value, 1, this.sign); - }; - SmallInteger.prototype.prev = function () { - var value = this.value; - if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); - return new BigInteger(MAX_INT_ARR, true); - }; - NativeBigInt.prototype.prev = function () { - return new NativeBigInt(this.value - BigInt(1)); - }; - - var powersOfTwo = [1]; - while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); - var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; - - function shift_isSmall(n) { - return Math.abs(n) <= BASE; + finish() { + const out = new Uint8Array(64); + let n = this.p; + const x = new Uint8Array(256); + const b = this.total; + for (let i = 0; i < n; i++) + x[i] = this.next[i]; + x[n] = 128; + n = 256 - 128 * (n < 112 ? 1 : 0); + x[n - 9] = 0; + ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(this.hh, this.hl, x, n); + for (let i = 0; i < 8; i++) + ts64(out, 8 * i, this.hh[i], this.hl[i]); + return out; } - - BigInteger.prototype.shiftLeft = function (v) { - var n = parseValue(v).toJSNumber(); - if (!shift_isSmall(n)) { - throw new Error(String(n) + " is too large for shifting."); - } - if (n < 0) return this.shiftRight(-n); - var result = this; - if (result.isZero()) return result; - while (n >= powers2Length) { - result = result.multiply(highestPower2); - n -= powers2Length - 1; - } - return result.multiply(powersOfTwo[n]); - }; - NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; - - BigInteger.prototype.shiftRight = function (v) { - var remQuo; - var n = parseValue(v).toJSNumber(); - if (!shift_isSmall(n)) { - throw new Error(String(n) + " is too large for shifting."); - } - if (n < 0) return this.shiftLeft(-n); - var result = this; - while (n >= powers2Length) { - if (result.isZero() || (result.isNegative() && result.isUnit())) return result; - remQuo = divModAny(result, highestPower2); - result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; - n -= powers2Length - 1; - } - remQuo = divModAny(result, powersOfTwo[n]); - return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; - }; - NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; - - function bitwise(x, y, fn) { - y = parseValue(y); - var xSign = x.isNegative(), ySign = y.isNegative(); - var xRem = xSign ? x.not() : x, - yRem = ySign ? y.not() : y; - var xDigit = 0, yDigit = 0; - var xDivMod = null, yDivMod = null; - var result = []; - while (!xRem.isZero() || !yRem.isZero()) { - xDivMod = divModAny(xRem, highestPower2); - xDigit = xDivMod[1].toJSNumber(); - if (xSign) { - xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers - } - - yDivMod = divModAny(yRem, highestPower2); - yDigit = yDivMod[1].toJSNumber(); - if (ySign) { - yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers - } - - xRem = xDivMod[0]; - yRem = yDivMod[0]; - result.push(fn(xDigit, yDigit)); +} +exports.HashState = HashState; +function add(p, q) { + const a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = gf(), h = gf(), t = gf(); + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} +function cswap(p, q, b) { + let i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} +function pack(r, p) { + const tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} +function scalarmult(p, q, s) { + let b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i / 8) | 0] >> (i & 7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} +function scalarbase(p, s) { + const q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} +function crypto_sign_keypair(pk, sk, seeded) { + const d = new Uint8Array(64); + const p = [gf(), gf(), gf(), gf()]; + if (!seeded) + randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + scalarbase(p, d); + pack(pk, p); + for (let i = 0; i < 32; i++) + sk[i + 32] = pk[i]; + return 0; +} +const L = new Float64Array([ + 0xed, + 0xd3, + 0xf5, + 0x5c, + 0x1a, + 0x63, + 0x12, + 0x58, + 0xd6, + 0x9c, + 0xf7, + 0xa2, + 0xde, + 0xf9, + 0xde, + 0x14, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0x10, +]); +function modL(r, x) { + let carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry * 256; } - var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); - for (var i = result.length - 1; i >= 0; i -= 1) { - sum = sum.multiply(highestPower2).add(bigInt(result[i])); + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) + x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i + 1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} +function reduce(r) { + const x = new Float64Array(64); + for (let i = 0; i < 64; i++) + x[i] = r[i]; + for (let i = 0; i < 64; i++) + r[i] = 0; + modL(r, x); +} +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + const d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + let i, j; + const x = new Float64Array(64); + const p = [gf(), gf(), gf(), gf()]; + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + const smlen = n + 64; + for (i = 0; i < n; i++) + sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) + sm[32 + i] = d[32 + i]; + crypto_hash(r, sm.subarray(32), n + 32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + for (i = 32; i < 64; i++) + sm[i] = sk[i]; + crypto_hash(h, sm, n + 64); + reduce(h); + for (i = 0; i < 64; i++) + x[i] = 0; + for (i = 0; i < 32; i++) + x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i + j] += h[i] * d[j]; } - return sum; } - - BigInteger.prototype.not = function () { - return this.negate().prev(); - }; - NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not; - - BigInteger.prototype.and = function (n) { - return bitwise(this, n, function (a, b) { return a & b; }); - }; - NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and; - - BigInteger.prototype.or = function (n) { - return bitwise(this, n, function (a, b) { return a | b; }); - }; - NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or; - - BigInteger.prototype.xor = function (n) { - return bitwise(this, n, function (a, b) { return a ^ b; }); - }; - NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor; - - var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; - function roughLOB(n) { // get lowestOneBit (rough) - // SmallInteger: return Min(lowestOneBit(n), 1 << 30) - // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] - var v = n.value, - x = typeof v === "number" ? v | LOBMASK_I : - typeof v === "bigint" ? v | BigInt(LOBMASK_I) : - v[0] + v[1] * BASE | LOBMASK_BI; - return x & -x; + modL(sm.subarray(32), x); + return smlen; +} +function unpackneg(r, p) { + const t = gf(); + const chk = gf(); + const num = gf(); + const den = gf(); + const den2 = gf(); + const den4 = gf(); + const den6 = gf(); + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + M(r[0], r[0], I); + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + return -1; + if (par25519(r[0]) === p[31] >> 7) + Z(r[0], gf0, r[0]); + M(r[3], r[0], r[1]); + return 0; +} +function crypto_sign_open(m, sm, n, pk) { + let i, mlen; + const t = new Uint8Array(32), h = new Uint8Array(64); + const p = [gf(), gf(), gf(), gf()], q = [gf(), gf(), gf(), gf()]; + mlen = -1; + if (n < 64) + return -1; + if (unpackneg(q, pk)) + return -1; + for (i = 0; i < n; i++) + m[i] = sm[i]; + for (i = 0; i < 32; i++) + m[i + 32] = pk[i]; + crypto_hash(h, m, n); + reduce(h); + scalarmult(p, q, h); + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) + m[i] = 0; + return -1; } - - function integerLogarithm(value, base) { - if (base.compareTo(value) <= 0) { - var tmp = integerLogarithm(value, base.square(base)); - var p = tmp.p; - var e = tmp.e; - var t = p.multiply(base); - return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; - } - return { p: bigInt(1), e: 0 }; + for (i = 0; i < n; i++) + m[i] = sm[i + 64]; + mlen = n; + return mlen; +} +const crypto_scalarmult_BYTES = 32, crypto_scalarmult_SCALARBYTES = 32, crypto_sign_BYTES = 64, crypto_sign_PUBLICKEYBYTES = 32, crypto_sign_SECRETKEYBYTES = 64, crypto_sign_SEEDBYTES = 32, crypto_hash_BYTES = 64; +/* High-level API */ +function checkArrayTypes(...args) { + for (let i = 0; i < args.length; i++) { + if (!(args[i] instanceof Uint8Array)) + throw new TypeError("unexpected type, use Uint8Array"); } - - BigInteger.prototype.bitLength = function () { - var n = this; - if (n.compareTo(bigInt(0)) < 0) { - n = n.negate().subtract(bigInt(1)); - } - if (n.compareTo(bigInt(0)) === 0) { - return bigInt(0); - } - return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); - }; - NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; - - function max(a, b) { - a = parseValue(a); - b = parseValue(b); - return a.greater(b) ? a : b; - } - function min(a, b) { - a = parseValue(a); - b = parseValue(b); - return a.lesser(b) ? a : b; - } - function gcd(a, b) { - a = parseValue(a).abs(); - b = parseValue(b).abs(); - if (a.equals(b)) return a; - if (a.isZero()) return b; - if (b.isZero()) return a; - var c = Integer[1], d, t; - while (a.isEven() && b.isEven()) { - d = min(roughLOB(a), roughLOB(b)); - a = a.divide(d); - b = b.divide(d); - c = c.multiply(d); - } - while (a.isEven()) { - a = a.divide(roughLOB(a)); - } - do { - while (b.isEven()) { - b = b.divide(roughLOB(b)); - } - if (a.greater(b)) { - t = b; b = a; a = t; - } - b = b.subtract(a); - } while (!b.isZero()); - return c.isUnit() ? a : a.multiply(c); - } - function lcm(a, b) { - a = parseValue(a).abs(); - b = parseValue(b).abs(); - return a.divide(gcd(a, b)).multiply(b); - } - function randBetween(a, b, rng) { - a = parseValue(a); - b = parseValue(b); - var usedRNG = rng || Math.random; - var low = min(a, b), high = max(a, b); - var range = high.subtract(low).add(1); - if (range.isSmall) return low.add(Math.floor(usedRNG() * range)); - var digits = toBase(range, BASE).value; - var result = [], restricted = true; - for (var i = 0; i < digits.length; i++) { - var top = restricted ? digits[i] : BASE; - var digit = truncate(usedRNG() * top); - result.push(digit); - if (digit < top) restricted = false; - } - return low.add(Integer.fromArray(result, BASE, false)); - } - - var parseBase = function (text, base, alphabet, caseSensitive) { - alphabet = alphabet || DEFAULT_ALPHABET; - text = String(text); - if (!caseSensitive) { - text = text.toLowerCase(); - alphabet = alphabet.toLowerCase(); - } - var length = text.length; - var i; - var absBase = Math.abs(base); - var alphabetValues = {}; - for (i = 0; i < alphabet.length; i++) { - alphabetValues[alphabet[i]] = i; - } - for (i = 0; i < length; i++) { - var c = text[i]; - if (c === "-") continue; - if (c in alphabetValues) { - if (alphabetValues[c] >= absBase) { - if (c === "1" && absBase === 1) continue; - throw new Error(c + " is not a valid digit in base " + base + "."); - } - } - } - base = parseValue(base); - var digits = []; - var isNegative = text[0] === "-"; - for (i = isNegative ? 1 : 0; i < text.length; i++) { - var c = text[i]; - if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); - else if (c === "<") { - var start = i; - do { i++; } while (text[i] !== ">" && i < text.length); - digits.push(parseValue(text.slice(start + 1, i))); - } - else throw new Error(c + " is not a valid character"); - } - return parseBaseFromArray(digits, base, isNegative); - }; - - function parseBaseFromArray(digits, base, isNegative) { - var val = Integer[0], pow = Integer[1], i; - for (i = digits.length - 1; i >= 0; i--) { - val = val.add(digits[i].times(pow)); - pow = pow.times(base); - } - return isNegative ? val.negate() : val; +} +function cleanup(arr) { + for (let i = 0; i < arr.length; i++) + arr[i] = 0; +} +function randomBytes(n) { + const b = new Uint8Array(n); + randombytes(b, n); + return b; +} +exports.randomBytes = randomBytes; +function scalarMult(n, p) { + checkArrayTypes(n, p); + if (n.length !== crypto_scalarmult_SCALARBYTES) + throw new Error("bad n size"); + if (p.length !== crypto_scalarmult_BYTES) + throw new Error("bad p size"); + const q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult(q, n, p); + return q; +} +exports.scalarMult = scalarMult; +function scalarMult_base(n) { + checkArrayTypes(n); + if (n.length !== crypto_scalarmult_SCALARBYTES) + throw new Error("bad n size"); + const q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult_base(q, n); + return q; +} +exports.scalarMult_base = scalarMult_base; +exports.scalarMult_scalarLength = crypto_scalarmult_SCALARBYTES; +exports.scalarMult_groupElementLength = crypto_scalarmult_BYTES; +function sign(msg, secretKey) { + checkArrayTypes(msg, secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error("bad secret key size"); + const signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + return signedMsg; +} +exports.sign = sign; +function sign_open(signedMsg, publicKey) { + checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error("bad public key size"); + const tmp = new Uint8Array(signedMsg.length); + const mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) + return null; + const m = new Uint8Array(mlen); + for (let i = 0; i < m.length; i++) + m[i] = tmp[i]; + return m; +} +exports.sign_open = sign_open; +function sign_detached(msg, secretKey) { + const signedMsg = sign(msg, secretKey); + const sig = new Uint8Array(crypto_sign_BYTES); + for (let i = 0; i < sig.length; i++) + sig[i] = signedMsg[i]; + return sig; +} +exports.sign_detached = sign_detached; +function sign_detached_verify(msg, sig, publicKey) { + checkArrayTypes(msg, sig, publicKey); + if (sig.length !== crypto_sign_BYTES) + throw new Error("bad signature size"); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error("bad public key size"); + const sm = new Uint8Array(crypto_sign_BYTES + msg.length); + const m = new Uint8Array(crypto_sign_BYTES + msg.length); + let i; + for (i = 0; i < crypto_sign_BYTES; i++) + sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) + sm[i + crypto_sign_BYTES] = msg[i]; + return crypto_sign_open(m, sm, sm.length, publicKey) >= 0; +} +exports.sign_detached_verify = sign_detached_verify; +function sign_keyPair() { + const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + const sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair(pk, sk, false); + return { publicKey: pk, secretKey: sk }; +} +exports.sign_keyPair = sign_keyPair; +function x25519_edwards_keyPair_fromSecretKey(secretKey) { + const p = [gf(), gf(), gf(), gf()]; + const pk = new Uint8Array(32); + const d = new Uint8Array(64); + if (secretKey.length != 32) { + throw new Error("bad secret key size"); } - - function stringify(digit, alphabet) { - alphabet = alphabet || DEFAULT_ALPHABET; - if (digit < alphabet.length) { - return alphabet[digit]; - } - return "<" + digit + ">"; + d.set(secretKey, 0); + //crypto_hash(d, secretKey, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + scalarbase(p, d); + pack(pk, p); + return pk; +} +exports.x25519_edwards_keyPair_fromSecretKey = x25519_edwards_keyPair_fromSecretKey; +function sign_keyPair_fromSecretKey(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error("bad secret key size"); + const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + for (let i = 0; i < pk.length; i++) + pk[i] = secretKey[32 + i]; + return { publicKey: pk, secretKey: new Uint8Array(secretKey) }; +} +exports.sign_keyPair_fromSecretKey = sign_keyPair_fromSecretKey; +function sign_keyPair_fromSeed(seed) { + checkArrayTypes(seed); + if (seed.length !== crypto_sign_SEEDBYTES) + throw new Error("bad seed size"); + const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + const sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (let i = 0; i < 32; i++) + sk[i] = seed[i]; + crypto_sign_keypair(pk, sk, true); + return { publicKey: pk, secretKey: sk }; +} +exports.sign_keyPair_fromSeed = sign_keyPair_fromSeed; +exports.sign_publicKeyLength = crypto_sign_PUBLICKEYBYTES; +exports.sign_secretKeyLength = crypto_sign_SECRETKEYBYTES; +exports.sign_seedLength = crypto_sign_SEEDBYTES; +exports.sign_signatureLength = crypto_sign_BYTES; +function hash(msg) { + checkArrayTypes(msg); + const h = new Uint8Array(crypto_hash_BYTES); + crypto_hash(h, msg, msg.length); + return h; +} +exports.hash = hash; +exports.hash_hashLength = crypto_hash_BYTES; +function verify(x, y) { + checkArrayTypes(x, y); + // Zero length arguments are considered not equal. + if (x.length === 0 || y.length === 0) + return false; + if (x.length !== y.length) + return false; + return vn(x, 0, y, 0, x.length) === 0 ? true : false; +} +exports.verify = verify; +function setPRNG(fn) { + randombytes = fn; +} +exports.setPRNG = setPRNG; +function sign_ed25519_pk_to_curve25519(ed25519_pk) { + const ge_a = [gf(), gf(), gf(), gf()]; + const x = gf(); + const one_minus_y = gf(); + const x25519_pk = new Uint8Array(32); + if (unpackneg(ge_a, ed25519_pk)) { + throw Error("invalid public key"); } - - function toBase(n, base) { - base = bigInt(base); - if (base.isZero()) { - if (n.isZero()) return { value: [0], isNegative: false }; - throw new Error("Cannot convert nonzero numbers to base 0."); - } - if (base.equals(-1)) { - if (n.isZero()) return { value: [0], isNegative: false }; - if (n.isNegative()) - return { - value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber())) - .map(Array.prototype.valueOf, [1, 0]) - ), - isNegative: false - }; - - var arr = Array.apply(null, Array(n.toJSNumber() - 1)) - .map(Array.prototype.valueOf, [0, 1]); - arr.unshift([1]); - return { - value: [].concat.apply([], arr), - isNegative: false - }; - } - - var neg = false; - if (n.isNegative() && base.isPositive()) { - neg = true; - n = n.abs(); - } - if (base.isUnit()) { - if (n.isZero()) return { value: [0], isNegative: false }; - - return { - value: Array.apply(null, Array(n.toJSNumber())) - .map(Number.prototype.valueOf, 1), - isNegative: neg - }; - } - var out = []; - var left = n, divmod; - while (left.isNegative() || left.compareAbs(base) >= 0) { - divmod = left.divmod(base); - left = divmod.quotient; - var digit = divmod.remainder; - if (digit.isNegative()) { - digit = base.minus(digit).abs(); - left = left.next(); + set25519(one_minus_y, gf1); + Z(one_minus_y, one_minus_y, ge_a[1]); + set25519(x, gf1); + A(x, x, ge_a[1]); + inv25519(one_minus_y, one_minus_y); + M(x, x, one_minus_y); + pack25519(x25519_pk, x); + return x25519_pk; +} +exports.sign_ed25519_pk_to_curve25519 = sign_ed25519_pk_to_curve25519; +(function () { + // Initialize PRNG if environment provides CSPRNG. + // If not, methods calling randombytes will throw. + const cr = typeof self !== "undefined" ? self.crypto || self.msCrypto : null; + if (cr && cr.getRandomValues) { + // Browsers. + const QUOTA = 65536; + setPRNG(function (x, n) { + let i; + const v = new Uint8Array(n); + for (i = 0; i < n; i += QUOTA) { + cr.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); } - out.push(digit.toJSNumber()); - } - out.push(left.toJSNumber()); - return { value: out.reverse(), isNegative: neg }; - } - - function toBaseString(n, base, alphabet) { - var arr = toBase(n, base); - return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { - return stringify(x, alphabet); - }).join(''); + for (i = 0; i < n; i++) + x[i] = v[i]; + cleanup(v); + }); } - - BigInteger.prototype.toArray = function (radix) { - return toBase(this, radix); - }; - - SmallInteger.prototype.toArray = function (radix) { - return toBase(this, radix); - }; - - NativeBigInt.prototype.toArray = function (radix) { - return toBase(this, radix); - }; - - BigInteger.prototype.toString = function (radix, alphabet) { - if (radix === undefined$1) radix = 10; - if (radix !== 10) return toBaseString(this, radix, alphabet); - var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; - while (--l >= 0) { - digit = String(v[l]); - str += zeros.slice(digit.length) + digit; + else if (typeof require !== "undefined") { + // Node.js. + // eslint-disable-next-line @typescript-eslint/no-var-requires + const cr = crypto; + if (cr && cr.randomBytes) { + setPRNG(function (x, n) { + const v = cr.randomBytes(n); + for (let i = 0; i < n; i++) + x[i] = v[i]; + cleanup(v); + }); } - var sign = this.sign ? "-" : ""; - return sign + str; - }; - - SmallInteger.prototype.toString = function (radix, alphabet) { - if (radix === undefined$1) radix = 10; - if (radix != 10) return toBaseString(this, radix, alphabet); - return String(this.value); - }; - - NativeBigInt.prototype.toString = SmallInteger.prototype.toString; - - NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); }; - - BigInteger.prototype.valueOf = function () { - return parseInt(this.toString(), 10); - }; - BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; - - SmallInteger.prototype.valueOf = function () { - return this.value; - }; - SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; - NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () { - return parseInt(this.toString(), 10); - }; + } +})(); - function parseStringValue(v) { - if (isPrecise(+v)) { - var x = +v; - if (x === truncate(x)) - return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x); - throw new Error("Invalid integer: " + v); - } - var sign = v[0] === "-"; - if (sign) v = v.slice(1); - var split = v.split(/e/i); - if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); - if (split.length === 2) { - var exp = split[1]; - if (exp[0] === "+") exp = exp.slice(1); - exp = +exp; - if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); - var text = split[0]; - var decimalPlace = text.indexOf("."); - if (decimalPlace >= 0) { - exp -= text.length - decimalPlace - 1; - text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); - } - if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); - text += (new Array(exp + 1)).join("0"); - v = text; - } - var isValid = /^([0-9][0-9]*)$/.test(v); - if (!isValid) throw new Error("Invalid integer: " + v); - if (supportsNativeBigInt) { - return new NativeBigInt(BigInt(sign ? "-" + v : v)); - } - var r = [], max = v.length, l = LOG_BASE, min = max - l; - while (max > 0) { - r.push(+v.slice(min, max)); - min -= l; - if (min < 0) min = 0; - max -= l; - } - trim(r); - return new BigInteger(r, sign); - } - - function parseNumberValue(v) { - if (supportsNativeBigInt) { - return new NativeBigInt(BigInt(v)); - } - if (isPrecise(v)) { - if (v !== truncate(v)) throw new Error(v + " is not an integer."); - return new SmallInteger(v); - } - return parseStringValue(v.toString()); - } - - function parseValue(v) { - if (typeof v === "number") { - return parseNumberValue(v); - } - if (typeof v === "string") { - return parseStringValue(v); - } - if (typeof v === "bigint") { - return new NativeBigInt(v); - } - return v; - } - // Pre-define numbers in range [-999,999] - for (var i = 0; i < 1000; i++) { - Integer[i] = parseValue(i); - if (i > 0) Integer[-i] = parseValue(-i); - } - // Backwards compatibility - Integer.one = Integer[1]; - Integer.zero = Integer[0]; - Integer.minusOne = Integer[-1]; - Integer.max = max; - Integer.min = min; - Integer.gcd = gcd; - Integer.lcm = lcm; - Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; }; - Integer.randBetween = randBetween; - - Integer.fromArray = function (digits, base, isNegative) { - return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); - }; +}); - return Integer; -})(); +unwrapExports(naclFast); +var naclFast_1 = naclFast.HashState; +var naclFast_2 = naclFast.randomBytes; +var naclFast_3 = naclFast.scalarMult; +var naclFast_4 = naclFast.scalarMult_base; +var naclFast_5 = naclFast.scalarMult_scalarLength; +var naclFast_6 = naclFast.scalarMult_groupElementLength; +var naclFast_7 = naclFast.sign; +var naclFast_8 = naclFast.sign_open; +var naclFast_9 = naclFast.sign_detached; +var naclFast_10 = naclFast.sign_detached_verify; +var naclFast_11 = naclFast.sign_keyPair; +var naclFast_12 = naclFast.x25519_edwards_keyPair_fromSecretKey; +var naclFast_13 = naclFast.sign_keyPair_fromSecretKey; +var naclFast_14 = naclFast.sign_keyPair_fromSeed; +var naclFast_15 = naclFast.sign_publicKeyLength; +var naclFast_16 = naclFast.sign_secretKeyLength; +var naclFast_17 = naclFast.sign_seedLength; +var naclFast_18 = naclFast.sign_signatureLength; +var naclFast_19 = naclFast.hash; +var naclFast_20 = naclFast.hash_hashLength; +var naclFast_21 = naclFast.verify; +var naclFast_22 = naclFast.setPRNG; +var naclFast_23 = naclFast.sign_ed25519_pk_to_curve25519; -// Node.js check -if ( module.hasOwnProperty("exports")) { - module.exports = bigInt; -} +var BigInteger = createCommonjsModule(function (module) { +var bigInt = (function (undefined$1) { + + var BASE = 1e7, + LOG_BASE = 7, + MAX_INT = 9007199254740992, + MAX_INT_ARR = smallToArray(MAX_INT), + DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; + + var supportsNativeBigInt = typeof BigInt === "function"; + + function Integer(v, radix, alphabet, caseSensitive) { + if (typeof v === "undefined") return Integer[0]; + if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); + return parseValue(v); + } + + function BigInteger(value, sign) { + this.value = value; + this.sign = sign; + this.isSmall = false; + } + BigInteger.prototype = Object.create(Integer.prototype); + + function SmallInteger(value) { + this.value = value; + this.sign = value < 0; + this.isSmall = true; + } + SmallInteger.prototype = Object.create(Integer.prototype); + + function NativeBigInt(value) { + this.value = value; + } + NativeBigInt.prototype = Object.create(Integer.prototype); + + function isPrecise(n) { + return -MAX_INT < n && n < MAX_INT; + } + + function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes + if (n < 1e7) + return [n]; + if (n < 1e14) + return [n % 1e7, Math.floor(n / 1e7)]; + return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; + } + + function arrayToSmall(arr) { // If BASE changes this function may need to change + trim(arr); + var length = arr.length; + if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { + switch (length) { + case 0: return 0; + case 1: return arr[0]; + case 2: return arr[0] + arr[1] * BASE; + default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; + } + } + return arr; + } + + function trim(v) { + var i = v.length; + while (v[--i] === 0); + v.length = i + 1; + } + + function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger + var x = new Array(length); + var i = -1; + while (++i < length) { + x[i] = 0; + } + return x; + } + + function truncate(n) { + if (n > 0) return Math.floor(n); + return Math.ceil(n); + } + + function add(a, b) { // assumes a and b are arrays with a.length >= b.length + var l_a = a.length, + l_b = b.length, + r = new Array(l_a), + carry = 0, + base = BASE, + sum, i; + for (i = 0; i < l_b; i++) { + sum = a[i] + b[i] + carry; + carry = sum >= base ? 1 : 0; + r[i] = sum - carry * base; + } + while (i < l_a) { + sum = a[i] + carry; + carry = sum === base ? 1 : 0; + r[i++] = sum - carry * base; + } + if (carry > 0) r.push(carry); + return r; + } + + function addAny(a, b) { + if (a.length >= b.length) return add(a, b); + return add(b, a); + } + + function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT + var l = a.length, + r = new Array(l), + base = BASE, + sum, i; + for (i = 0; i < l; i++) { + sum = a[i] - base + carry; + carry = Math.floor(sum / base); + r[i] = sum - carry * base; + carry += 1; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + BigInteger.prototype.add = function (v) { + var n = parseValue(v); + if (this.sign !== n.sign) { + return this.subtract(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) { + return new BigInteger(addSmall(a, Math.abs(b)), this.sign); + } + return new BigInteger(addAny(a, b), this.sign); + }; + BigInteger.prototype.plus = BigInteger.prototype.add; + + SmallInteger.prototype.add = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.subtract(n.negate()); + } + var b = n.value; + if (n.isSmall) { + if (isPrecise(a + b)) return new SmallInteger(a + b); + b = smallToArray(Math.abs(b)); + } + return new BigInteger(addSmall(b, Math.abs(a)), a < 0); + }; + SmallInteger.prototype.plus = SmallInteger.prototype.add; + + NativeBigInt.prototype.add = function (v) { + return new NativeBigInt(this.value + parseValue(v).value); + }; + NativeBigInt.prototype.plus = NativeBigInt.prototype.add; + + function subtract(a, b) { // assumes a and b are arrays with a >= b + var a_l = a.length, + b_l = b.length, + r = new Array(a_l), + borrow = 0, + base = BASE, + i, difference; + for (i = 0; i < b_l; i++) { + difference = a[i] - borrow - b[i]; + if (difference < 0) { + difference += base; + borrow = 1; + } else borrow = 0; + r[i] = difference; + } + for (i = b_l; i < a_l; i++) { + difference = a[i] - borrow; + if (difference < 0) difference += base; + else { + r[i++] = difference; + break; + } + r[i] = difference; + } + for (; i < a_l; i++) { + r[i] = a[i]; + } + trim(r); + return r; + } + + function subtractAny(a, b, sign) { + var value; + if (compareAbs(a, b) >= 0) { + value = subtract(a, b); + } else { + value = subtract(b, a); + sign = !sign; + } + value = arrayToSmall(value); + if (typeof value === "number") { + if (sign) value = -value; + return new SmallInteger(value); + } + return new BigInteger(value, sign); + } + + function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT + var l = a.length, + r = new Array(l), + carry = -b, + base = BASE, + i, difference; + for (i = 0; i < l; i++) { + difference = a[i] + carry; + carry = Math.floor(difference / base); + difference %= base; + r[i] = difference < 0 ? difference + base : difference; + } + r = arrayToSmall(r); + if (typeof r === "number") { + if (sign) r = -r; + return new SmallInteger(r); + } return new BigInteger(r, sign); + } + + BigInteger.prototype.subtract = function (v) { + var n = parseValue(v); + if (this.sign !== n.sign) { + return this.add(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) + return subtractSmall(a, Math.abs(b), this.sign); + return subtractAny(a, b, this.sign); + }; + BigInteger.prototype.minus = BigInteger.prototype.subtract; + + SmallInteger.prototype.subtract = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.add(n.negate()); + } + var b = n.value; + if (n.isSmall) { + return new SmallInteger(a - b); + } + return subtractSmall(b, Math.abs(a), a >= 0); + }; + SmallInteger.prototype.minus = SmallInteger.prototype.subtract; + + NativeBigInt.prototype.subtract = function (v) { + return new NativeBigInt(this.value - parseValue(v).value); + }; + NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract; + + BigInteger.prototype.negate = function () { + return new BigInteger(this.value, !this.sign); + }; + SmallInteger.prototype.negate = function () { + var sign = this.sign; + var small = new SmallInteger(-this.value); + small.sign = !sign; + return small; + }; + NativeBigInt.prototype.negate = function () { + return new NativeBigInt(-this.value); + }; + + BigInteger.prototype.abs = function () { + return new BigInteger(this.value, false); + }; + SmallInteger.prototype.abs = function () { + return new SmallInteger(Math.abs(this.value)); + }; + NativeBigInt.prototype.abs = function () { + return new NativeBigInt(this.value >= 0 ? this.value : -this.value); + }; + + + function multiplyLong(a, b) { + var a_l = a.length, + b_l = b.length, + l = a_l + b_l, + r = createArray(l), + base = BASE, + product, carry, i, a_i, b_j; + for (i = 0; i < a_l; ++i) { + a_i = a[i]; + for (var j = 0; j < b_l; ++j) { + b_j = b[j]; + product = a_i * b_j + r[i + j]; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + r[i + j + 1] += carry; + } + } + trim(r); + return r; + } + + function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE + var l = a.length, + r = new Array(l), + base = BASE, + carry = 0, + product, i; + for (i = 0; i < l; i++) { + product = a[i] * b + carry; + carry = Math.floor(product / base); + r[i] = product - carry * base; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + function shiftLeft(x, n) { + var r = []; + while (n-- > 0) r.push(0); + return r.concat(x); + } + + function multiplyKaratsuba(x, y) { + var n = Math.max(x.length, y.length); + + if (n <= 30) return multiplyLong(x, y); + n = Math.ceil(n / 2); + + var b = x.slice(n), + a = x.slice(0, n), + d = y.slice(n), + c = y.slice(0, n); + + var ac = multiplyKaratsuba(a, c), + bd = multiplyKaratsuba(b, d), + abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); + + var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); + trim(product); + return product; + } + + // The following function is derived from a surface fit of a graph plotting the performance difference + // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. + function useKaratsuba(l1, l2) { + return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; + } + + BigInteger.prototype.multiply = function (v) { + var n = parseValue(v), + a = this.value, b = n.value, + sign = this.sign !== n.sign, + abs; + if (n.isSmall) { + if (b === 0) return Integer[0]; + if (b === 1) return this; + if (b === -1) return this.negate(); + abs = Math.abs(b); + if (abs < BASE) { + return new BigInteger(multiplySmall(a, abs), sign); + } + b = smallToArray(abs); + } + if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes + return new BigInteger(multiplyKaratsuba(a, b), sign); + return new BigInteger(multiplyLong(a, b), sign); + }; + + BigInteger.prototype.times = BigInteger.prototype.multiply; + + function multiplySmallAndArray(a, b, sign) { // a >= 0 + if (a < BASE) { + return new BigInteger(multiplySmall(b, a), sign); + } + return new BigInteger(multiplyLong(b, smallToArray(a)), sign); + } + SmallInteger.prototype._multiplyBySmall = function (a) { + if (isPrecise(a.value * this.value)) { + return new SmallInteger(a.value * this.value); + } + return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); + }; + BigInteger.prototype._multiplyBySmall = function (a) { + if (a.value === 0) return Integer[0]; + if (a.value === 1) return this; + if (a.value === -1) return this.negate(); + return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); + }; + SmallInteger.prototype.multiply = function (v) { + return parseValue(v)._multiplyBySmall(this); + }; + SmallInteger.prototype.times = SmallInteger.prototype.multiply; + + NativeBigInt.prototype.multiply = function (v) { + return new NativeBigInt(this.value * parseValue(v).value); + }; + NativeBigInt.prototype.times = NativeBigInt.prototype.multiply; + + function square(a) { + //console.assert(2 * BASE * BASE < MAX_INT); + var l = a.length, + r = createArray(l + l), + base = BASE, + product, carry, i, a_i, a_j; + for (i = 0; i < l; i++) { + a_i = a[i]; + carry = 0 - a_i * a_i; + for (var j = i; j < l; j++) { + a_j = a[j]; + product = 2 * (a_i * a_j) + r[i + j] + carry; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + } + r[i + l] = carry; + } + trim(r); + return r; + } + + BigInteger.prototype.square = function () { + return new BigInteger(square(this.value), false); + }; + + SmallInteger.prototype.square = function () { + var value = this.value * this.value; + if (isPrecise(value)) return new SmallInteger(value); + return new BigInteger(square(smallToArray(Math.abs(this.value))), false); + }; + + NativeBigInt.prototype.square = function (v) { + return new NativeBigInt(this.value * this.value); + }; + + function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. + var a_l = a.length, + b_l = b.length, + base = BASE, + result = createArray(b.length), + divisorMostSignificantDigit = b[b_l - 1], + // normalization + lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), + remainder = multiplySmall(a, lambda), + divisor = multiplySmall(b, lambda), + quotientDigit, shift, carry, borrow, i, l, q; + if (remainder.length <= a_l) remainder.push(0); + divisor.push(0); + divisorMostSignificantDigit = divisor[b_l - 1]; + for (shift = a_l - b_l; shift >= 0; shift--) { + quotientDigit = base - 1; + if (remainder[shift + b_l] !== divisorMostSignificantDigit) { + quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); + } + // quotientDigit <= base - 1 + carry = 0; + borrow = 0; + l = divisor.length; + for (i = 0; i < l; i++) { + carry += quotientDigit * divisor[i]; + q = Math.floor(carry / base); + borrow += remainder[shift + i] - (carry - q * base); + carry = q; + if (borrow < 0) { + remainder[shift + i] = borrow + base; + borrow = -1; + } else { + remainder[shift + i] = borrow; + borrow = 0; + } + } + while (borrow !== 0) { + quotientDigit -= 1; + carry = 0; + for (i = 0; i < l; i++) { + carry += remainder[shift + i] - base + divisor[i]; + if (carry < 0) { + remainder[shift + i] = carry + base; + carry = 0; + } else { + remainder[shift + i] = carry; + carry = 1; + } + } + borrow += carry; + } + result[shift] = quotientDigit; + } + // denormalization + remainder = divModSmall(remainder, lambda)[0]; + return [arrayToSmall(result), arrayToSmall(remainder)]; + } + + function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ + // Performs faster than divMod1 on larger input sizes. + var a_l = a.length, + b_l = b.length, + result = [], + part = [], + base = BASE, + guess, xlen, highx, highy, check; + while (a_l) { + part.unshift(a[--a_l]); + trim(part); + if (compareAbs(part, b) < 0) { + result.push(0); + continue; + } + xlen = part.length; + highx = part[xlen - 1] * base + part[xlen - 2]; + highy = b[b_l - 1] * base + b[b_l - 2]; + if (xlen > b_l) { + highx = (highx + 1) * base; + } + guess = Math.ceil(highx / highy); + do { + check = multiplySmall(b, guess); + if (compareAbs(check, part) <= 0) break; + guess--; + } while (guess); + result.push(guess); + part = subtract(part, check); + } + result.reverse(); + return [arrayToSmall(result), arrayToSmall(part)]; + } + + function divModSmall(value, lambda) { + var length = value.length, + quotient = createArray(length), + base = BASE, + i, q, remainder, divisor; + remainder = 0; + for (i = length - 1; i >= 0; --i) { + divisor = remainder * base + value[i]; + q = truncate(divisor / lambda); + remainder = divisor - q * lambda; + quotient[i] = q | 0; + } + return [quotient, remainder | 0]; + } + + function divModAny(self, v) { + var value, n = parseValue(v); + if (supportsNativeBigInt) { + return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)]; + } + var a = self.value, b = n.value; + var quotient; + if (b === 0) throw new Error("Cannot divide by zero"); + if (self.isSmall) { + if (n.isSmall) { + return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; + } + return [Integer[0], self]; + } + if (n.isSmall) { + if (b === 1) return [self, Integer[0]]; + if (b == -1) return [self.negate(), Integer[0]]; + var abs = Math.abs(b); + if (abs < BASE) { + value = divModSmall(a, abs); + quotient = arrayToSmall(value[0]); + var remainder = value[1]; + if (self.sign) remainder = -remainder; + if (typeof quotient === "number") { + if (self.sign !== n.sign) quotient = -quotient; + return [new SmallInteger(quotient), new SmallInteger(remainder)]; + } + return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; + } + b = smallToArray(abs); + } + var comparison = compareAbs(a, b); + if (comparison === -1) return [Integer[0], self]; + if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; + + // divMod1 is faster on smaller input sizes + if (a.length + b.length <= 200) + value = divMod1(a, b); + else value = divMod2(a, b); + + quotient = value[0]; + var qSign = self.sign !== n.sign, + mod = value[1], + mSign = self.sign; + if (typeof quotient === "number") { + if (qSign) quotient = -quotient; + quotient = new SmallInteger(quotient); + } else quotient = new BigInteger(quotient, qSign); + if (typeof mod === "number") { + if (mSign) mod = -mod; + mod = new SmallInteger(mod); + } else mod = new BigInteger(mod, mSign); + return [quotient, mod]; + } + + BigInteger.prototype.divmod = function (v) { + var result = divModAny(this, v); + return { + quotient: result[0], + remainder: result[1] + }; + }; + NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod; + + + BigInteger.prototype.divide = function (v) { + return divModAny(this, v)[0]; + }; + NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) { + return new NativeBigInt(this.value / parseValue(v).value); + }; + SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; + + BigInteger.prototype.mod = function (v) { + return divModAny(this, v)[1]; + }; + NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) { + return new NativeBigInt(this.value % parseValue(v).value); + }; + SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; + + BigInteger.prototype.pow = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value, + value, x, y; + if (b === 0) return Integer[1]; + if (a === 0) return Integer[0]; + if (a === 1) return Integer[1]; + if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; + if (n.sign) { + return Integer[0]; + } + if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); + if (this.isSmall) { + if (isPrecise(value = Math.pow(a, b))) + return new SmallInteger(truncate(value)); + } + x = this; + y = Integer[1]; + while (true) { + if (b & 1 === 1) { + y = y.times(x); + --b; + } + if (b === 0) break; + b /= 2; + x = x.square(); + } + return y; + }; + SmallInteger.prototype.pow = BigInteger.prototype.pow; + + NativeBigInt.prototype.pow = function (v) { + var n = parseValue(v); + var a = this.value, b = n.value; + var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2); + if (b === _0) return Integer[1]; + if (a === _0) return Integer[0]; + if (a === _1) return Integer[1]; + if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1]; + if (n.isNegative()) return new NativeBigInt(_0); + var x = this; + var y = Integer[1]; + while (true) { + if ((b & _1) === _1) { + y = y.times(x); + --b; + } + if (b === _0) break; + b /= _2; + x = x.square(); + } + return y; + }; + + BigInteger.prototype.modPow = function (exp, mod) { + exp = parseValue(exp); + mod = parseValue(mod); + if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); + var r = Integer[1], + base = this.mod(mod); + if (exp.isNegative()) { + exp = exp.multiply(Integer[-1]); + base = base.modInv(mod); + } + while (exp.isPositive()) { + if (base.isZero()) return Integer[0]; + if (exp.isOdd()) r = r.multiply(base).mod(mod); + exp = exp.divide(2); + base = base.square().mod(mod); + } + return r; + }; + NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow; + + function compareAbs(a, b) { + if (a.length !== b.length) { + return a.length > b.length ? 1 : -1; + } + for (var i = a.length - 1; i >= 0; i--) { + if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; + } + return 0; + } + + BigInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) return 1; + return compareAbs(a, b); + }; + SmallInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = Math.abs(this.value), + b = n.value; + if (n.isSmall) { + b = Math.abs(b); + return a === b ? 0 : a > b ? 1 : -1; + } + return -1; + }; + NativeBigInt.prototype.compareAbs = function (v) { + var a = this.value; + var b = parseValue(v).value; + a = a >= 0 ? a : -a; + b = b >= 0 ? b : -b; + return a === b ? 0 : a > b ? 1 : -1; + }; + + BigInteger.prototype.compare = function (v) { + // See discussion about comparison with Infinity: + // https://github.com/peterolson/BigInteger.js/issues/61 + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (this.sign !== n.sign) { + return n.sign ? 1 : -1; + } + if (n.isSmall) { + return this.sign ? -1 : 1; + } + return compareAbs(a, b) * (this.sign ? -1 : 1); + }; + BigInteger.prototype.compareTo = BigInteger.prototype.compare; + + SmallInteger.prototype.compare = function (v) { + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) { + return a == b ? 0 : a > b ? 1 : -1; + } + if (a < 0 !== n.sign) { + return a < 0 ? -1 : 1; + } + return a < 0 ? 1 : -1; + }; + SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; + + NativeBigInt.prototype.compare = function (v) { + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + var a = this.value; + var b = parseValue(v).value; + return a === b ? 0 : a > b ? 1 : -1; + }; + NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare; + + BigInteger.prototype.equals = function (v) { + return this.compare(v) === 0; + }; + NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; + + BigInteger.prototype.notEquals = function (v) { + return this.compare(v) !== 0; + }; + NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; + + BigInteger.prototype.greater = function (v) { + return this.compare(v) > 0; + }; + NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; + + BigInteger.prototype.lesser = function (v) { + return this.compare(v) < 0; + }; + NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; + + BigInteger.prototype.greaterOrEquals = function (v) { + return this.compare(v) >= 0; + }; + NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; + + BigInteger.prototype.lesserOrEquals = function (v) { + return this.compare(v) <= 0; + }; + NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; + + BigInteger.prototype.isEven = function () { + return (this.value[0] & 1) === 0; + }; + SmallInteger.prototype.isEven = function () { + return (this.value & 1) === 0; + }; + NativeBigInt.prototype.isEven = function () { + return (this.value & BigInt(1)) === BigInt(0); + }; + + BigInteger.prototype.isOdd = function () { + return (this.value[0] & 1) === 1; + }; + SmallInteger.prototype.isOdd = function () { + return (this.value & 1) === 1; + }; + NativeBigInt.prototype.isOdd = function () { + return (this.value & BigInt(1)) === BigInt(1); + }; + + BigInteger.prototype.isPositive = function () { + return !this.sign; + }; + SmallInteger.prototype.isPositive = function () { + return this.value > 0; + }; + NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive; + + BigInteger.prototype.isNegative = function () { + return this.sign; + }; + SmallInteger.prototype.isNegative = function () { + return this.value < 0; + }; + NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative; + + BigInteger.prototype.isUnit = function () { + return false; + }; + SmallInteger.prototype.isUnit = function () { + return Math.abs(this.value) === 1; + }; + NativeBigInt.prototype.isUnit = function () { + return this.abs().value === BigInt(1); + }; + + BigInteger.prototype.isZero = function () { + return false; + }; + SmallInteger.prototype.isZero = function () { + return this.value === 0; + }; + NativeBigInt.prototype.isZero = function () { + return this.value === BigInt(0); + }; + + BigInteger.prototype.isDivisibleBy = function (v) { + var n = parseValue(v); + if (n.isZero()) return false; + if (n.isUnit()) return true; + if (n.compareAbs(2) === 0) return this.isEven(); + return this.mod(n).isZero(); + }; + NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; + + function isBasicPrime(v) { + var n = v.abs(); + if (n.isUnit()) return false; + if (n.equals(2) || n.equals(3) || n.equals(5)) return true; + if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; + if (n.lesser(49)) return true; + // we don't know if it's prime: let the other functions figure it out + } + + function millerRabinTest(n, a) { + var nPrev = n.prev(), + b = nPrev, + r = 0, + d, i, x; + while (b.isEven()) b = b.divide(2), r++; + next: for (i = 0; i < a.length; i++) { + if (n.lesser(a[i])) continue; + x = bigInt(a[i]).modPow(b, n); + if (x.isUnit() || x.equals(nPrev)) continue; + for (d = r - 1; d != 0; d--) { + x = x.square().mod(n); + if (x.isUnit()) return false; + if (x.equals(nPrev)) continue next; + } + return false; + } + return true; + } + + // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2 + BigInteger.prototype.isPrime = function (strict) { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined$1) return isPrime; + var n = this.abs(); + var bits = n.bitLength(); + if (bits <= 64) + return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); + var logN = Math.log(2) * bits.toJSNumber(); + var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN); + for (var a = [], i = 0; i < t; i++) { + a.push(bigInt(i + 2)); + } + return millerRabinTest(n, a); + }; + NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; + + BigInteger.prototype.isProbablePrime = function (iterations, rng) { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined$1) return isPrime; + var n = this.abs(); + var t = iterations === undefined$1 ? 5 : iterations; + for (var a = [], i = 0; i < t; i++) { + a.push(bigInt.randBetween(2, n.minus(2), rng)); + } + return millerRabinTest(n, a); + }; + NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; + + BigInteger.prototype.modInv = function (n) { + var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; + while (!newR.isZero()) { + q = r.divide(newR); + lastT = t; + lastR = r; + t = newT; + r = newR; + newT = lastT.subtract(q.multiply(newT)); + newR = lastR.subtract(q.multiply(newR)); + } + if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); + if (t.compare(0) === -1) { + t = t.add(n); + } + if (this.isNegative()) { + return t.negate(); + } + return t; + }; + + NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv; + + BigInteger.prototype.next = function () { + var value = this.value; + if (this.sign) { + return subtractSmall(value, 1, this.sign); + } + return new BigInteger(addSmall(value, 1), this.sign); + }; + SmallInteger.prototype.next = function () { + var value = this.value; + if (value + 1 < MAX_INT) return new SmallInteger(value + 1); + return new BigInteger(MAX_INT_ARR, false); + }; + NativeBigInt.prototype.next = function () { + return new NativeBigInt(this.value + BigInt(1)); + }; + + BigInteger.prototype.prev = function () { + var value = this.value; + if (this.sign) { + return new BigInteger(addSmall(value, 1), true); + } + return subtractSmall(value, 1, this.sign); + }; + SmallInteger.prototype.prev = function () { + var value = this.value; + if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); + return new BigInteger(MAX_INT_ARR, true); + }; + NativeBigInt.prototype.prev = function () { + return new NativeBigInt(this.value - BigInt(1)); + }; + + var powersOfTwo = [1]; + while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); + var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; + + function shift_isSmall(n) { + return Math.abs(n) <= BASE; + } + + BigInteger.prototype.shiftLeft = function (v) { + var n = parseValue(v).toJSNumber(); + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + if (n < 0) return this.shiftRight(-n); + var result = this; + if (result.isZero()) return result; + while (n >= powers2Length) { + result = result.multiply(highestPower2); + n -= powers2Length - 1; + } + return result.multiply(powersOfTwo[n]); + }; + NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; + + BigInteger.prototype.shiftRight = function (v) { + var remQuo; + var n = parseValue(v).toJSNumber(); + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + if (n < 0) return this.shiftLeft(-n); + var result = this; + while (n >= powers2Length) { + if (result.isZero() || (result.isNegative() && result.isUnit())) return result; + remQuo = divModAny(result, highestPower2); + result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + n -= powers2Length - 1; + } + remQuo = divModAny(result, powersOfTwo[n]); + return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + }; + NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; + + function bitwise(x, y, fn) { + y = parseValue(y); + var xSign = x.isNegative(), ySign = y.isNegative(); + var xRem = xSign ? x.not() : x, + yRem = ySign ? y.not() : y; + var xDigit = 0, yDigit = 0; + var xDivMod = null, yDivMod = null; + var result = []; + while (!xRem.isZero() || !yRem.isZero()) { + xDivMod = divModAny(xRem, highestPower2); + xDigit = xDivMod[1].toJSNumber(); + if (xSign) { + xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers + } + + yDivMod = divModAny(yRem, highestPower2); + yDigit = yDivMod[1].toJSNumber(); + if (ySign) { + yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers + } + + xRem = xDivMod[0]; + yRem = yDivMod[0]; + result.push(fn(xDigit, yDigit)); + } + var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); + for (var i = result.length - 1; i >= 0; i -= 1) { + sum = sum.multiply(highestPower2).add(bigInt(result[i])); + } + return sum; + } + + BigInteger.prototype.not = function () { + return this.negate().prev(); + }; + NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not; + + BigInteger.prototype.and = function (n) { + return bitwise(this, n, function (a, b) { return a & b; }); + }; + NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and; + + BigInteger.prototype.or = function (n) { + return bitwise(this, n, function (a, b) { return a | b; }); + }; + NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or; + + BigInteger.prototype.xor = function (n) { + return bitwise(this, n, function (a, b) { return a ^ b; }); + }; + NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor; + + var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; + function roughLOB(n) { // get lowestOneBit (rough) + // SmallInteger: return Min(lowestOneBit(n), 1 << 30) + // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] + var v = n.value, + x = typeof v === "number" ? v | LOBMASK_I : + typeof v === "bigint" ? v | BigInt(LOBMASK_I) : + v[0] + v[1] * BASE | LOBMASK_BI; + return x & -x; + } + + function integerLogarithm(value, base) { + if (base.compareTo(value) <= 0) { + var tmp = integerLogarithm(value, base.square(base)); + var p = tmp.p; + var e = tmp.e; + var t = p.multiply(base); + return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; + } + return { p: bigInt(1), e: 0 }; + } + + BigInteger.prototype.bitLength = function () { + var n = this; + if (n.compareTo(bigInt(0)) < 0) { + n = n.negate().subtract(bigInt(1)); + } + if (n.compareTo(bigInt(0)) === 0) { + return bigInt(0); + } + return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); + }; + NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; + + function max(a, b) { + a = parseValue(a); + b = parseValue(b); + return a.greater(b) ? a : b; + } + function min(a, b) { + a = parseValue(a); + b = parseValue(b); + return a.lesser(b) ? a : b; + } + function gcd(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + if (a.equals(b)) return a; + if (a.isZero()) return b; + if (b.isZero()) return a; + var c = Integer[1], d, t; + while (a.isEven() && b.isEven()) { + d = min(roughLOB(a), roughLOB(b)); + a = a.divide(d); + b = b.divide(d); + c = c.multiply(d); + } + while (a.isEven()) { + a = a.divide(roughLOB(a)); + } + do { + while (b.isEven()) { + b = b.divide(roughLOB(b)); + } + if (a.greater(b)) { + t = b; b = a; a = t; + } + b = b.subtract(a); + } while (!b.isZero()); + return c.isUnit() ? a : a.multiply(c); + } + function lcm(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + return a.divide(gcd(a, b)).multiply(b); + } + function randBetween(a, b, rng) { + a = parseValue(a); + b = parseValue(b); + var usedRNG = rng || Math.random; + var low = min(a, b), high = max(a, b); + var range = high.subtract(low).add(1); + if (range.isSmall) return low.add(Math.floor(usedRNG() * range)); + var digits = toBase(range, BASE).value; + var result = [], restricted = true; + for (var i = 0; i < digits.length; i++) { + var top = restricted ? digits[i] : BASE; + var digit = truncate(usedRNG() * top); + result.push(digit); + if (digit < top) restricted = false; + } + return low.add(Integer.fromArray(result, BASE, false)); + } + + var parseBase = function (text, base, alphabet, caseSensitive) { + alphabet = alphabet || DEFAULT_ALPHABET; + text = String(text); + if (!caseSensitive) { + text = text.toLowerCase(); + alphabet = alphabet.toLowerCase(); + } + var length = text.length; + var i; + var absBase = Math.abs(base); + var alphabetValues = {}; + for (i = 0; i < alphabet.length; i++) { + alphabetValues[alphabet[i]] = i; + } + for (i = 0; i < length; i++) { + var c = text[i]; + if (c === "-") continue; + if (c in alphabetValues) { + if (alphabetValues[c] >= absBase) { + if (c === "1" && absBase === 1) continue; + throw new Error(c + " is not a valid digit in base " + base + "."); + } + } + } + base = parseValue(base); + var digits = []; + var isNegative = text[0] === "-"; + for (i = isNegative ? 1 : 0; i < text.length; i++) { + var c = text[i]; + if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); + else if (c === "<") { + var start = i; + do { i++; } while (text[i] !== ">" && i < text.length); + digits.push(parseValue(text.slice(start + 1, i))); + } + else throw new Error(c + " is not a valid character"); + } + return parseBaseFromArray(digits, base, isNegative); + }; + + function parseBaseFromArray(digits, base, isNegative) { + var val = Integer[0], pow = Integer[1], i; + for (i = digits.length - 1; i >= 0; i--) { + val = val.add(digits[i].times(pow)); + pow = pow.times(base); + } + return isNegative ? val.negate() : val; + } + + function stringify(digit, alphabet) { + alphabet = alphabet || DEFAULT_ALPHABET; + if (digit < alphabet.length) { + return alphabet[digit]; + } + return "<" + digit + ">"; + } + + function toBase(n, base) { + base = bigInt(base); + if (base.isZero()) { + if (n.isZero()) return { value: [0], isNegative: false }; + throw new Error("Cannot convert nonzero numbers to base 0."); + } + if (base.equals(-1)) { + if (n.isZero()) return { value: [0], isNegative: false }; + if (n.isNegative()) + return { + value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber())) + .map(Array.prototype.valueOf, [1, 0]) + ), + isNegative: false + }; + + var arr = Array.apply(null, Array(n.toJSNumber() - 1)) + .map(Array.prototype.valueOf, [0, 1]); + arr.unshift([1]); + return { + value: [].concat.apply([], arr), + isNegative: false + }; + } + + var neg = false; + if (n.isNegative() && base.isPositive()) { + neg = true; + n = n.abs(); + } + if (base.isUnit()) { + if (n.isZero()) return { value: [0], isNegative: false }; + + return { + value: Array.apply(null, Array(n.toJSNumber())) + .map(Number.prototype.valueOf, 1), + isNegative: neg + }; + } + var out = []; + var left = n, divmod; + while (left.isNegative() || left.compareAbs(base) >= 0) { + divmod = left.divmod(base); + left = divmod.quotient; + var digit = divmod.remainder; + if (digit.isNegative()) { + digit = base.minus(digit).abs(); + left = left.next(); + } + out.push(digit.toJSNumber()); + } + out.push(left.toJSNumber()); + return { value: out.reverse(), isNegative: neg }; + } + + function toBaseString(n, base, alphabet) { + var arr = toBase(n, base); + return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { + return stringify(x, alphabet); + }).join(''); + } + + BigInteger.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + SmallInteger.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + NativeBigInt.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + BigInteger.prototype.toString = function (radix, alphabet) { + if (radix === undefined$1) radix = 10; + if (radix !== 10) return toBaseString(this, radix, alphabet); + var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; + while (--l >= 0) { + digit = String(v[l]); + str += zeros.slice(digit.length) + digit; + } + var sign = this.sign ? "-" : ""; + return sign + str; + }; + + SmallInteger.prototype.toString = function (radix, alphabet) { + if (radix === undefined$1) radix = 10; + if (radix != 10) return toBaseString(this, radix, alphabet); + return String(this.value); + }; + + NativeBigInt.prototype.toString = SmallInteger.prototype.toString; + + NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); }; + + BigInteger.prototype.valueOf = function () { + return parseInt(this.toString(), 10); + }; + BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; + + SmallInteger.prototype.valueOf = function () { + return this.value; + }; + SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; + NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () { + return parseInt(this.toString(), 10); + }; + + function parseStringValue(v) { + if (isPrecise(+v)) { + var x = +v; + if (x === truncate(x)) + return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x); + throw new Error("Invalid integer: " + v); + } + var sign = v[0] === "-"; + if (sign) v = v.slice(1); + var split = v.split(/e/i); + if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); + if (split.length === 2) { + var exp = split[1]; + if (exp[0] === "+") exp = exp.slice(1); + exp = +exp; + if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); + var text = split[0]; + var decimalPlace = text.indexOf("."); + if (decimalPlace >= 0) { + exp -= text.length - decimalPlace - 1; + text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); + } + if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); + text += (new Array(exp + 1)).join("0"); + v = text; + } + var isValid = /^([0-9][0-9]*)$/.test(v); + if (!isValid) throw new Error("Invalid integer: " + v); + if (supportsNativeBigInt) { + return new NativeBigInt(BigInt(sign ? "-" + v : v)); + } + var r = [], max = v.length, l = LOG_BASE, min = max - l; + while (max > 0) { + r.push(+v.slice(min, max)); + min -= l; + if (min < 0) min = 0; + max -= l; + } + trim(r); + return new BigInteger(r, sign); + } + + function parseNumberValue(v) { + if (supportsNativeBigInt) { + return new NativeBigInt(BigInt(v)); + } + if (isPrecise(v)) { + if (v !== truncate(v)) throw new Error(v + " is not an integer."); + return new SmallInteger(v); + } + return parseStringValue(v.toString()); + } + + function parseValue(v) { + if (typeof v === "number") { + return parseNumberValue(v); + } + if (typeof v === "string") { + return parseStringValue(v); + } + if (typeof v === "bigint") { + return new NativeBigInt(v); + } + return v; + } + // Pre-define numbers in range [-999,999] + for (var i = 0; i < 1000; i++) { + Integer[i] = parseValue(i); + if (i > 0) Integer[-i] = parseValue(-i); + } + // Backwards compatibility + Integer.one = Integer[1]; + Integer.zero = Integer[0]; + Integer.minusOne = Integer[-1]; + Integer.max = max; + Integer.min = min; + Integer.gcd = gcd; + Integer.lcm = lcm; + Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; }; + Integer.randBetween = randBetween; + + Integer.fromArray = function (digits, base, isNegative) { + return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); + }; + + return Integer; +})(); + +// Node.js check +if ( module.hasOwnProperty("exports")) { + module.exports = bigInt; +} }); var sha256_1 = createCommonjsModule(function (module, exports) { @@ -7855,10 +6425,7 @@ class HMAC { this.digestLength = this.inner.digestLength; const pad = new Uint8Array(this.blockSize); if (key.length > this.blockSize) { - new HashSha256() - .update(key) - .finish(pad) - .clean(); + new HashSha256().update(key).finish(pad).clean(); } else { for (let i = 0; i < key.length; i++) { @@ -7966,9 +6533,10 @@ var kdf_1 = createCommonjsModule(function (module, exports) { */ Object.defineProperty(exports, "__esModule", { value: true }); +const nacl = tslib_1.__importStar(naclFast); function sha512(data) { - return naclFast.hash(data); + return nacl.hash(data); } exports.sha512 = sha512; function hmac(digest, blockSize, key, message) { @@ -8056,18 +6624,16 @@ var talerCrypto = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * Native implementation of GNU Taler crypto. */ - -const big_integer_1 = __importDefault(BigInteger); +const nacl = tslib_1.__importStar(naclFast); +const big_integer_1 = tslib_1.__importDefault(BigInteger); function getRandomBytes(n) { - return naclFast.randomBytes(n); + return nacl.randomBytes(n); } exports.getRandomBytes = getRandomBytes; const encTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; @@ -8166,28 +6732,28 @@ function decodeCrock(encoded) { } exports.decodeCrock = decodeCrock; function eddsaGetPublic(eddsaPriv) { - const pair = naclFast.sign_keyPair_fromSeed(eddsaPriv); + const pair = nacl.sign_keyPair_fromSeed(eddsaPriv); return pair.publicKey; } exports.eddsaGetPublic = eddsaGetPublic; function ecdheGetPublic(ecdhePriv) { - return naclFast.scalarMult_base(ecdhePriv); + return nacl.scalarMult_base(ecdhePriv); } exports.ecdheGetPublic = ecdheGetPublic; function keyExchangeEddsaEcdhe(eddsaPriv, ecdhePub) { - const ph = naclFast.hash(eddsaPriv); + const ph = nacl.hash(eddsaPriv); const a = new Uint8Array(32); for (let i = 0; i < 32; i++) { a[i] = ph[i]; } - const x = naclFast.scalarMult(a, ecdhePub); - return naclFast.hash(x); + const x = nacl.scalarMult(a, ecdhePub); + return nacl.hash(x); } exports.keyExchangeEddsaEcdhe = keyExchangeEddsaEcdhe; function keyExchangeEcdheEddsa(ecdhePriv, eddsaPub) { - const curve25519Pub = naclFast.sign_ed25519_pk_to_curve25519(eddsaPub); - const x = naclFast.scalarMult(ecdhePriv, curve25519Pub); - return naclFast.hash(x); + const curve25519Pub = nacl.sign_ed25519_pk_to_curve25519(eddsaPub); + const x = nacl.scalarMult(ecdhePriv, curve25519Pub); + return nacl.hash(x); } exports.keyExchangeEcdheEddsa = keyExchangeEcdheEddsa; /** @@ -8302,36 +6868,36 @@ function rsaVerify(hm, rsaSig, rsaPubEnc) { } exports.rsaVerify = rsaVerify; function createEddsaKeyPair() { - const eddsaPriv = naclFast.randomBytes(32); + const eddsaPriv = nacl.randomBytes(32); const eddsaPub = eddsaGetPublic(eddsaPriv); return { eddsaPriv, eddsaPub }; } exports.createEddsaKeyPair = createEddsaKeyPair; function createEcdheKeyPair() { - const ecdhePriv = naclFast.randomBytes(32); + const ecdhePriv = nacl.randomBytes(32); const ecdhePub = ecdheGetPublic(ecdhePriv); return { ecdhePriv, ecdhePub }; } exports.createEcdheKeyPair = createEcdheKeyPair; function createBlindingKeySecret() { - return naclFast.randomBytes(32); + return nacl.randomBytes(32); } exports.createBlindingKeySecret = createBlindingKeySecret; function hash(d) { - return naclFast.hash(d); + return nacl.hash(d); } exports.hash = hash; function eddsaSign(msg, eddsaPriv) { - const pair = naclFast.sign_keyPair_fromSeed(eddsaPriv); - return naclFast.sign_detached(msg, pair.secretKey); + const pair = nacl.sign_keyPair_fromSeed(eddsaPriv); + return nacl.sign_detached(msg, pair.secretKey); } exports.eddsaSign = eddsaSign; function eddsaVerify(msg, sig, eddsaPub) { - return naclFast.sign_detached_verify(msg, sig, eddsaPub); + return nacl.sign_detached_verify(msg, sig, eddsaPub); } exports.eddsaVerify = eddsaVerify; function createHashContext() { - return new naclFast.HashState(); + return new nacl.HashState(); } exports.createHashContext = createHashContext; function setupRefreshPlanchet(secretSeed, coinNumber) { @@ -8374,10 +6940,10 @@ var talerCrypto_17 = talerCrypto.eddsaVerify; var talerCrypto_18 = talerCrypto.createHashContext; var talerCrypto_19 = talerCrypto.setupRefreshPlanchet; -var refresh = createCommonjsModule(function (module, exports) { +var ReserveTransaction = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler - (C) 2019 GNUnet e.V. + (C) 2019 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -8390,605 +6956,996 @@ var refresh = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); +/** + * @author Florian Dold + */ +/** + * Imports. + */ +exports.codecForReserveWithdrawTransaction = () => codec.makeCodecForObject() + .property("amount", codec.codecForString) + .property("h_coin_envelope", codec.codecForString) + .property("h_denom_pub", codec.codecForString) + .property("reserve_sig", codec.codecForString) + .property("type", codec.makeCodecForConstString("WITHDRAW" /* Withdraw */)) + .property("withdraw_fee", codec.codecForString) + .build("ReserveWithdrawTransaction"); +exports.codecForReserveCreditTransaction = () => codec.makeCodecForObject() + .property("amount", codec.codecForString) + .property("sender_account_url", codec.codecForString) + .property("timestamp", time.codecForTimestamp) + .property("wire_reference", codec.codecForString) + .property("type", codec.makeCodecForConstString("CREDIT" /* Credit */)) + .build("ReserveCreditTransaction"); +exports.codecForReserveClosingTransaction = () => codec.makeCodecForObject() + .property("amount", codec.codecForString) + .property("closing_fee", codec.codecForString) + .property("exchange_pub", codec.codecForString) + .property("exchange_sig", codec.codecForString) + .property("h_wire", codec.codecForString) + .property("timestamp", time.codecForTimestamp) + .property("type", codec.makeCodecForConstString("CLOSING" /* Closing */)) + .property("wtid", codec.codecForString) + .build("ReserveClosingTransaction"); +exports.codecForReserveRecoupTransaction = () => codec.makeCodecForObject() + .property("amount", codec.codecForString) + .property("coin_pub", codec.codecForString) + .property("exchange_pub", codec.codecForString) + .property("exchange_sig", codec.codecForString) + .property("timestamp", time.codecForTimestamp) + .property("type", codec.makeCodecForConstString("RECOUP" /* Recoup */)) + .build("ReserveRecoupTransaction"); +exports.codecForReserveTransaction = () => codec.makeCodecForUnion() + .discriminateOn("type") + .alternative("WITHDRAW" /* Withdraw */, exports.codecForReserveWithdrawTransaction()) + .alternative("CLOSING" /* Closing */, exports.codecForReserveClosingTransaction()) + .alternative("RECOUP" /* Recoup */, exports.codecForReserveRecoupTransaction()) + .alternative("CREDIT" /* Credit */, exports.codecForReserveCreditTransaction()) + .build("ReserveTransaction"); +}); +unwrapExports(ReserveTransaction); +var ReserveTransaction_1 = ReserveTransaction.codecForReserveWithdrawTransaction; +var ReserveTransaction_2 = ReserveTransaction.codecForReserveCreditTransaction; +var ReserveTransaction_3 = ReserveTransaction.codecForReserveClosingTransaction; +var ReserveTransaction_4 = ReserveTransaction.codecForReserveRecoupTransaction; +var ReserveTransaction_5 = ReserveTransaction.codecForReserveTransaction; +var ReserveStatus = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019 Taler Systems S.A. + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. -const logger = new logging.Logger("refresh.ts"); + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); /** - * Get the amount that we lose when refreshing a coin of the given denomination - * with a certain amount left. - * - * If the amount left is zero, then the refresh cost - * is also considered to be zero. If a refresh isn't possible (e.g. due to lack of - * the right denominations), then the cost is the full amount left. - * - * Considers refresh fees, withdrawal fees after refresh and amounts too small - * to refresh. + * @author Florian Dold */ -function getTotalRefreshCost(denoms, refreshedDenom, amountLeft) { - const withdrawAmount = Amounts.sub(amountLeft, refreshedDenom.feeRefresh) - .amount; - const withdrawDenoms = withdraw.getWithdrawDenomList(withdrawAmount, denoms); - const resultingAmount = Amounts.add(Amounts.getZero(withdrawAmount.currency), ...withdrawDenoms.map(d => d.value)).amount; - const totalCost = Amounts.sub(amountLeft, resultingAmount).amount; - logger.trace("total refresh cost for", helpers.amountToPretty(amountLeft), "is", helpers.amountToPretty(totalCost)); - return totalCost; +/** + * Imports. + */ + + +exports.codecForReserveStatus = () => codec.makeCodecForObject() + .property("balance", codec.codecForString) + .property("history", codec.makeCodecForList(ReserveTransaction.codecForReserveTransaction())) + .build("ReserveStatus"); + +}); + +unwrapExports(ReserveStatus); +var ReserveStatus_1 = ReserveStatus.codecForReserveStatus; + +var reserveHistoryUtil = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + +const Amounts = tslib_1.__importStar(amounts); + + +/** + * Check if two reserve history items (exchange's version) match. + */ +function isRemoteHistoryMatch(t1, t2) { + switch (t1.type) { + case "CLOSING" /* Closing */: { + return t1.type === t2.type && t1.wtid == t2.wtid; + } + case "CREDIT" /* Credit */: { + return t1.type === t2.type && t1.wire_reference === t2.wire_reference; + } + case "RECOUP" /* Recoup */: { + return (t1.type === t2.type && + t1.coin_pub === t2.coin_pub && + time.timestampCmp(t1.timestamp, t2.timestamp) === 0); + } + case "WITHDRAW" /* Withdraw */: { + return t1.type === t2.type && t1.h_coin_envelope === t2.h_coin_envelope; + } + } +} +/** + * Check a local reserve history item and a remote history item are a match. + */ +function isLocalRemoteHistoryMatch(t1, t2) { + switch (t1.type) { + case "credit" /* Credit */: { + return (t2.type === "CREDIT" /* Credit */ && + !!t1.expectedAmount && + Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0); + } + case "withdraw" /* Withdraw */: + return (t2.type === "WITHDRAW" /* Withdraw */ && + !!t1.expectedAmount && + Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0); + case "recoup" /* Recoup */: { + return (t2.type === "RECOUP" /* Recoup */ && + !!t1.expectedAmount && + Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0); + } + } + return false; +} +exports.isLocalRemoteHistoryMatch = isLocalRemoteHistoryMatch; +/** + * Compute totals for the wallet's view of the reserve history. + */ +function summarizeReserveHistory(localHistory, currency) { + const posAmounts = []; + const negAmounts = []; + const expectedPosAmounts = []; + const expectedNegAmounts = []; + const withdrawnAmounts = []; + for (const item of localHistory) { + switch (item.type) { + case "credit" /* Credit */: + if (item.matchedExchangeTransaction) { + posAmounts.push(Amounts.parseOrThrow(item.matchedExchangeTransaction.amount)); + } + else if (item.expectedAmount) { + expectedPosAmounts.push(item.expectedAmount); + } + break; + case "recoup" /* Recoup */: + if (item.matchedExchangeTransaction) { + if (item.matchedExchangeTransaction) { + posAmounts.push(Amounts.parseOrThrow(item.matchedExchangeTransaction.amount)); + } + else if (item.expectedAmount) { + expectedPosAmounts.push(item.expectedAmount); + } + else { + throw Error("invariant failed"); + } + } + break; + case "closing" /* Closing */: + if (item.matchedExchangeTransaction) { + negAmounts.push(Amounts.parseOrThrow(item.matchedExchangeTransaction.amount)); + } + else { + throw Error("invariant failed"); + } + break; + case "withdraw" /* Withdraw */: + if (item.matchedExchangeTransaction) { + negAmounts.push(Amounts.parseOrThrow(item.matchedExchangeTransaction.amount)); + withdrawnAmounts.push(Amounts.parseOrThrow(item.matchedExchangeTransaction.amount)); + } + else if (item.expectedAmount) { + expectedNegAmounts.push(item.expectedAmount); + } + else { + throw Error("invariant failed"); + } + break; + } + } + const z = Amounts.getZero(currency); + const computedBalance = Amounts.sub(Amounts.add(z, ...posAmounts).amount, ...negAmounts).amount; + const unclaimedReserveAmount = Amounts.sub(Amounts.add(z, ...posAmounts).amount, ...negAmounts, ...expectedNegAmounts).amount; + const awaitedReserveAmount = Amounts.sub(Amounts.add(z, ...expectedPosAmounts).amount, ...expectedNegAmounts).amount; + const withdrawnAmount = Amounts.add(z, ...withdrawnAmounts).amount; + return { + computedReserveBalance: computedBalance, + unclaimedReserveAmount: unclaimedReserveAmount, + awaitedReserveAmount: awaitedReserveAmount, + withdrawnAmount, + }; +} +exports.summarizeReserveHistory = summarizeReserveHistory; +/** + * Reconcile the wallet's local model of the reserve history + * with the reserve history of the exchange. + */ +function reconcileReserveHistory(localHistory, remoteHistory) { + const updatedLocalHistory = helpers.deepCopy(localHistory); + const newMatchedItems = []; + const newAddedItems = []; + const remoteMatched = remoteHistory.map(() => false); + const localMatched = localHistory.map(() => false); + // Take care of deposits + // First, see which pairs are already a definite match. + for (let remoteIndex = 0; remoteIndex < remoteHistory.length; remoteIndex++) { + const rhi = remoteHistory[remoteIndex]; + for (let localIndex = 0; localIndex < localHistory.length; localIndex++) { + if (localMatched[localIndex]) { + continue; + } + const lhi = localHistory[localIndex]; + if (!lhi.matchedExchangeTransaction) { + continue; + } + if (isRemoteHistoryMatch(rhi, lhi.matchedExchangeTransaction)) { + localMatched[localIndex] = true; + remoteMatched[remoteIndex] = true; + break; + } + } + } + // Check that all previously matched items are still matched + for (let localIndex = 0; localIndex < localHistory.length; localIndex++) { + if (localMatched[localIndex]) { + continue; + } + const lhi = localHistory[localIndex]; + if (lhi.matchedExchangeTransaction) { + // Don't use for further matching + localMatched[localIndex] = true; + // FIXME: emit some error here! + throw Error("previously matched reserve history item now unmatched"); + } + } + // Next, find out if there are any exact new matches between local and remote + // history items + for (let localIndex = 0; localIndex < localHistory.length; localIndex++) { + if (localMatched[localIndex]) { + continue; + } + const lhi = localHistory[localIndex]; + for (let remoteIndex = 0; remoteIndex < remoteHistory.length; remoteIndex++) { + const rhi = remoteHistory[remoteIndex]; + if (remoteMatched[remoteIndex]) { + continue; + } + if (isLocalRemoteHistoryMatch(lhi, rhi)) { + localMatched[localIndex] = true; + remoteMatched[remoteIndex] = true; + updatedLocalHistory[localIndex].matchedExchangeTransaction = rhi; + newMatchedItems.push(lhi); + break; + } + } + } + // Finally we add new history items + for (let remoteIndex = 0; remoteIndex < remoteHistory.length; remoteIndex++) { + if (remoteMatched[remoteIndex]) { + continue; + } + const rhi = remoteHistory[remoteIndex]; + let newItem; + switch (rhi.type) { + case "CLOSING" /* Closing */: { + newItem = { + type: "closing" /* Closing */, + matchedExchangeTransaction: rhi, + }; + break; + } + case "CREDIT" /* Credit */: { + newItem = { + type: "credit" /* Credit */, + matchedExchangeTransaction: rhi, + }; + break; + } + case "RECOUP" /* Recoup */: { + newItem = { + type: "recoup" /* Recoup */, + matchedExchangeTransaction: rhi, + }; + break; + } + case "WITHDRAW" /* Withdraw */: { + newItem = { + type: "withdraw" /* Withdraw */, + matchedExchangeTransaction: rhi, + }; + break; + } + } + updatedLocalHistory.push(newItem); + newAddedItems.push(newItem); + } + return { + updatedLocalHistory, + newAddedItems, + newMatchedItems, + }; +} +exports.reconcileReserveHistory = reconcileReserveHistory; + +}); + +unwrapExports(reserveHistoryUtil); +var reserveHistoryUtil_1 = reserveHistoryUtil.isLocalRemoteHistoryMatch; +var reserveHistoryUtil_2 = reserveHistoryUtil.summarizeReserveHistory; +var reserveHistoryUtil_3 = reserveHistoryUtil.reconcileReserveHistory; + +var reserves = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + + + + + + + + + + + + + + + +const logger = new logging.Logger("reserves.ts"); +function resetReserveRetry(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, (x) => { + if (x.retryInfo.active) { + x.retryInfo = dbTypes.initRetryInfo(); + } + return x; + }); + }); } -exports.getTotalRefreshCost = getTotalRefreshCost; /** - * Create a refresh session inside a refresh group. + * Create a reserve, but do not flag it as confirmed yet. + * + * Adds the corresponding exchange as a trusted exchange if it is neither + * audited nor trusted already. */ -function refreshCreateSession(ws, refreshGroupId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace(`creating refresh session for coin ${coinIndex} in refresh group ${refreshGroupId}`); - const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { - return; +function createReserve(ws, req) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const keypair = yield ws.cryptoApi.createEddsaKeypair(); + const now = time.getTimestampNow(); + const canonExchange = helpers.canonicalizeBaseUrl(req.exchange); + let reserveStatus; + if (req.bankWithdrawStatusUrl) { + reserveStatus = dbTypes.ReserveRecordStatus.REGISTERING_BANK; } - if (refreshGroup.finishedPerCoin[coinIndex]) { - return; + else { + reserveStatus = dbTypes.ReserveRecordStatus.UNCONFIRMED; } - const existingRefreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; - if (existingRefreshSession) { - return; + const reserveRecord = { + timestampCreated: now, + exchangeBaseUrl: canonExchange, + reservePriv: keypair.priv, + reservePub: keypair.pub, + senderWire: req.senderWire, + timestampConfirmed: undefined, + timestampReserveInfoPosted: undefined, + bankWithdrawStatusUrl: req.bankWithdrawStatusUrl, + exchangeWire: req.exchangeWire, + reserveStatus, + lastSuccessfulStatusQuery: undefined, + retryInfo: dbTypes.initRetryInfo(), + lastError: undefined, + reserveTransactions: [], + currency: req.amount.currency, + }; + reserveRecord.reserveTransactions.push({ + type: "credit" /* Credit */, + expectedAmount: req.amount, + }); + const senderWire = req.senderWire; + if (senderWire) { + const rec = { + paytoUri: senderWire, + }; + yield ws.db.put(dbTypes.Stores.senderWires, rec); } - const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex]; - const coin = yield ws.db.get(dbTypes.Stores.coins, oldCoinPub); - if (!coin) { - throw Error("Can't refresh, coin not found"); + const exchangeInfo = yield exchanges.updateExchangeFromUrl(ws, req.exchange); + const exchangeDetails = exchangeInfo.details; + if (!exchangeDetails) { + console.log(exchangeDetails); + throw Error("exchange not updated"); } - const exchange = yield exchanges.updateExchangeFromUrl(ws, coin.exchangeBaseUrl); - if (!exchange) { - throw Error("db inconsistent: exchange of coin not found"); + const { isAudited, isTrusted } = yield exchanges.getExchangeTrust(ws, exchangeInfo); + let currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, exchangeDetails.currency); + if (!currencyRecord) { + currencyRecord = { + auditors: [], + exchanges: [], + fractionalDigits: 2, + name: exchangeDetails.currency, + }; } - const oldDenom = yield ws.db.get(dbTypes.Stores.denominations, [ - exchange.baseUrl, - coin.denomPub, - ]); - if (!oldDenom) { - throw Error("db inconsistent: denomination for coin not found"); + if (!isAudited && !isTrusted) { + currencyRecord.exchanges.push({ + baseUrl: req.exchange, + exchangePub: exchangeDetails.masterPublicKey, + }); } - const availableDenoms = yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) - .toArray(); - const availableAmount = Amounts.sub(coin.currentAmount, oldDenom.feeRefresh) - .amount; - const newCoinDenoms = withdraw.getWithdrawDenomList(availableAmount, availableDenoms); - if (newCoinDenoms.length === 0) { - logger.trace(`not refreshing, available amount ${helpers.amountToPretty(availableAmount)} too small`); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.refreshGroups], (tx) => __awaiter(this, void 0, void 0, function* () { - const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!rg) { - return; - } - rg.finishedPerCoin[coinIndex] = true; - rg.finishedPerCoin[coinIndex] = true; - let allDone = true; - for (const f of rg.finishedPerCoin) { - if (!f) { - allDone = false; - break; + const cr = currencyRecord; + const resp = yield ws.db.runWithWriteTransaction([dbTypes.Stores.currencies, dbTypes.Stores.reserves, dbTypes.Stores.bankWithdrawUris], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + // Check if we have already created a reserve for that bankWithdrawStatusUrl + if (reserveRecord.bankWithdrawStatusUrl) { + const bwi = yield tx.get(dbTypes.Stores.bankWithdrawUris, reserveRecord.bankWithdrawStatusUrl); + if (bwi) { + const otherReserve = yield tx.get(dbTypes.Stores.reserves, bwi.reservePub); + if (otherReserve) { + logger.trace("returning existing reserve for bankWithdrawStatusUri"); + return { + exchange: otherReserve.exchangeBaseUrl, + reservePub: otherReserve.reservePub, + }; } } - if (allDone) { - rg.timestampFinished = time.getTimestampNow(); - rg.retryInfo = dbTypes.initRetryInfo(false); - } - yield tx.put(dbTypes.Stores.refreshGroups, rg); - })); - ws.notify({ type: "refresh-unwarranted" /* RefreshUnwarranted */ }); - return; - } - const refreshSession = yield ws.cryptoApi.createRefreshSession(exchange.baseUrl, 3, coin, newCoinDenoms, oldDenom.feeRefresh); - // Store refresh session and subtract refreshed amount from - // coin in the same transaction. - yield ws.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups, dbTypes.Stores.coins], (tx) => __awaiter(this, void 0, void 0, function* () { - const c = yield tx.get(dbTypes.Stores.coins, coin.coinPub); - if (!c) { - throw Error("coin not found, but marked for refresh"); - } - const r = Amounts.sub(c.currentAmount, refreshSession.amountRefreshInput); - if (r.saturated) { - console.log("can't refresh coin, no amount left"); - return; - } - c.currentAmount = r.amount; - c.status = dbTypes.CoinStatus.Dormant; - const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!rg) { - return; - } - if (rg.refreshSessionPerCoin[coinIndex]) { - return; + yield tx.put(dbTypes.Stores.bankWithdrawUris, { + reservePub: reserveRecord.reservePub, + talerWithdrawUri: reserveRecord.bankWithdrawStatusUrl, + }); } - rg.refreshSessionPerCoin[coinIndex] = refreshSession; - yield tx.put(dbTypes.Stores.refreshGroups, rg); - yield tx.put(dbTypes.Stores.coins, c); + yield tx.put(dbTypes.Stores.currencies, cr); + yield tx.put(dbTypes.Stores.reserves, reserveRecord); + const r = { + exchange: canonExchange, + reservePub: keypair.pub, + }; + return r; })); - logger.info(`created refresh session for coin #${coinIndex} in ${refreshGroupId}`); - ws.notify({ type: "refresh-started" /* RefreshStarted */ }); + ws.notify({ type: "reserve-created" /* ReserveCreated */ }); + // Asynchronously process the reserve, but return + // to the caller already. + processReserve(ws, resp.reservePub, true).catch((e) => { + console.error("Processing reserve (after createReserve) failed:", e); + }); + return resp; }); } -function refreshMelt(ws, refreshGroupId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { - return; - } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; - if (!refreshSession) { - return; - } - if (refreshSession.norevealIndex !== undefined) { - return; - } - const coin = yield ws.db.get(dbTypes.Stores.coins, refreshSession.meltCoinPub); - if (!coin) { - console.error("can't melt coin, it does not exist"); - return; - } - const reqUrl = new URL("refresh/melt", refreshSession.exchangeBaseUrl); - const meltReq = { - coin_pub: coin.coinPub, - confirm_sig: refreshSession.confirmSig, - denom_pub_hash: coin.denomPubHash, - denom_sig: coin.denomSig, - rc: refreshSession.hash, - value_with_fee: Amounts.toString(refreshSession.amountRefreshInput), - }; - logger.trace(`melt request for coin:`, meltReq); - const resp = yield ws.http.postJson(reqUrl.href, meltReq); - if (resp.status !== 200) { - console.log(`got status ${resp.status} for refresh/melt`); - try { - const respJson = yield resp.json(); - console.log(`body of refresh/melt error response:`, JSON.stringify(respJson, undefined, 2)); - } - catch (e) { - console.log(`body of refresh/melt error response is not JSON`); - } - throw Error(`unexpected status code ${resp.status} for refresh/melt`); - } - const respJson = yield resp.json(); - logger.trace("melt response:", respJson); - if (resp.status !== 200) { - console.error(respJson); - throw Error("refresh failed"); - } - const norevealIndex = respJson.noreveal_index; - if (typeof norevealIndex !== "number") { - throw Error("invalid response"); - } - refreshSession.norevealIndex = norevealIndex; - yield ws.db.mutate(dbTypes.Stores.refreshGroups, refreshGroupId, rg => { - const rs = rg.refreshSessionPerCoin[coinIndex]; - if (!rs) { - return; - } - if (rs.norevealIndex !== undefined) { +exports.createReserve = createReserve; +/** + * Re-query the status of a reserve. + */ +function forceQueryReserve(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.reserves], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield tx.get(dbTypes.Stores.reserves, reservePub); + if (!reserve) { return; } - if (rs.finishedTimestamp) { - return; + // Only force status query where it makes sense + switch (reserve.reserveStatus) { + case dbTypes.ReserveRecordStatus.DORMANT: + case dbTypes.ReserveRecordStatus.WITHDRAWING: + case dbTypes.ReserveRecordStatus.QUERYING_STATUS: + break; + default: + return; } - rs.norevealIndex = norevealIndex; - return rg; - }); - ws.notify({ - type: "refresh-melted" /* RefreshMelted */, - }); + reserve.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; + reserve.retryInfo = dbTypes.initRetryInfo(); + yield tx.put(dbTypes.Stores.reserves, reserve); + })); + yield processReserve(ws, reservePub, true); + }); +} +exports.forceQueryReserve = forceQueryReserve; +/** + * First fetch information requred to withdraw from the reserve, + * then deplete the reserve, withdrawing coins until it is empty. + * + * The returned promise resolves once the reserve is set to the + * state DORMANT. + */ +function processReserve(ws, reservePub, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + return ws.memoProcessReserve.memo(reservePub, () => tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpError = (err) => incrementReserveRetry(ws, reservePub, err); + yield errors.guardOperationException(() => processReserveImpl(ws, reservePub, forceNow), onOpError); + })); }); } -function refreshReveal(ws, refreshGroupId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { - return; +exports.processReserve = processReserve; +function registerReserveWithBank(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + switch (reserve === null || reserve === void 0 ? void 0 : reserve.reserveStatus) { + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + case dbTypes.ReserveRecordStatus.REGISTERING_BANK: + break; + default: + return; } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; - if (!refreshSession) { + const bankStatusUrl = reserve.bankWithdrawStatusUrl; + if (!bankStatusUrl) { return; } - const norevealIndex = refreshSession.norevealIndex; - if (norevealIndex === undefined) { - throw Error("can't reveal without melting first"); - } - const privs = Array.from(refreshSession.transferPrivs); - privs.splice(norevealIndex, 1); - const planchets = refreshSession.planchetsForGammas[norevealIndex]; - if (!planchets) { - throw Error("refresh index error"); - } - const meltCoinRecord = yield ws.db.get(dbTypes.Stores.coins, refreshSession.meltCoinPub); - if (!meltCoinRecord) { - throw Error("inconsistent database"); - } - const evs = planchets.map((x) => x.coinEv); - const linkSigs = []; - for (let i = 0; i < refreshSession.newDenoms.length; i++) { - const linkSig = yield ws.cryptoApi.signCoinLink(meltCoinRecord.coinPriv, refreshSession.newDenomHashes[i], refreshSession.meltCoinPub, refreshSession.transferPubs[norevealIndex], planchets[i].coinEv); - linkSigs.push(linkSig); - } - const req = { - coin_evs: evs, - new_denoms_h: refreshSession.newDenomHashes, - rc: refreshSession.hash, - transfer_privs: privs, - transfer_pub: refreshSession.transferPubs[norevealIndex], - link_sigs: linkSigs, - }; - const reqUrl = new URL("refresh/reveal", refreshSession.exchangeBaseUrl); - logger.trace("reveal request:", req); - let resp; - try { - resp = yield ws.http.postJson(reqUrl.href, req); + console.log("making selection"); + if (reserve.timestampReserveInfoPosted) { + throw Error("bank claims that reserve info selection is not done"); } - catch (e) { - console.error("got error during /refresh/reveal request"); - console.error(e); - return; + // FIXME: parse bank response + yield ws.http.postJson(bankStatusUrl, { + reserve_pub: reservePub, + selected_exchange: reserve.exchangeWire, + }); + yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, (r) => { + switch (r.reserveStatus) { + case dbTypes.ReserveRecordStatus.REGISTERING_BANK: + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + break; + default: + return; + } + r.timestampReserveInfoPosted = time.getTimestampNow(); + r.reserveStatus = dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK; + r.retryInfo = dbTypes.initRetryInfo(); + return r; + }); + ws.notify({ type: "wildcard" /* Wildcard */ }); + return processReserveBankStatus(ws, reservePub); + }); +} +function processReserveBankStatus(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpError = (err) => incrementReserveRetry(ws, reservePub, err); + yield errors.guardOperationException(() => processReserveBankStatusImpl(ws, reservePub), onOpError); + }); +} +exports.processReserveBankStatus = processReserveBankStatus; +function processReserveBankStatusImpl(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + switch (reserve === null || reserve === void 0 ? void 0 : reserve.reserveStatus) { + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + case dbTypes.ReserveRecordStatus.REGISTERING_BANK: + break; + default: + return; } - logger.trace("session:", refreshSession); - logger.trace("reveal response:", resp); - if (resp.status !== 200) { - console.error("error: /refresh/reveal returned status " + resp.status); + const bankStatusUrl = reserve.bankWithdrawStatusUrl; + if (!bankStatusUrl) { return; } - const respJson = yield resp.json(); - if (!respJson.ev_sigs || !Array.isArray(respJson.ev_sigs)) { - console.error("/refresh/reveal did not contain ev_sigs"); - return; + const statusResp = yield ws.http.get(bankStatusUrl); + if (statusResp.status !== 200) { + throw Error(`unexpected status ${statusResp.status} for bank status query`); } - const coins = []; - for (let i = 0; i < respJson.ev_sigs.length; i++) { - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - refreshSession.exchangeBaseUrl, - refreshSession.newDenoms[i], - ]); - if (!denom) { - console.error("denom not found"); - continue; + const status = talerTypes.codecForWithdrawOperationStatusResponse().decode(yield statusResp.json()); + ws.notify({ type: "wildcard" /* Wildcard */ }); + if (status.selection_done) { + if (reserve.reserveStatus === dbTypes.ReserveRecordStatus.REGISTERING_BANK) { + yield registerReserveWithBank(ws, reservePub); + return yield processReserveBankStatus(ws, reservePub); } - const pc = refreshSession.planchetsForGammas[refreshSession.norevealIndex][i]; - const denomSig = yield ws.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig, pc.blindingKey, denom.denomPub); - const coin = { - blindingKey: pc.blindingKey, - coinPriv: pc.privateKey, - coinPub: pc.publicKey, - currentAmount: denom.value, - denomPub: denom.denomPub, - denomPubHash: denom.denomPubHash, - denomSig, - exchangeBaseUrl: refreshSession.exchangeBaseUrl, - reservePub: undefined, - status: dbTypes.CoinStatus.Fresh, - coinIndex: -1, - withdrawSessionId: "", - }; - coins.push(coin); } - yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.refreshGroups], (tx) => __awaiter(this, void 0, void 0, function* () { - const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!rg) { - console.log("no refresh session found"); - return; - } - const rs = rg.refreshSessionPerCoin[coinIndex]; - if (!rs) { - return; - } - if (rs.finishedTimestamp) { - console.log("refresh session already finished"); - return; - } - rs.finishedTimestamp = time.getTimestampNow(); - rg.finishedPerCoin[coinIndex] = true; - let allDone = true; - for (const f of rg.finishedPerCoin) { - if (!f) { - allDone = false; - break; + else { + yield registerReserveWithBank(ws, reservePub); + return yield processReserveBankStatus(ws, reservePub); + } + if (status.transfer_done) { + yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, (r) => { + switch (r.reserveStatus) { + case dbTypes.ReserveRecordStatus.REGISTERING_BANK: + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + break; + default: + return; } - } - if (allDone) { - rg.timestampFinished = time.getTimestampNow(); - rg.retryInfo = dbTypes.initRetryInfo(false); - } - for (let coin of coins) { - yield tx.put(dbTypes.Stores.coins, coin); - } - yield tx.put(dbTypes.Stores.refreshGroups, rg); - })); - console.log("refresh finished (end of reveal)"); - ws.notify({ - type: "refresh-revealed" /* RefreshRevealed */, - }); + const now = time.getTimestampNow(); + r.timestampConfirmed = now; + r.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; + r.retryInfo = dbTypes.initRetryInfo(); + return r; + }); + yield processReserveImpl(ws, reservePub, true); + } + else { + yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, (r) => { + switch (r.reserveStatus) { + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + break; + default: + return; + } + r.bankWithdrawConfirmUrl = status.confirm_transfer_url; + return r; + }); + yield incrementReserveRetry(ws, reservePub, undefined); + } + ws.notify({ type: "wildcard" /* Wildcard */ }); }); } -function incrementRefreshRetry(ws, refreshGroupId, err) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); +function incrementReserveRetry(ws, reservePub, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.reserves], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.reserves, reservePub); if (!r) { return; } if (!r.retryInfo) { return; } + console.log("updating retry info"); + console.log("before", r.retryInfo); r.retryInfo.retryCounter++; dbTypes.updateRetryInfoTimeout(r.retryInfo); + console.log("after", r.retryInfo); r.lastError = err; - yield tx.put(dbTypes.Stores.refreshGroups, r); + yield tx.put(dbTypes.Stores.reserves, r); })); - ws.notify({ type: "refresh-operation-error" /* RefreshOperationError */ }); + if (err) { + ws.notify({ + type: "reserve-error" /* ReserveOperationError */, + operationError: err, + }); + } }); } -function processRefreshGroup(ws, refreshGroupId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.memoProcessRefresh.memo(refreshGroupId, () => __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => incrementRefreshRetry(ws, refreshGroupId, e); - return yield errors.guardOperationException(() => __awaiter(this, void 0, void 0, function* () { return yield processRefreshGroupImpl(ws, refreshGroupId, forceNow); }), onOpErr); +/** + * Update the information about a reserve that is stored in the wallet + * by quering the reserve's exchange. + */ +function updateReserve(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + if (!reserve) { + throw Error("reserve not in db"); + } + if (reserve.timestampConfirmed === undefined) { + throw Error("reserve not confirmed yet"); + } + if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.QUERYING_STATUS) { + return; + } + const reqUrl = new URL(`reserves/${reservePub}`, reserve.exchangeBaseUrl); + let resp; + try { + resp = yield ws.http.get(reqUrl.href); + console.log("got reserves/${RESERVE_PUB} response", yield resp.json()); + if (resp.status === 404) { + const m = "reserve not known to the exchange yet"; + throw new errors.OperationFailedError({ + type: "waiting", + message: m, + details: {}, + }); + } + if (resp.status !== 200) { + throw Error(`unexpected status code ${resp.status} for reserve/status`); + } + } + catch (e) { + logger.trace("caught exception for reserve/status"); + const m = e.message; + const opErr = { + type: "network", + details: {}, + message: m, + }; + yield incrementReserveRetry(ws, reservePub, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + const respJson = yield resp.json(); + const reserveInfo = ReserveStatus.codecForReserveStatus().decode(respJson); + const balance = amounts.Amounts.parseOrThrow(reserveInfo.balance); + const currency = balance.currency; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.reserves, dbTypes.Stores.reserveUpdatedEvents], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.reserves, reservePub); + if (!r) { + return; + } + if (r.reserveStatus !== dbTypes.ReserveRecordStatus.QUERYING_STATUS) { + return; + } + const newHistoryTransactions = reserveInfo.history.slice(r.reserveTransactions.length); + const reserveUpdateId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); + const reconciled = reserveHistoryUtil.reconcileReserveHistory(r.reserveTransactions, reserveInfo.history); + console.log("reconciled history:", JSON.stringify(reconciled, undefined, 2)); + const summary = reserveHistoryUtil.summarizeReserveHistory(reconciled.updatedLocalHistory, currency); + console.log("summary", summary); + if (reconciled.newAddedItems.length + reconciled.newMatchedItems.length != + 0) { + const reserveUpdate = { + reservePub: r.reservePub, + timestamp: time.getTimestampNow(), + amountReserveBalance: amounts.Amounts.stringify(balance), + amountExpected: amounts.Amounts.stringify(summary.awaitedReserveAmount), + newHistoryTransactions, + reserveUpdateId, + }; + yield tx.put(dbTypes.Stores.reserveUpdatedEvents, reserveUpdate); + r.reserveStatus = dbTypes.ReserveRecordStatus.WITHDRAWING; + r.retryInfo = dbTypes.initRetryInfo(); + } + else { + r.reserveStatus = dbTypes.ReserveRecordStatus.DORMANT; + r.retryInfo = dbTypes.initRetryInfo(false); + } + r.lastSuccessfulStatusQuery = time.getTimestampNow(); + r.reserveTransactions = reconciled.updatedLocalHistory; + r.lastError = undefined; + yield tx.put(dbTypes.Stores.reserves, r); })); + ws.notify({ type: "reserve-updated" /* ReserveUpdated */ }); }); } -exports.processRefreshGroup = processRefreshGroup; -function resetRefreshGroupRetry(ws, refreshSessionId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.refreshGroups, refreshSessionId, x => { - if (x.retryInfo.active) { - x.retryInfo = dbTypes.initRetryInfo(); +function processReserveImpl(ws, reservePub, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + if (!reserve) { + console.log("not processing reserve: reserve does not exist"); + return; + } + if (!forceNow) { + const now = time.getTimestampNow(); + if (reserve.retryInfo.nextRetry.t_ms > now.t_ms) { + logger.trace("processReserve retry not due yet"); + return; } - return x; - }); - }); -} -function processRefreshGroupImpl(ws, refreshGroupId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { - if (forceNow) { - yield resetRefreshGroupRetry(ws, refreshGroupId); } - const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { - return; + else { + yield resetReserveRetry(ws, reservePub); } - if (refreshGroup.timestampFinished) { - return; + logger.trace(`Processing reserve ${reservePub} with status ${reserve.reserveStatus}`); + switch (reserve.reserveStatus) { + case dbTypes.ReserveRecordStatus.UNCONFIRMED: + // nothing to do + break; + case dbTypes.ReserveRecordStatus.REGISTERING_BANK: + yield processReserveBankStatus(ws, reservePub); + return yield processReserveImpl(ws, reservePub, true); + case dbTypes.ReserveRecordStatus.QUERYING_STATUS: + yield updateReserve(ws, reservePub); + return yield processReserveImpl(ws, reservePub, true); + case dbTypes.ReserveRecordStatus.WITHDRAWING: + yield depleteReserve(ws, reservePub); + break; + case dbTypes.ReserveRecordStatus.DORMANT: + // nothing to do + break; + case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: + yield processReserveBankStatus(ws, reservePub); + break; + default: + console.warn("unknown reserve record status:", reserve.reserveStatus); + assertUnreachable_1.assertUnreachable(reserve.reserveStatus); + break; } - const ps = refreshGroup.oldCoinPubs.map((x, i) => processRefreshSession(ws, refreshGroupId, i)); - yield Promise.all(ps); - logger.trace("refresh finished"); }); } -function processRefreshSession(ws, refreshGroupId, coinIndex) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace(`processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`); - let refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { - return; - } - if (refreshGroup.finishedPerCoin[coinIndex]) { - return; - } - if (!refreshGroup.refreshSessionPerCoin[coinIndex]) { - yield refreshCreateSession(ws, refreshGroupId, coinIndex); - refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); - if (!refreshGroup) { +function confirmReserve(ws, req) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const now = time.getTimestampNow(); + yield ws.db.mutate(dbTypes.Stores.reserves, req.reservePub, (reserve) => { + if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.UNCONFIRMED) { return; } - } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; - if (!refreshSession) { - if (!refreshGroup.finishedPerCoin[coinIndex]) { - throw Error("BUG: refresh session was not created and coin not marked as finished"); - } - return; - } - if (refreshSession.norevealIndex === undefined) { - yield refreshMelt(ws, refreshGroupId, coinIndex); - } - yield refreshReveal(ws, refreshGroupId, coinIndex); + reserve.timestampConfirmed = now; + reserve.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; + reserve.retryInfo = dbTypes.initRetryInfo(); + return reserve; + }); + ws.notify({ type: "reserve-updated" /* ReserveUpdated */ }); + processReserve(ws, req.reservePub, true).catch((e) => { + console.log("processing reserve (after confirmReserve) failed:", e); + }); }); } -/** - * Create a refresh group for a list of coins. - */ -function createRefreshGroup(tx, oldCoinPubs, reason) { - return __awaiter(this, void 0, void 0, function* () { - const refreshGroupId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); - const refreshGroup = { - timestampFinished: undefined, - finishedPerCoin: oldCoinPubs.map(x => false), - lastError: undefined, - lastErrorPerCoin: {}, - oldCoinPubs: oldCoinPubs.map(x => x.coinPub), - reason, - refreshGroupId, - refreshSessionPerCoin: oldCoinPubs.map(x => undefined), - retryInfo: dbTypes.initRetryInfo(), - }; - yield tx.put(dbTypes.Stores.refreshGroups, refreshGroup); +exports.confirmReserve = confirmReserve; +function makePlanchet(ws, reserve, denom) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield ws.cryptoApi.createPlanchet({ + denomPub: denom.denomPub, + feeWithdraw: denom.feeWithdraw, + reservePriv: reserve.reservePriv, + reservePub: reserve.reservePub, + value: denom.value, + }); return { - refreshGroupId, + blindingKey: r.blindingKey, + coinEv: r.coinEv, + coinPriv: r.coinPriv, + coinPub: r.coinPub, + coinValue: r.coinValue, + denomPub: r.denomPub, + denomPubHash: r.denomPubHash, + isFromTip: false, + reservePub: r.reservePub, + withdrawSig: r.withdrawSig, + coinEvHash: r.coinEvHash, }; }); } -exports.createRefreshGroup = createRefreshGroup; - -}); - -unwrapExports(refresh); -var refresh_1 = refresh.getTotalRefreshCost; -var refresh_2 = refresh.processRefreshGroup; -var refresh_3 = refresh.createRefreshGroup; - -var http = createCommonjsModule(function (module, exports) { -/* - This file is part of TALER - (C) 2016 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var HttpResponseStatus; -(function (HttpResponseStatus) { - HttpResponseStatus[HttpResponseStatus["Ok"] = 200] = "Ok"; - HttpResponseStatus[HttpResponseStatus["Gone"] = 210] = "Gone"; -})(HttpResponseStatus = exports.HttpResponseStatus || (exports.HttpResponseStatus = {})); /** - * Headers, roughly modeled after the fetch API's headers object. + * Withdraw coins from a reserve until it is empty. + * + * When finished, marks the reserve as depleted by setting + * the depleted timestamp. */ -class Headers { - constructor() { - this.headerMap = new Map(); - } - get(name) { - const r = this.headerMap.get(name.toLowerCase()); - if (r) { - return r; +function depleteReserve(ws, reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + if (!reserve) { + return; } - return null; - } - set(name, value) { - const normalizedName = name.toLowerCase(); - const existing = this.headerMap.get(normalizedName); - if (existing !== undefined) { - this.headerMap.set(normalizedName, existing + "," + value); + if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.WITHDRAWING) { + return; } - else { - this.headerMap.set(normalizedName, value); + logger.trace(`depleting reserve ${reservePub}`); + const summary = reserveHistoryUtil.summarizeReserveHistory(reserve.reserveTransactions, reserve.currency); + const withdrawAmount = summary.unclaimedReserveAmount; + logger.trace(`getting denom list`); + const denomsForWithdraw = yield withdraw.getVerifiedWithdrawDenomList(ws, reserve.exchangeBaseUrl, withdrawAmount); + logger.trace(`got denom list`); + if (denomsForWithdraw.length === 0) { + // Only complain about inability to withdraw if we + // didn't withdraw before. + if (amounts.Amounts.isZero(summary.withdrawnAmount)) { + const m = `Unable to withdraw from reserve, no denominations are available to withdraw.`; + const opErr = { + type: "internal", + message: m, + details: {}, + }; + yield incrementReserveRetry(ws, reserve.reservePub, opErr); + console.log(m); + throw new errors.OperationFailedAndReportedError(opErr); + } + return; } - } -} -exports.Headers = Headers; -/** - * An implementation of the [[HttpRequestLibrary]] using the - * browser's XMLHttpRequest. - */ -class BrowserHttpLib { - req(method, url, requestBody, options) { - return new Promise((resolve, reject) => { - var _a; - const myRequest = new XMLHttpRequest(); - myRequest.open(method, url); - if ((_a = options) === null || _a === void 0 ? void 0 : _a.headers) { - for (const headerName in options.headers) { - myRequest.setRequestHeader(headerName, options.headers[headerName]); - } + logger.trace("selected denominations"); + const withdrawalGroupId = talerCrypto.encodeCrock(naclFast.randomBytes(32)); + const totalCoinValue = amounts.Amounts.sum(denomsForWithdraw.map((x) => x.value)) + .amount; + const planchets = []; + for (const d of denomsForWithdraw) { + const p = yield makePlanchet(ws, reserve, d); + planchets.push(p); + } + const withdrawalRecord = { + withdrawalGroupId: withdrawalGroupId, + exchangeBaseUrl: reserve.exchangeBaseUrl, + source: { + type: "reserve" /* Reserve */, + reservePub: reserve.reservePub, + }, + rawWithdrawalAmount: withdrawAmount, + timestampStart: time.getTimestampNow(), + denoms: denomsForWithdraw.map((x) => x.denomPub), + withdrawn: denomsForWithdraw.map((x) => false), + planchets, + totalCoinValue, + retryInfo: dbTypes.initRetryInfo(), + lastErrorPerCoin: {}, + lastError: undefined, + }; + const totalCoinWithdrawFee = amounts.Amounts.sum(denomsForWithdraw.map((x) => x.feeWithdraw)).amount; + const totalWithdrawAmount = amounts.Amounts.add(totalCoinValue, totalCoinWithdrawFee) + .amount; + const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.withdrawalGroups, dbTypes.Stores.reserves], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const newReserve = yield tx.get(dbTypes.Stores.reserves, reservePub); + if (!newReserve) { + return false; } - if (requestBody) { - myRequest.send(requestBody); + if (newReserve.reserveStatus !== dbTypes.ReserveRecordStatus.WITHDRAWING) { + return false; } - else { - myRequest.send(); + const newSummary = reserveHistoryUtil.summarizeReserveHistory(newReserve.reserveTransactions, newReserve.currency); + if (amounts.Amounts.cmp(newSummary.unclaimedReserveAmount, totalWithdrawAmount) < 0) { + // Something must have happened concurrently! + logger.error("aborting withdrawal session, likely concurrent withdrawal happened"); + return false; + } + for (let i = 0; i < planchets.length; i++) { + const amt = amounts.Amounts.add(denomsForWithdraw[i].value, denomsForWithdraw[i].feeWithdraw).amount; + newReserve.reserveTransactions.push({ + type: "withdraw" /* Withdraw */, + expectedAmount: amt, + }); } - myRequest.onerror = e => { - console.error("http request error"); - reject(Error("could not make XMLHttpRequest")); - }; - myRequest.addEventListener("readystatechange", e => { - if (myRequest.readyState === XMLHttpRequest.DONE) { - if (myRequest.status === 0) { - reject(Error("HTTP Request failed (status code 0, maybe URI scheme is wrong?)")); - return; - } - const makeJson = () => __awaiter(this, void 0, void 0, function* () { - let responseJson; - try { - responseJson = JSON.parse(myRequest.responseText); - } - catch (e) { - throw Error("Invalid JSON from HTTP response"); - } - if (responseJson === null || typeof responseJson !== "object") { - throw Error("Invalid JSON from HTTP response"); - } - return responseJson; - }); - const headers = myRequest.getAllResponseHeaders(); - const arr = headers.trim().split(/[\r\n]+/); - // Create a map of header names to values - const headerMap = new Headers(); - arr.forEach(function (line) { - const parts = line.split(": "); - const header = parts.shift(); - const value = parts.join(": "); - headerMap.set(header, value); - }); - const resp = { - status: myRequest.status, - headers: headerMap, - json: makeJson, - text: () => __awaiter(this, void 0, void 0, function* () { return myRequest.responseText; }), - }; - resolve(resp); - } + newReserve.reserveStatus = dbTypes.ReserveRecordStatus.DORMANT; + newReserve.retryInfo = dbTypes.initRetryInfo(false); + yield tx.put(dbTypes.Stores.reserves, newReserve); + yield tx.put(dbTypes.Stores.withdrawalGroups, withdrawalRecord); + return true; + })); + if (success) { + console.log("processing new withdraw group"); + ws.notify({ + type: "withdraw-group-created" /* WithdrawGroupCreated */, + withdrawalGroupId: withdrawalGroupId, }); + yield withdraw.processWithdrawGroup(ws, withdrawalGroupId); + } + else { + console.trace("withdraw session already existed"); + } + }); +} +function createTalerWithdrawReserve(ws, talerWithdrawUri, selectedExchange) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const withdrawInfo = yield withdraw.getBankWithdrawalInfo(ws, talerWithdrawUri); + const exchangeWire = yield exchanges.getExchangePaytoUri(ws, selectedExchange, withdrawInfo.wireTypes); + const reserve = yield createReserve(ws, { + amount: withdrawInfo.amount, + bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl, + exchange: selectedExchange, + senderWire: withdrawInfo.senderWire, + exchangeWire: exchangeWire, }); - } - get(url, opt) { - return this.req("get", url, undefined, opt); - } - postJson(url, body, opt) { - return this.req("post", url, JSON.stringify(body), opt); - } - stop() { - // Nothing to do - } + // We do this here, as the reserve should be registered before we return, + // so that we can redirect the user to the bank's status page. + yield processReserveBankStatus(ws, reserve.reservePub); + console.log("acceptWithdrawal: returning"); + return { + reservePub: reserve.reservePub, + confirmTransferUrl: withdrawInfo.confirmTransferUrl, + }; + }); } -exports.BrowserHttpLib = BrowserHttpLib; +exports.createTalerWithdrawReserve = createTalerWithdrawReserve; }); -unwrapExports(http); -var http_1 = http.HttpResponseStatus; -var http_2 = http.Headers; -var http_3 = http.BrowserHttpLib; +unwrapExports(reserves); +var reserves_1 = reserves.createReserve; +var reserves_2 = reserves.forceQueryReserve; +var reserves_3 = reserves.processReserve; +var reserves_4 = reserves.processReserveBankStatus; +var reserves_5 = reserves.confirmReserve; +var reserves_6 = reserves.createTalerWithdrawReserve; -var refund = createCommonjsModule(function (module, exports) { +var refresh = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler - (C) 2019-2019 Taler Systems S.A. + (C) 2019 GNUnet e.V. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -9001,27 +7958,10 @@ var refund = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); @@ -9029,426 +7969,746 @@ const Amounts = __importStar(amounts); -const logger = new logging.Logger("refund.ts"); -function incrementPurchaseQueryRefundRetry(ws, proposalId, err) { - return __awaiter(this, void 0, void 0, function* () { - console.log("incrementing purchase refund query retry with error", err); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const pr = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!pr) { +const logger = new logging.Logger("refresh.ts"); +/** + * Get the amount that we lose when refreshing a coin of the given denomination + * with a certain amount left. + * + * If the amount left is zero, then the refresh cost + * is also considered to be zero. If a refresh isn't possible (e.g. due to lack of + * the right denominations), then the cost is the full amount left. + * + * Considers refresh fees, withdrawal fees after refresh and amounts too small + * to refresh. + */ +function getTotalRefreshCost(denoms, refreshedDenom, amountLeft) { + const withdrawAmount = amounts.Amounts.sub(amountLeft, refreshedDenom.feeRefresh) + .amount; + const withdrawDenoms = withdraw.getWithdrawDenomList(withdrawAmount, denoms); + const resultingAmount = amounts.Amounts.add(amounts.Amounts.getZero(withdrawAmount.currency), ...withdrawDenoms.map((d) => d.value)).amount; + const totalCost = amounts.Amounts.sub(amountLeft, resultingAmount).amount; + logger.trace("total refresh cost for", helpers.amountToPretty(amountLeft), "is", helpers.amountToPretty(totalCost)); + return totalCost; +} +exports.getTotalRefreshCost = getTotalRefreshCost; +/** + * Create a refresh session inside a refresh group. + */ +function refreshCreateSession(ws, refreshGroupId, coinIndex) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + logger.trace(`creating refresh session for coin ${coinIndex} in refresh group ${refreshGroupId}`); + const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { + return; + } + if (refreshGroup.finishedPerCoin[coinIndex]) { + return; + } + const existingRefreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + if (existingRefreshSession) { + return; + } + const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex]; + const coin = yield ws.db.get(dbTypes.Stores.coins, oldCoinPub); + if (!coin) { + throw Error("Can't refresh, coin not found"); + } + const exchange = yield exchanges.updateExchangeFromUrl(ws, coin.exchangeBaseUrl); + if (!exchange) { + throw Error("db inconsistent: exchange of coin not found"); + } + const oldDenom = yield ws.db.get(dbTypes.Stores.denominations, [ + exchange.baseUrl, + coin.denomPub, + ]); + if (!oldDenom) { + throw Error("db inconsistent: denomination for coin not found"); + } + const availableDenoms = yield ws.db + .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) + .toArray(); + const availableAmount = amounts.Amounts.sub(coin.currentAmount, oldDenom.feeRefresh) + .amount; + const newCoinDenoms = withdraw.getWithdrawDenomList(availableAmount, availableDenoms); + if (newCoinDenoms.length === 0) { + logger.trace(`not refreshing, available amount ${helpers.amountToPretty(availableAmount)} too small`); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!rg) { + return; + } + rg.finishedPerCoin[coinIndex] = true; + let allDone = true; + for (const f of rg.finishedPerCoin) { + if (!f) { + allDone = false; + break; + } + } + if (allDone) { + rg.timestampFinished = time.getTimestampNow(); + rg.retryInfo = dbTypes.initRetryInfo(false); + } + yield tx.put(dbTypes.Stores.refreshGroups, rg); + })); + ws.notify({ type: "refresh-unwarranted" /* RefreshUnwarranted */ }); + return; + } + const refreshSession = yield ws.cryptoApi.createRefreshSession(exchange.baseUrl, 3, coin, newCoinDenoms, oldDenom.feeRefresh); + // Store refresh session and subtract refreshed amount from + // coin in the same transaction. + yield ws.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups, dbTypes.Stores.coins], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const c = yield tx.get(dbTypes.Stores.coins, coin.coinPub); + if (!c) { + throw Error("coin not found, but marked for refresh"); + } + const r = amounts.Amounts.sub(c.currentAmount, refreshSession.amountRefreshInput); + if (r.saturated) { + console.log("can't refresh coin, no amount left"); return; } - if (!pr.refundStatusRetryInfo) { + c.currentAmount = r.amount; + c.status = "dormant" /* Dormant */; + const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!rg) { + return; + } + if (rg.refreshSessionPerCoin[coinIndex]) { + return; + } + rg.refreshSessionPerCoin[coinIndex] = refreshSession; + yield tx.put(dbTypes.Stores.refreshGroups, rg); + yield tx.put(dbTypes.Stores.coins, c); + })); + logger.info(`created refresh session for coin #${coinIndex} in ${refreshGroupId}`); + ws.notify({ type: "refresh-started" /* RefreshStarted */ }); + }); +} +function refreshMelt(ws, refreshGroupId, coinIndex) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { + return; + } + const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + if (!refreshSession) { + return; + } + if (refreshSession.norevealIndex !== undefined) { + return; + } + const coin = yield ws.db.get(dbTypes.Stores.coins, refreshSession.meltCoinPub); + if (!coin) { + console.error("can't melt coin, it does not exist"); + return; + } + const reqUrl = new URL(`coins/${coin.coinPub}/melt`, refreshSession.exchangeBaseUrl); + const meltReq = { + coin_pub: coin.coinPub, + confirm_sig: refreshSession.confirmSig, + denom_pub_hash: coin.denomPubHash, + denom_sig: coin.denomSig, + rc: refreshSession.hash, + value_with_fee: amounts.Amounts.stringify(refreshSession.amountRefreshInput), + }; + logger.trace(`melt request for coin:`, meltReq); + const resp = yield ws.http.postJson(reqUrl.href, meltReq); + if (resp.status !== 200) { + console.log(`got status ${resp.status} for refresh/melt`); + try { + const respJson = yield resp.json(); + console.log(`body of refresh/melt error response:`, JSON.stringify(respJson, undefined, 2)); + } + catch (e) { + console.log(`body of refresh/melt error response is not JSON`); + } + throw Error(`unexpected status code ${resp.status} for refresh/melt`); + } + const respJson = yield resp.json(); + logger.trace("melt response:", respJson); + if (resp.status !== 200) { + console.error(respJson); + throw Error("refresh failed"); + } + const norevealIndex = respJson.noreveal_index; + if (typeof norevealIndex !== "number") { + throw Error("invalid response"); + } + refreshSession.norevealIndex = norevealIndex; + yield ws.db.mutate(dbTypes.Stores.refreshGroups, refreshGroupId, (rg) => { + const rs = rg.refreshSessionPerCoin[coinIndex]; + if (!rs) { return; } - pr.refundStatusRetryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(pr.refundStatusRetryInfo); - pr.lastRefundStatusError = err; - yield tx.put(dbTypes.Stores.purchases, pr); - })); - ws.notify({ type: "refund-status-error" /* RefundStatusOperationError */ }); - }); -} -function incrementPurchaseApplyRefundRetry(ws, proposalId, err) { - return __awaiter(this, void 0, void 0, function* () { - console.log("incrementing purchase refund apply retry with error", err); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const pr = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!pr) { + if (rs.norevealIndex !== undefined) { return; } - if (!pr.refundApplyRetryInfo) { + if (rs.finishedTimestamp) { return; } - pr.refundApplyRetryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(pr.refundApplyRetryInfo); - pr.lastRefundApplyError = err; - yield tx.put(dbTypes.Stores.purchases, pr); - })); - ws.notify({ type: "refund-apply-error" /* RefundApplyOperationError */ }); + rs.norevealIndex = norevealIndex; + return rg; + }); + ws.notify({ + type: "refresh-melted" /* RefreshMelted */, + }); }); } -function getFullRefundFees(ws, refundPermissions) { - return __awaiter(this, void 0, void 0, function* () { - if (refundPermissions.length === 0) { - throw Error("no refunds given"); +function refreshReveal(ws, refreshGroupId, coinIndex) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { + return; } - const coin0 = yield ws.db.get(dbTypes.Stores.coins, refundPermissions[0].coin_pub); - if (!coin0) { - throw Error("coin not found"); + const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + if (!refreshSession) { + return; } - let feeAcc = Amounts.getZero(Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency); - const denoms = yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, coin0.exchangeBaseUrl) - .toArray(); - for (const rp of refundPermissions) { - const coin = yield ws.db.get(dbTypes.Stores.coins, rp.coin_pub); - if (!coin) { - throw Error("coin not found"); - } + const norevealIndex = refreshSession.norevealIndex; + if (norevealIndex === undefined) { + throw Error("can't reveal without melting first"); + } + const privs = Array.from(refreshSession.transferPrivs); + privs.splice(norevealIndex, 1); + const planchets = refreshSession.planchetsForGammas[norevealIndex]; + if (!planchets) { + throw Error("refresh index error"); + } + const meltCoinRecord = yield ws.db.get(dbTypes.Stores.coins, refreshSession.meltCoinPub); + if (!meltCoinRecord) { + throw Error("inconsistent database"); + } + const evs = planchets.map((x) => x.coinEv); + const linkSigs = []; + for (let i = 0; i < refreshSession.newDenoms.length; i++) { + const linkSig = yield ws.cryptoApi.signCoinLink(meltCoinRecord.coinPriv, refreshSession.newDenomHashes[i], refreshSession.meltCoinPub, refreshSession.transferPubs[norevealIndex], planchets[i].coinEv); + linkSigs.push(linkSig); + } + const req = { + coin_evs: evs, + new_denoms_h: refreshSession.newDenomHashes, + rc: refreshSession.hash, + transfer_privs: privs, + transfer_pub: refreshSession.transferPubs[norevealIndex], + link_sigs: linkSigs, + }; + const reqUrl = new URL(`refreshes/${refreshSession.hash}/reveal`, refreshSession.exchangeBaseUrl); + logger.trace("reveal request:", req); + let resp; + try { + resp = yield ws.http.postJson(reqUrl.href, req); + } + catch (e) { + console.error("got error during /refresh/reveal request"); + console.error(e); + return; + } + logger.trace("session:", refreshSession); + logger.trace("reveal response:", resp); + if (resp.status !== 200) { + console.error("error: /refresh/reveal returned status " + resp.status); + return; + } + const respJson = yield resp.json(); + if (!respJson.ev_sigs || !Array.isArray(respJson.ev_sigs)) { + console.error("/refresh/reveal did not contain ev_sigs"); + return; + } + const coins = []; + for (let i = 0; i < respJson.ev_sigs.length; i++) { const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - coin0.exchangeBaseUrl, - coin.denomPub, + refreshSession.exchangeBaseUrl, + refreshSession.newDenoms[i], ]); if (!denom) { - throw Error(`denom not found (${coin.denomPub})`); + console.error("denom not found"); + continue; } - // FIXME: this assumes that the refund already happened. - // When it hasn't, the refresh cost is inaccurate. To fix this, - // we need introduce a flag to tell if a coin was refunded or - // refreshed normally (and what about incremental refunds?) - const refundAmount = Amounts.parseOrThrow(rp.refund_amount); - const refundFee = Amounts.parseOrThrow(rp.refund_fee); - const refreshCost = refresh.getTotalRefreshCost(denoms, denom, Amounts.sub(refundAmount, refundFee).amount); - feeAcc = Amounts.add(feeAcc, refreshCost, refundFee).amount; + const pc = refreshSession.planchetsForGammas[norevealIndex][i]; + const denomSig = yield ws.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig, pc.blindingKey, denom.denomPub); + const coin = { + blindingKey: pc.blindingKey, + coinPriv: pc.privateKey, + coinPub: pc.publicKey, + currentAmount: denom.value, + denomPub: denom.denomPub, + denomPubHash: denom.denomPubHash, + denomSig, + exchangeBaseUrl: refreshSession.exchangeBaseUrl, + status: "fresh" /* Fresh */, + coinSource: { + type: "refresh" /* Refresh */, + oldCoinPub: refreshSession.meltCoinPub, + }, + suspended: false, + }; + coins.push(coin); } - return feeAcc; - }); -} -exports.getFullRefundFees = getFullRefundFees; -function acceptRefundResponse(ws, proposalId, refundResponse, reason) { - return __awaiter(this, void 0, void 0, function* () { - const refundPermissions = refundResponse.refund_permissions; - let numNewRefunds = 0; - const refundGroupId = talerCrypto.encodeCrock(naclFast.randomBytes(32)); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!p) { - console.error("purchase not found, not adding refunds"); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const rg = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!rg) { + console.log("no refresh session found"); return; } - if (!p.refundStatusRequested) { + const rs = rg.refreshSessionPerCoin[coinIndex]; + if (!rs) { return; } - for (const perm of refundPermissions) { - const isDone = p.refundState.refundsDone[perm.merchant_sig]; - const isPending = p.refundState.refundsPending[perm.merchant_sig]; - if (!isDone && !isPending) { - p.refundState.refundsPending[perm.merchant_sig] = { - perm, - refundGroupId, - }; - numNewRefunds++; - } + if (rs.finishedTimestamp) { + console.log("refresh session already finished"); + return; } - // Are we done with querying yet, or do we need to do another round - // after a retry delay? - let queryDone = true; - if (numNewRefunds === 0) { - if (p.autoRefundDeadline && - p.autoRefundDeadline.t_ms > time.getTimestampNow().t_ms) { - queryDone = false; + rs.finishedTimestamp = time.getTimestampNow(); + rg.finishedPerCoin[coinIndex] = true; + let allDone = true; + for (const f of rg.finishedPerCoin) { + if (!f) { + allDone = false; + break; } } - if (queryDone) { - p.timestampLastRefundStatus = time.getTimestampNow(); - p.lastRefundStatusError = undefined; - p.refundStatusRetryInfo = dbTypes.initRetryInfo(); - p.refundStatusRequested = false; - console.log("refund query done"); - } - else { - // No error, but we need to try again! - p.timestampLastRefundStatus = time.getTimestampNow(); - p.refundStatusRetryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(p.refundStatusRetryInfo); - p.lastRefundStatusError = undefined; - console.log("refund query not done"); + if (allDone) { + rg.timestampFinished = time.getTimestampNow(); + rg.retryInfo = dbTypes.initRetryInfo(false); } - if (numNewRefunds > 0) { - const now = time.getTimestampNow(); - p.lastRefundApplyError = undefined; - p.refundApplyRetryInfo = dbTypes.initRetryInfo(); - p.refundState.refundGroups.push({ - timestampQueried: now, - reason, - }); + for (const coin of coins) { + yield tx.put(dbTypes.Stores.coins, coin); } - yield tx.put(dbTypes.Stores.purchases, p); + yield tx.put(dbTypes.Stores.refreshGroups, rg); })); + console.log("refresh finished (end of reveal)"); ws.notify({ - type: "refund-queried" /* RefundQueried */, + type: "refresh-revealed" /* RefreshRevealed */, }); - if (numNewRefunds > 0) { - yield processPurchaseApplyRefund(ws, proposalId); - } }); } -exports.acceptRefundResponse = acceptRefundResponse; -function startRefundQuery(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!p) { - console.log("no purchase found for refund URL"); - return false; +function incrementRefreshRetry(ws, refreshGroupId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!r) { + return; } - p.refundStatusRequested = true; - p.lastRefundStatusError = undefined; - p.refundStatusRetryInfo = dbTypes.initRetryInfo(); - yield tx.put(dbTypes.Stores.purchases, p); - return true; + if (!r.retryInfo) { + return; + } + r.retryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(r.retryInfo); + r.lastError = err; + yield tx.put(dbTypes.Stores.refreshGroups, r); })); - if (!success) { - return; - } - ws.notify({ - type: "refund-started" /* RefundStarted */, - }); - yield processPurchaseQueryRefund(ws, proposalId); - }); -} -/** - * Accept a refund, return the contract hash for the contract - * that was involved in the refund. - */ -function applyRefund(ws, talerRefundUri) { - return __awaiter(this, void 0, void 0, function* () { - const parseResult = taleruri.parseRefundUri(talerRefundUri); - console.log("applying refund", parseResult); - if (!parseResult) { - throw Error("invalid refund URI"); - } - const purchase = yield ws.db.getIndexed(dbTypes.Stores.purchases.orderIdIndex, [ - parseResult.merchantBaseUrl, - parseResult.orderId, - ]); - if (!purchase) { - throw Error(`no purchase for the taler://refund/ URI (${talerRefundUri}) was found`); - } - console.log("processing purchase for refund"); - yield startRefundQuery(ws, purchase.proposalId); - return purchase.contractData.contractTermsHash; + ws.notify({ type: "refresh-operation-error" /* RefreshOperationError */ }); }); } -exports.applyRefund = applyRefund; -function processPurchaseQueryRefund(ws, proposalId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => incrementPurchaseQueryRefundRetry(ws, proposalId, e); - yield errors.guardOperationException(() => processPurchaseQueryRefundImpl(ws, proposalId, forceNow), onOpErr); +function processRefreshGroup(ws, refreshGroupId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.memoProcessRefresh.memo(refreshGroupId, () => tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementRefreshRetry(ws, refreshGroupId, e); + return yield errors.guardOperationException(() => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield processRefreshGroupImpl(ws, refreshGroupId, forceNow); }), onOpErr); + })); }); } -exports.processPurchaseQueryRefund = processPurchaseQueryRefund; -function resetPurchaseQueryRefundRetry(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, x => { - if (x.refundStatusRetryInfo.active) { - x.refundStatusRetryInfo = dbTypes.initRetryInfo(); +exports.processRefreshGroup = processRefreshGroup; +function resetRefreshGroupRetry(ws, refreshSessionId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.refreshGroups, refreshSessionId, (x) => { + if (x.retryInfo.active) { + x.retryInfo = dbTypes.initRetryInfo(); } return x; }); }); } -function processPurchaseQueryRefundImpl(ws, proposalId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { +function processRefreshGroupImpl(ws, refreshGroupId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (forceNow) { - yield resetPurchaseQueryRefundRetry(ws, proposalId); + yield resetRefreshGroupRetry(ws, refreshGroupId); } - const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); - if (!purchase) { + const refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { return; } - if (!purchase.refundStatusRequested) { + if (refreshGroup.timestampFinished) { return; } - const refundUrlObj = new URL("refund", purchase.contractData.merchantBaseUrl); - refundUrlObj.searchParams.set("order_id", purchase.contractData.orderId); - const refundUrl = refundUrlObj.href; - let resp; - try { - resp = yield ws.http.get(refundUrl); + const ps = refreshGroup.oldCoinPubs.map((x, i) => processRefreshSession(ws, refreshGroupId, i)); + yield Promise.all(ps); + logger.trace("refresh finished"); + }); +} +function processRefreshSession(ws, refreshGroupId, coinIndex) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + logger.trace(`processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`); + let refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { + return; } - catch (e) { - console.error("error downloading refund permission", e); - throw e; + if (refreshGroup.finishedPerCoin[coinIndex]) { + return; } - if (resp.status !== 200) { - throw Error(`unexpected status code (${resp.status}) for /refund`); + if (!refreshGroup.refreshSessionPerCoin[coinIndex]) { + yield refreshCreateSession(ws, refreshGroupId, coinIndex); + refreshGroup = yield ws.db.get(dbTypes.Stores.refreshGroups, refreshGroupId); + if (!refreshGroup) { + return; + } } - const refundResponse = talerTypes.codecForMerchantRefundResponse().decode(yield resp.json()); - yield acceptRefundResponse(ws, proposalId, refundResponse, "normal-refund" /* NormalRefund */); + const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + if (!refreshSession) { + if (!refreshGroup.finishedPerCoin[coinIndex]) { + throw Error("BUG: refresh session was not created and coin not marked as finished"); + } + return; + } + if (refreshSession.norevealIndex === undefined) { + yield refreshMelt(ws, refreshGroupId, coinIndex); + } + yield refreshReveal(ws, refreshGroupId, coinIndex); }); } -function processPurchaseApplyRefund(ws, proposalId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => incrementPurchaseApplyRefundRetry(ws, proposalId, e); - yield errors.guardOperationException(() => processPurchaseApplyRefundImpl(ws, proposalId, forceNow), onOpErr); +/** + * Create a refresh group for a list of coins. + */ +function createRefreshGroup(tx, oldCoinPubs, reason) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const refreshGroupId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); + const refreshGroup = { + timestampFinished: undefined, + finishedPerCoin: oldCoinPubs.map((x) => false), + lastError: undefined, + lastErrorPerCoin: {}, + oldCoinPubs: oldCoinPubs.map((x) => x.coinPub), + reason, + refreshGroupId, + refreshSessionPerCoin: oldCoinPubs.map((x) => undefined), + retryInfo: dbTypes.initRetryInfo(), + }; + yield tx.put(dbTypes.Stores.refreshGroups, refreshGroup); + return { + refreshGroupId, + }; }); } -exports.processPurchaseApplyRefund = processPurchaseApplyRefund; -function resetPurchaseApplyRefundRetry(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, x => { - if (x.refundApplyRetryInfo.active) { - x.refundApplyRetryInfo = dbTypes.initRetryInfo(); +exports.createRefreshGroup = createRefreshGroup; + +}); + +unwrapExports(refresh); +var refresh_1 = refresh.getTotalRefreshCost; +var refresh_2 = refresh.processRefreshGroup; +var refresh_3 = refresh.createRefreshGroup; + +var recoup = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019-2020 Taler Systems SA + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + + + + + + + + + +function incrementRecoupRetry(ws, recoupGroupId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.recoupGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!r) { + return; } - return x; - }); + if (!r.retryInfo) { + return; + } + r.retryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(r.retryInfo); + r.lastError = err; + yield tx.put(dbTypes.Stores.recoupGroups, r); + })); + ws.notify({ type: "recoup-operation-error" /* RecoupOperationError */ }); }); } -function processPurchaseApplyRefundImpl(ws, proposalId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { - if (forceNow) { - yield resetPurchaseApplyRefundRetry(ws, proposalId); +function putGroupAsFinished(ws, tx, recoupGroup, coinIdx) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (recoupGroup.timestampFinished) { + return; } - const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); - if (!purchase) { - console.error("not submitting refunds, payment not found:"); + recoupGroup.recoupFinishedPerCoin[coinIdx] = true; + let allFinished = true; + for (const b of recoupGroup.recoupFinishedPerCoin) { + if (!b) { + allFinished = false; + } + } + if (allFinished) { + recoupGroup.timestampFinished = time.getTimestampNow(); + recoupGroup.retryInfo = dbTypes.initRetryInfo(false); + recoupGroup.lastError = undefined; + if (recoupGroup.scheduleRefreshCoins.length > 0) { + const refreshGroupId = yield refresh.createRefreshGroup(tx, recoupGroup.scheduleRefreshCoins.map((x) => ({ coinPub: x })), "recoup" /* Recoup */); + refresh.processRefreshGroup(ws, refreshGroupId.refreshGroupId).then((e) => { + console.error("error while refreshing after recoup", e); + }); + } + } + yield tx.put(dbTypes.Stores.recoupGroups, recoupGroup); + }); +} +function recoupTipCoin(ws, recoupGroupId, coinIdx, coin) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + // We can't really recoup a coin we got via tipping. + // Thus we just put the coin to sleep. + // FIXME: somehow report this to the user + yield ws.db.runWithWriteTransaction([dbTypes.Stores.recoupGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const recoupGroup = yield tx.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!recoupGroup) { + return; + } + if (recoupGroup.recoupFinishedPerCoin[coinIdx]) { + return; + } + yield putGroupAsFinished(ws, tx, recoupGroup, coinIdx); + })); + }); +} +function recoupWithdrawCoin(ws, recoupGroupId, coinIdx, coin, cs) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reservePub = cs.reservePub; + const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); + if (!reserve) { + // FIXME: We should at least emit some pending operation / warning for this? return; } - const pendingKeys = Object.keys(purchase.refundState.refundsPending); - if (pendingKeys.length === 0) { - console.log("no pending refunds"); + ws.notify({ + type: "recoup-started" /* RecoupStarted */, + }); + const recoupRequest = yield ws.cryptoApi.createRecoupRequest(coin); + const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl); + const resp = yield ws.http.postJson(reqUrl.href, recoupRequest); + const recoupConfirmation = yield errors.scrutinizeTalerJsonResponse(resp, talerTypes.codecForRecoupConfirmation()); + if (recoupConfirmation.reserve_pub !== reservePub) { + throw Error(`Coin's reserve doesn't match reserve on recoup`); + } + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, coin.exchangeBaseUrl); + if (!exchange) { + // FIXME: report inconsistency? return; } - const newRefundsDone = {}; - const newRefundsFailed = {}; - for (const pk of pendingKeys) { - const info = purchase.refundState.refundsPending[pk]; - const perm = info.perm; - const req = { - coin_pub: perm.coin_pub, - h_contract_terms: purchase.contractData.contractTermsHash, - merchant_pub: purchase.contractData.merchantPub, - merchant_sig: perm.merchant_sig, - refund_amount: perm.refund_amount, - refund_fee: perm.refund_fee, - rtransaction_id: perm.rtransaction_id, - }; - console.log("sending refund permission", perm); - // FIXME: not correct once we support multiple exchanges per payment - const exchangeUrl = purchase.payReq.coins[0].exchange_url; - const reqUrl = new URL("refund", exchangeUrl); - const resp = yield ws.http.postJson(reqUrl.href, req); - console.log("sent refund permission"); - switch (resp.status) { - case http.HttpResponseStatus.Ok: - newRefundsDone[pk] = info; - break; - case http.HttpResponseStatus.Gone: - // We're too late, refund is expired. - newRefundsFailed[pk] = info; - break; - default: - let body = null; - try { - body = yield resp.json(); - } - catch (_a) { } - const m = "refund request (at exchange) failed"; - throw new errors.OperationFailedError(m, { - message: m, - type: "network", - details: { - body, - }, - }); - } + const exchangeDetails = exchange.details; + if (!exchangeDetails) { + // FIXME: report inconsistency? + return; } - let allRefundsProcessed = false; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases, dbTypes.Stores.coins, dbTypes.Stores.refreshGroups, dbTypes.Stores.refundEvents], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!p) { + // FIXME: verify that our expectations about the amount match + yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.reserves, dbTypes.Stores.recoupGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const recoupGroup = yield tx.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!recoupGroup) { return; } - // Groups that failed/succeeded - let groups = {}; - // Avoid duplicates - const refreshCoinsMap = {}; - const modCoin = (perm) => __awaiter(this, void 0, void 0, function* () { - const c = yield tx.get(dbTypes.Stores.coins, perm.coin_pub); - if (!c) { - console.warn("coin not found, can't apply refund"); - return; - } - refreshCoinsMap[c.coinPub] = { coinPub: c.coinPub }; - logger.trace(`commiting refund ${perm.merchant_sig} to coin ${c.coinPub}`); - logger.trace(`coin amount before is ${Amounts.toString(c.currentAmount)}`); - logger.trace(`refund amount (via merchant) is ${perm.refund_amount}`); - logger.trace(`refund fee (via merchant) is ${perm.refund_fee}`); - const refundAmount = Amounts.parseOrThrow(perm.refund_amount); - const refundFee = Amounts.parseOrThrow(perm.refund_fee); - c.status = dbTypes.CoinStatus.Dormant; - c.currentAmount = Amounts.add(c.currentAmount, refundAmount).amount; - c.currentAmount = Amounts.sub(c.currentAmount, refundFee).amount; - logger.trace(`coin amount after is ${Amounts.toString(c.currentAmount)}`); - yield tx.put(dbTypes.Stores.coins, c); - }); - for (const pk of Object.keys(newRefundsFailed)) { - if (p.refundState.refundsDone[pk]) { - // We already processed this one. - break; - } - const r = newRefundsFailed[pk]; - groups[r.refundGroupId] = true; - delete p.refundState.refundsPending[pk]; - p.refundState.refundsFailed[pk] = r; + if (recoupGroup.recoupFinishedPerCoin[coinIdx]) { + return; } - for (const pk of Object.keys(newRefundsDone)) { - if (p.refundState.refundsDone[pk]) { - // We already processed this one. - break; - } - const r = newRefundsDone[pk]; - groups[r.refundGroupId] = true; - delete p.refundState.refundsPending[pk]; - p.refundState.refundsDone[pk] = r; - yield modCoin(r.perm); + const updatedCoin = yield tx.get(dbTypes.Stores.coins, coin.coinPub); + if (!updatedCoin) { + return; + } + const updatedReserve = yield tx.get(dbTypes.Stores.reserves, reserve.reservePub); + if (!updatedReserve) { + return; + } + updatedCoin.status = "dormant" /* Dormant */; + const currency = updatedCoin.currentAmount.currency; + updatedCoin.currentAmount = amounts.Amounts.getZero(currency); + updatedReserve.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; + yield tx.put(dbTypes.Stores.coins, updatedCoin); + yield tx.put(dbTypes.Stores.reserves, updatedReserve); + yield putGroupAsFinished(ws, tx, recoupGroup, coinIdx); + })); + ws.notify({ + type: "recoup-finished" /* RecoupFinished */, + }); + reserves.forceQueryReserve(ws, reserve.reservePub).catch((e) => { + console.log("re-querying reserve after recoup failed:", e); + }); + }); +} +function recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + ws.notify({ + type: "recoup-started" /* RecoupStarted */, + }); + const recoupRequest = yield ws.cryptoApi.createRecoupRequest(coin); + const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl); + console.log("making recoup request"); + const resp = yield ws.http.postJson(reqUrl.href, recoupRequest); + const recoupConfirmation = yield errors.scrutinizeTalerJsonResponse(resp, talerTypes.codecForRecoupConfirmation()); + if (recoupConfirmation.old_coin_pub != cs.oldCoinPub) { + throw Error(`Coin's oldCoinPub doesn't match reserve on recoup`); + } + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, coin.exchangeBaseUrl); + if (!exchange) { + // FIXME: report inconsistency? + return; + } + const exchangeDetails = exchange.details; + if (!exchangeDetails) { + // FIXME: report inconsistency? + return; + } + yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.reserves, dbTypes.Stores.recoupGroups, dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const recoupGroup = yield tx.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!recoupGroup) { + return; } - const now = time.getTimestampNow(); - for (const g of Object.keys(groups)) { - let groupDone = true; - for (const pk of Object.keys(p.refundState.refundsPending)) { - const r = p.refundState.refundsPending[pk]; - if (r.refundGroupId == g) { - groupDone = false; - } - } - if (groupDone) { - const refundEvent = { - proposalId, - refundGroupId: g, - timestamp: now, - }; - yield tx.put(dbTypes.Stores.refundEvents, refundEvent); - } + if (recoupGroup.recoupFinishedPerCoin[coinIdx]) { + return; } - if (Object.keys(p.refundState.refundsPending).length === 0) { - p.refundStatusRetryInfo = dbTypes.initRetryInfo(); - p.lastRefundStatusError = undefined; - allRefundsProcessed = true; + const oldCoin = yield tx.get(dbTypes.Stores.coins, cs.oldCoinPub); + const revokedCoin = yield tx.get(dbTypes.Stores.coins, coin.coinPub); + if (!revokedCoin) { + return; } - yield tx.put(dbTypes.Stores.purchases, p); - const coinsPubsToBeRefreshed = Object.values(refreshCoinsMap); - if (coinsPubsToBeRefreshed.length > 0) { - yield refresh.createRefreshGroup(tx, coinsPubsToBeRefreshed, "refund" /* Refund */); + if (!oldCoin) { + return; } + revokedCoin.status = "dormant" /* Dormant */; + oldCoin.currentAmount = amounts.Amounts.add(oldCoin.currentAmount, recoupGroup.oldAmountPerCoin[coinIdx]).amount; + console.log("recoup: setting old coin amount to", amounts.Amounts.stringify(oldCoin.currentAmount)); + recoupGroup.scheduleRefreshCoins.push(oldCoin.coinPub); + yield tx.put(dbTypes.Stores.coins, revokedCoin); + yield tx.put(dbTypes.Stores.coins, oldCoin); + yield putGroupAsFinished(ws, tx, recoupGroup, coinIdx); })); - if (allRefundsProcessed) { - ws.notify({ - type: "refund-finished" /* RefundFinished */, - }); - } - ws.notify({ - type: "refunds-submitted" /* RefundsSubmitted */, - proposalId, + }); +} +function resetRecoupGroupRetry(ws, recoupGroupId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.recoupGroups, recoupGroupId, (x) => { + if (x.retryInfo.active) { + x.retryInfo = dbTypes.initRetryInfo(); + } + return x; }); }); } +function processRecoupGroup(ws, recoupGroupId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.memoProcessRecoup.memo(recoupGroupId, () => tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementRecoupRetry(ws, recoupGroupId, e); + return yield errors.guardOperationException(() => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield processRecoupGroupImpl(ws, recoupGroupId, forceNow); }), onOpErr); + })); + }); +} +exports.processRecoupGroup = processRecoupGroup; +function processRecoupGroupImpl(ws, recoupGroupId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (forceNow) { + yield resetRecoupGroupRetry(ws, recoupGroupId); + } + console.log("in processRecoupGroupImpl"); + const recoupGroup = yield ws.db.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!recoupGroup) { + return; + } + console.log(recoupGroup); + if (recoupGroup.timestampFinished) { + console.log("recoup group finished"); + return; + } + const ps = recoupGroup.coinPubs.map((x, i) => processRecoup(ws, recoupGroupId, i)); + yield Promise.all(ps); + }); +} +function createRecoupGroup(ws, tx, coinPubs) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const recoupGroupId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); + const recoupGroup = { + recoupGroupId, + coinPubs: coinPubs, + lastError: undefined, + timestampFinished: undefined, + timestampStarted: time.getTimestampNow(), + retryInfo: dbTypes.initRetryInfo(), + recoupFinishedPerCoin: coinPubs.map(() => false), + // Will be populated later + oldAmountPerCoin: [], + scheduleRefreshCoins: [], + }; + for (let coinIdx = 0; coinIdx < coinPubs.length; coinIdx++) { + const coinPub = coinPubs[coinIdx]; + const coin = yield tx.get(dbTypes.Stores.coins, coinPub); + if (!coin) { + yield putGroupAsFinished(ws, tx, recoupGroup, coinIdx); + continue; + } + if (amounts.Amounts.isZero(coin.currentAmount)) { + yield putGroupAsFinished(ws, tx, recoupGroup, coinIdx); + continue; + } + recoupGroup.oldAmountPerCoin[coinIdx] = coin.currentAmount; + coin.currentAmount = amounts.Amounts.getZero(coin.currentAmount.currency); + yield tx.put(dbTypes.Stores.coins, coin); + } + yield tx.put(dbTypes.Stores.recoupGroups, recoupGroup); + return recoupGroupId; + }); +} +exports.createRecoupGroup = createRecoupGroup; +function processRecoup(ws, recoupGroupId, coinIdx) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const recoupGroup = yield ws.db.get(dbTypes.Stores.recoupGroups, recoupGroupId); + if (!recoupGroup) { + return; + } + if (recoupGroup.timestampFinished) { + return; + } + if (recoupGroup.recoupFinishedPerCoin[coinIdx]) { + return; + } + const coinPub = recoupGroup.coinPubs[coinIdx]; + const coin = yield ws.db.get(dbTypes.Stores.coins, coinPub); + if (!coin) { + throw Error(`Coin ${coinPub} not found, can't request payback`); + } + const cs = coin.coinSource; + switch (cs.type) { + case "tip" /* Tip */: + return recoupTipCoin(ws, recoupGroupId, coinIdx); + case "refresh" /* Refresh */: + return recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs); + case "withdraw" /* Withdraw */: + return recoupWithdrawCoin(ws, recoupGroupId, coinIdx, coin, cs); + default: + throw Error("unknown coin source type"); + } + }); +} }); -unwrapExports(refund); -var refund_1 = refund.getFullRefundFees; -var refund_2 = refund.acceptRefundResponse; -var refund_3 = refund.applyRefund; -var refund_4 = refund.processPurchaseQueryRefund; -var refund_5 = refund.processPurchaseApplyRefund; +unwrapExports(recoup); +var recoup_1 = recoup.processRecoupGroup; +var recoup_2 = recoup.createRecoupGroup; -var pay = createCommonjsModule(function (module, exports) { +var exchanges = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler - (C) 2019 Taler Systems S.A. + (C) 2019 GNUnet e.V. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -9461,1007 +8721,1490 @@ var pay = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Implementation of the payment operation, including downloading and - * claiming of proposals. - * - * @author Florian Dold - */ -/** - * Imports. - */ -const Amounts = __importStar(amounts); +const Amounts = tslib_1.__importStar(amounts); +function denominationRecordFromKeys(ws, exchangeBaseUrl, denomIn) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const denomPubHash = yield ws.cryptoApi.hashEncoded(denomIn.denom_pub); + const d = { + denomPub: denomIn.denom_pub, + denomPubHash, + exchangeBaseUrl, + feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit), + feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh), + feeRefund: Amounts.parseOrThrow(denomIn.fee_refund), + feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw), + isOffered: true, + isRevoked: false, + masterSig: denomIn.master_sig, + stampExpireDeposit: denomIn.stamp_expire_deposit, + stampExpireLegal: denomIn.stamp_expire_legal, + stampExpireWithdraw: denomIn.stamp_expire_withdraw, + stampStart: denomIn.stamp_start, + status: dbTypes.DenominationStatus.Unverified, + value: Amounts.parseOrThrow(denomIn.value), + }; + return d; + }); +} +function setExchangeError(ws, baseUrl, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + console.log(`last error for exchange ${baseUrl}:`, err); + const mut = (exchange) => { + exchange.lastError = err; + return exchange; + }; + yield ws.db.mutate(dbTypes.Stores.exchanges, baseUrl, mut); + }); +} /** - * Logger. - */ -const logger = new logging.Logger("pay.ts"); -/** - * Compute the total cost of a payment to the customer. + * Fetch the exchange's /keys and update our database accordingly. * - * This includes the amount taken by the merchant, fees (wire/deposit) contributed - * by the customer, refreshing fees, fees for withdraw-after-refresh and "trimmings" - * of coins that are too small to spend. + * Exceptions thrown in this method must be caught and reported + * in the pending operations. */ -function getTotalPaymentCost(ws, pcs) { - return __awaiter(this, void 0, void 0, function* () { - const costs = [ - pcs.paymentAmount, - pcs.customerDepositFees, - pcs.customerWireFees, - ]; - for (let i = 0; i < pcs.coinPubs.length; i++) { - const coin = yield ws.db.get(dbTypes.Stores.coins, pcs.coinPubs[i]); - if (!coin) { - throw Error("can't calculate payment cost, coin not found"); +function updateExchangeWithKeys(ws, baseUrl) { + var _a; + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const existingExchangeRecord = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); + if ((existingExchangeRecord === null || existingExchangeRecord === void 0 ? void 0 : existingExchangeRecord.updateStatus) != "fetch-keys" /* FetchKeys */) { + return; + } + const keysUrl = new URL("keys", baseUrl); + keysUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); + let keysResp; + try { + const r = yield ws.http.get(keysUrl.href); + if (r.status !== 200) { + throw Error(`unexpected status for keys: ${r.status}`); + } + keysResp = yield r.json(); + } + catch (e) { + const m = `Fetching keys failed: ${e.message}`; + const opErr = { + type: "network", + details: { + requestUrl: (_a = e.config) === null || _a === void 0 ? void 0 : _a.url, + }, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + let exchangeKeysJson; + try { + exchangeKeysJson = talerTypes.codecForExchangeKeysJson().decode(keysResp); + } + catch (e) { + const m = `Parsing /keys response failed: ${e.message}`; + const opErr = { + type: "protocol-violation", + details: {}, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + const lastUpdateTimestamp = exchangeKeysJson.list_issue_date; + if (!lastUpdateTimestamp) { + const m = `Parsing /keys response failed: invalid list_issue_date.`; + const opErr = { + type: "protocol-violation", + details: {}, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + if (exchangeKeysJson.denoms.length === 0) { + const m = "exchange doesn't offer any denominations"; + const opErr = { + type: "protocol-violation", + details: {}, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + const protocolVersion = exchangeKeysJson.version; + if (!protocolVersion) { + const m = "outdate exchange, no version in /keys response"; + const opErr = { + type: "protocol-violation", + details: {}, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + const versionRes = libtoolVersion.compare(versions.WALLET_EXCHANGE_PROTOCOL_VERSION, protocolVersion); + if ((versionRes === null || versionRes === void 0 ? void 0 : versionRes.compatible) != true) { + const m = "exchange protocol version not compatible with wallet"; + const opErr = { + type: "protocol-incompatible", + details: { + exchangeProtocolVersion: protocolVersion, + walletProtocolVersion: versions.WALLET_EXCHANGE_PROTOCOL_VERSION, + }, + message: m, + }; + yield setExchangeError(ws, baseUrl, opErr); + throw new errors.OperationFailedAndReportedError(opErr); + } + const currency = Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value) + .currency; + const newDenominations = yield Promise.all(exchangeKeysJson.denoms.map((d) => denominationRecordFromKeys(ws, baseUrl, d))); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges, dbTypes.Stores.denominations, dbTypes.Stores.recoupGroups, dbTypes.Stores.coins], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + var _b; + const r = yield tx.get(dbTypes.Stores.exchanges, baseUrl); + if (!r) { + console.warn(`exchange ${baseUrl} no longer present`); + return; + } + if (r.details) ; + // FIXME: validate signing keys and merge with old set + r.details = { + auditors: exchangeKeysJson.auditors, + currency: currency, + lastUpdateTime: lastUpdateTimestamp, + masterPublicKey: exchangeKeysJson.master_public_key, + protocolVersion: protocolVersion, + signingKeys: exchangeKeysJson.signkeys, + }; + r.updateStatus = "fetch-wire" /* FetchWire */; + r.lastError = undefined; + yield tx.put(dbTypes.Stores.exchanges, r); + for (const newDenom of newDenominations) { + const oldDenom = yield tx.get(dbTypes.Stores.denominations, [ + baseUrl, + newDenom.denomPub, + ]); + if (oldDenom) ; + else { + yield tx.put(dbTypes.Stores.denominations, newDenom); + } + } + // Handle recoup + const recoupDenomList = (_b = exchangeKeysJson.recoup) !== null && _b !== void 0 ? _b : []; + const newlyRevokedCoinPubs = []; + console.log("recoup list from exchange", recoupDenomList); + for (const recoupInfo of recoupDenomList) { + const oldDenom = yield tx.getIndexed(dbTypes.Stores.denominations.denomPubHashIndex, recoupInfo.h_denom_pub); + if (!oldDenom) { + // We never even knew about the revoked denomination, all good. + continue; + } + if (oldDenom.isRevoked) { + // We already marked the denomination as revoked, + // this implies we revoked all coins + console.log("denom already revoked"); + continue; + } + console.log("revoking denom", recoupInfo.h_denom_pub); + oldDenom.isRevoked = true; + yield tx.put(dbTypes.Stores.denominations, oldDenom); + const affectedCoins = yield tx + .iterIndexed(dbTypes.Stores.coins.denomPubHashIndex, recoupInfo.h_denom_pub) + .toArray(); + for (const ac of affectedCoins) { + newlyRevokedCoinPubs.push(ac.coinPub); + } + } + if (newlyRevokedCoinPubs.length != 0) { + console.log("recouping coins", newlyRevokedCoinPubs); + yield recoup.createRecoupGroup(ws, tx, newlyRevokedCoinPubs); } - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - coin.exchangeBaseUrl, - coin.denomPub, - ]); - if (!denom) { - throw Error("can't calculate payment cost, denomination for coin not found"); + })); + }); +} +function updateExchangeFinalize(ws, exchangeBaseUrl) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!exchange) { + return; + } + if (exchange.updateStatus != "finalize-update" /* FinalizeUpdate */) { + return; + } + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges, dbTypes.Stores.exchangeUpdatedEvents], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!r) { + return; } - const allDenoms = yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, coin.exchangeBaseUrl) - .toArray(); - const amountLeft = Amounts.sub(denom.value, pcs.coinContributions[i]) - .amount; - const refreshCost = refresh.getTotalRefreshCost(allDenoms, denom, amountLeft); - costs.push(refreshCost); + if (r.updateStatus != "finalize-update" /* FinalizeUpdate */) { + return; + } + r.updateStatus = "finished" /* Finished */; + yield tx.put(dbTypes.Stores.exchanges, r); + const updateEvent = { + exchangeBaseUrl: exchange.baseUrl, + timestamp: time.getTimestampNow(), + }; + yield tx.put(dbTypes.Stores.exchangeUpdatedEvents, updateEvent); + })); + }); +} +function updateExchangeWithTermsOfService(ws, exchangeBaseUrl) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!exchange) { + return; } - return Amounts.sum(costs).amount; + if (exchange.updateStatus != "fetch-terms" /* FetchTerms */) { + return; + } + const reqUrl = new URL("terms", exchangeBaseUrl); + reqUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); + const headers = { + Accept: "text/plain", + }; + const resp = yield ws.http.get(reqUrl.href, { headers }); + if (resp.status !== 200) { + throw Error(`/terms response has unexpected status code (${resp.status})`); + } + const tosText = yield resp.text(); + const tosEtag = resp.headers.get("etag") || undefined; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!r) { + return; + } + if (r.updateStatus != "fetch-terms" /* FetchTerms */) { + return; + } + r.termsOfServiceText = tosText; + r.termsOfServiceLastEtag = tosEtag; + r.updateStatus = "finalize-update" /* FinalizeUpdate */; + yield tx.put(dbTypes.Stores.exchanges, r); + })); }); } -exports.getTotalPaymentCost = getTotalPaymentCost; +function acceptExchangeTermsOfService(ws, exchangeBaseUrl, etag) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!r) { + return; + } + r.termsOfServiceAcceptedEtag = etag; + r.termsOfServiceAcceptedTimestamp = time.getTimestampNow(); + yield tx.put(dbTypes.Stores.exchanges, r); + })); + }); +} +exports.acceptExchangeTermsOfService = acceptExchangeTermsOfService; /** - * Given a list of available coins, select coins to spend under the merchant's - * constraints. + * Fetch wire information for an exchange and store it in the database. * - * This function is only exported for the sake of unit tests. + * @param exchangeBaseUrl Exchange base URL, assumed to be already normalized. */ -function selectPayCoins(acis, paymentAmount, depositFeeLimit) { - if (acis.length === 0) { - return undefined; - } - const coinPubs = []; - const coinContributions = []; - // Sort by available amount (descending), deposit fee (ascending) and - // denomPub (ascending) if deposit fee is the same - // (to guarantee deterministic results) - acis.sort((o1, o2) => -Amounts.cmp(o1.availableAmount, o2.availableAmount) || - Amounts.cmp(o1.feeDeposit, o2.feeDeposit) || - helpers.strcmp(o1.denomPub, o2.denomPub)); - const currency = paymentAmount.currency; - let totalFees = Amounts.getZero(currency); - let amountPayRemaining = paymentAmount; - let amountDepositFeeLimitRemaining = depositFeeLimit; - let customerWireFees = Amounts.getZero(currency); - let customerDepositFees = Amounts.getZero(currency); - for (const aci of acis) { - // Don't use this coin if depositing it is more expensive than - // the amount it would give the merchant. - if (Amounts.cmp(aci.feeDeposit, aci.availableAmount) >= 0) { - continue; +function updateExchangeWithWireInfo(ws, exchangeBaseUrl) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!exchange) { + return; } - if (amountPayRemaining.value === 0 && amountPayRemaining.fraction === 0) { - // We have spent enough! - break; + if (exchange.updateStatus != "fetch-wire" /* FetchWire */) { + return; } - // How much does the user spend on deposit fees for this coin? - const depositFeeSpend = Amounts.sub(aci.feeDeposit, amountDepositFeeLimitRemaining).amount; - if (Amounts.isZero(depositFeeSpend)) { - // Fees are still covered by the merchant. - amountDepositFeeLimitRemaining = Amounts.sub(amountDepositFeeLimitRemaining, aci.feeDeposit).amount; + const details = exchange.details; + if (!details) { + throw Error("invalid exchange state"); } - else { - amountDepositFeeLimitRemaining = Amounts.getZero(currency); + const reqUrl = new URL("wire", exchangeBaseUrl); + reqUrl.searchParams.set("cacheBreaker", versions.WALLET_CACHE_BREAKER_CLIENT_VERSION); + const resp = yield ws.http.get(reqUrl.href); + if (resp.status !== 200) { + throw Error(`/wire response has unexpected status code (${resp.status})`); } - let coinSpend; - const amountActualAvailable = Amounts.sub(aci.availableAmount, depositFeeSpend).amount; - if (Amounts.cmp(amountActualAvailable, amountPayRemaining) > 0) { - // Partial spending - coinSpend = Amounts.add(amountPayRemaining, depositFeeSpend).amount; - amountPayRemaining = Amounts.getZero(currency); + const wiJson = yield resp.json(); + if (!wiJson) { + throw Error("/wire response malformed"); + } + const wireInfo = talerTypes.codecForExchangeWireJson().decode(wiJson); + for (const a of wireInfo.accounts) { + console.log("validating exchange acct"); + const isValid = yield ws.cryptoApi.isValidWireAccount(a.payto_uri, a.master_sig, details.masterPublicKey); + if (!isValid) { + throw Error("exchange acct signature invalid"); + } + } + const feesForType = {}; + for (const wireMethod of Object.keys(wireInfo.fees)) { + const feeList = []; + for (const x of wireInfo.fees[wireMethod]) { + const startStamp = x.start_date; + const endStamp = x.end_date; + const fee = { + closingFee: Amounts.parseOrThrow(x.closing_fee), + endStamp, + sig: x.sig, + startStamp, + wireFee: Amounts.parseOrThrow(x.wire_fee), + }; + const isValid = yield ws.cryptoApi.isValidWireFee(wireMethod, fee, details.masterPublicKey); + if (!isValid) { + throw Error("exchange wire fee signature invalid"); + } + feeList.push(fee); + } + feesForType[wireMethod] = feeList; + } + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const r = yield tx.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!r) { + return; + } + if (r.updateStatus != "fetch-wire" /* FetchWire */) { + return; + } + r.wireInfo = { + accounts: wireInfo.accounts, + feesForType: feesForType, + }; + r.updateStatus = "fetch-terms" /* FetchTerms */; + r.lastError = undefined; + yield tx.put(dbTypes.Stores.exchanges, r); + })); + }); +} +function updateExchangeFromUrl(ws, baseUrl, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => setExchangeError(ws, baseUrl, e); + return yield errors.guardOperationException(() => updateExchangeFromUrlImpl(ws, baseUrl, forceNow), onOpErr); + }); +} +exports.updateExchangeFromUrl = updateExchangeFromUrl; +/** + * Update or add exchange DB entry by fetching the /keys and /wire information. + * Optionally link the reserve entry to the new or existing + * exchange entry in then DB. + */ +function updateExchangeFromUrlImpl(ws, baseUrl, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const now = time.getTimestampNow(); + baseUrl = helpers.canonicalizeBaseUrl(baseUrl); + const r = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); + if (!r) { + const newExchangeRecord = { + builtIn: false, + baseUrl: baseUrl, + details: undefined, + wireInfo: undefined, + updateStatus: "fetch-keys" /* FetchKeys */, + updateStarted: now, + updateReason: "initial" /* Initial */, + timestampAdded: time.getTimestampNow(), + termsOfServiceAcceptedEtag: undefined, + termsOfServiceAcceptedTimestamp: undefined, + termsOfServiceLastEtag: undefined, + termsOfServiceText: undefined, + updateDiff: undefined, + }; + yield ws.db.put(dbTypes.Stores.exchanges, newExchangeRecord); } else { - // Spend the full remaining amount - coinSpend = aci.availableAmount; - amountPayRemaining = Amounts.add(amountPayRemaining, depositFeeSpend) - .amount; - amountPayRemaining = Amounts.sub(amountPayRemaining, aci.availableAmount) - .amount; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.exchanges], (t) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const rec = yield t.get(dbTypes.Stores.exchanges, baseUrl); + if (!rec) { + return; + } + if (rec.updateStatus != "fetch-keys" /* FetchKeys */ && !forceNow) { + return; + } + if (rec.updateStatus != "fetch-keys" /* FetchKeys */ && forceNow) { + rec.updateReason = "forced" /* Forced */; + } + rec.updateStarted = now; + rec.updateStatus = "fetch-keys" /* FetchKeys */; + rec.lastError = undefined; + t.put(dbTypes.Stores.exchanges, rec); + })); } - coinPubs.push(aci.coinPub); - coinContributions.push(coinSpend); - totalFees = Amounts.add(totalFees, depositFeeSpend).amount; - } - if (Amounts.isZero(amountPayRemaining)) { - return { - paymentAmount, - coinContributions, - coinPubs, - customerDepositFees, - customerWireFees, - }; - } - return undefined; + yield updateExchangeWithKeys(ws, baseUrl); + yield updateExchangeWithWireInfo(ws, baseUrl); + yield updateExchangeWithTermsOfService(ws, baseUrl); + yield updateExchangeFinalize(ws, baseUrl); + const updatedExchange = yield ws.db.get(dbTypes.Stores.exchanges, baseUrl); + if (!updatedExchange) { + // This should practically never happen + throw Error("exchange not found"); + } + return updatedExchange; + }); } -exports.selectPayCoins = selectPayCoins; /** - * Select coins from the wallet's database that can be used - * to pay for the given contract. - * - * If payment is impossible, undefined is returned. + * Check if and how an exchange is trusted and/or audited. */ -function getCoinsForPayment(ws, contractData) { - return __awaiter(this, void 0, void 0, function* () { - let remainingAmount = contractData.amount; - const exchanges = yield ws.db.iter(dbTypes.Stores.exchanges).toArray(); - for (const exchange of exchanges) { - let isOkay = false; - const exchangeDetails = exchange.details; - if (!exchangeDetails) { - continue; - } - const exchangeFees = exchange.wireInfo; - if (!exchangeFees) { - continue; - } - // is the exchange explicitly allowed? - for (const allowedExchange of contractData.allowedExchanges) { - if (allowedExchange.exchangePub === exchangeDetails.masterPublicKey) { - isOkay = true; +function getExchangeTrust(ws, exchangeInfo) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + let isTrusted = false; + let isAudited = false; + const exchangeDetails = exchangeInfo.details; + if (!exchangeDetails) { + throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); + } + const currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, exchangeDetails.currency); + if (currencyRecord) { + for (const trustedExchange of currencyRecord.exchanges) { + if (trustedExchange.exchangePub === exchangeDetails.masterPublicKey) { + isTrusted = true; break; } } - // is the exchange allowed because of one of its auditors? - if (!isOkay) { - for (const allowedAuditor of contractData.allowedAuditors) { - for (const auditor of exchangeDetails.auditors) { - if (auditor.auditor_pub === allowedAuditor.auditorPub) { - isOkay = true; - break; - } - } - if (isOkay) { + for (const trustedAuditor of currencyRecord.auditors) { + for (const exchangeAuditor of exchangeDetails.auditors) { + if (trustedAuditor.auditorPub === exchangeAuditor.auditor_pub) { + isAudited = true; break; } } } - if (!isOkay) { + } + return { isTrusted, isAudited }; + }); +} +exports.getExchangeTrust = getExchangeTrust; +function getExchangePaytoUri(ws, exchangeBaseUrl, supportedTargetTypes) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + // We do the update here, since the exchange might not even exist + // yet in our database. + const exchangeRecord = yield updateExchangeFromUrl(ws, exchangeBaseUrl); + if (!exchangeRecord) { + throw Error(`Exchange '${exchangeBaseUrl}' not found.`); + } + const exchangeWireInfo = exchangeRecord.wireInfo; + if (!exchangeWireInfo) { + throw Error(`Exchange wire info for '${exchangeBaseUrl}' not found.`); + } + for (const account of exchangeWireInfo.accounts) { + const res = payto.parsePaytoUri(account.payto_uri); + if (!res) { continue; } - const coins = yield ws.db - .iterIndex(dbTypes.Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl) - .toArray(); - const denoms = yield ws.db - .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) - .toArray(); - if (!coins || coins.length === 0) { - continue; + if (supportedTargetTypes.includes(res.targetType)) { + return account.payto_uri; } - // Denomination of the first coin, we assume that all other - // coins have the same currency - const firstDenom = yield ws.db.get(dbTypes.Stores.denominations, [ - exchange.baseUrl, - coins[0].denomPub, - ]); - if (!firstDenom) { - throw Error("db inconsistent"); + } + throw Error("no matching exchange account found"); + }); +} +exports.getExchangePaytoUri = getExchangePaytoUri; + +}); + +unwrapExports(exchanges); +var exchanges_1 = exchanges.acceptExchangeTermsOfService; +var exchanges_2 = exchanges.updateExchangeFromUrl; +var exchanges_3 = exchanges.getExchangeTrust; +var exchanges_4 = exchanges.getExchangePaytoUri; + +var withdraw = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019-2029 Taler Systems SA + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + + +const Amounts = tslib_1.__importStar(amounts); + + + + + +const LibtoolVersion = tslib_1.__importStar(libtoolVersion); + + +const logger = new logging.Logger("withdraw.ts"); +function isWithdrawableDenom(d) { + const now = time.getTimestampNow(); + const started = time.timestampCmp(now, d.stampStart) >= 0; + const lastPossibleWithdraw = time.timestampSubtractDuraction(d.stampExpireWithdraw, { d_ms: 50 * 1000 }); + const remaining = time.getDurationRemaining(lastPossibleWithdraw, now); + const stillOkay = remaining.d_ms !== 0; + return started && stillOkay && !d.isRevoked; +} +/** + * Get a list of denominations (with repetitions possible) + * whose total value is as close as possible to the available + * amount, but never larger. + */ +function getWithdrawDenomList(amountAvailable, denoms) { + let remaining = Amounts.copy(amountAvailable); + const ds = []; + denoms = denoms.filter(isWithdrawableDenom); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); + // This is an arbitrary number of coins + // we can withdraw in one go. It's not clear if this limit + // is useful ... + for (let i = 0; i < 1000; i++) { + let found = false; + for (const d of denoms) { + const cost = Amounts.add(d.value, d.feeWithdraw).amount; + if (Amounts.cmp(remaining, cost) < 0) { + continue; } - const currency = firstDenom.value.currency; - const acis = []; - for (const coin of coins) { - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - exchange.baseUrl, - coin.denomPub, - ]); - if (!denom) { - throw Error("db inconsistent"); - } - if (denom.value.currency !== currency) { - console.warn(`same pubkey for different currencies at exchange ${exchange.baseUrl}`); - continue; - } - if (coin.suspended) { - continue; - } - if (coin.status !== dbTypes.CoinStatus.Fresh) { - continue; - } - acis.push({ - availableAmount: coin.currentAmount, - coinPub: coin.coinPub, - denomPub: coin.denomPub, - feeDeposit: denom.feeDeposit, - }); + found = true; + remaining = Amounts.sub(remaining, cost).amount; + ds.push(d); + break; + } + if (!found) { + break; + } + } + return ds; +} +exports.getWithdrawDenomList = getWithdrawDenomList; +/** + * Get information about a withdrawal from + * a taler://withdraw URI by asking the bank. + */ +function getBankWithdrawalInfo(ws, talerWithdrawUri) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const uriResult = taleruri.parseWithdrawUri(talerWithdrawUri); + if (!uriResult) { + throw Error(`can't parse URL ${talerWithdrawUri}`); + } + const resp = yield ws.http.get(uriResult.statusUrl); + if (resp.status !== 200) { + throw Error(`unexpected status (${resp.status}) from bank for ${uriResult.statusUrl}`); + } + const respJson = yield resp.json(); + console.log("resp:", respJson); + const status = talerTypes.codecForWithdrawOperationStatusResponse().decode(respJson); + return { + amount: Amounts.parseOrThrow(status.amount), + confirmTransferUrl: status.confirm_transfer_url, + extractedStatusUrl: uriResult.statusUrl, + selectionDone: status.selection_done, + senderWire: status.sender_wire, + suggestedExchange: status.suggested_exchange, + transferDone: status.transfer_done, + wireTypes: status.wire_types, + }; + }); +} +exports.getBankWithdrawalInfo = getBankWithdrawalInfo; +function getPossibleDenoms(ws, exchangeBaseUrl) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + return yield ws.db + .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl) + .filter((d) => { + return ((d.status === dbTypes.DenominationStatus.Unverified || + d.status === dbTypes.DenominationStatus.VerifiedGood) && + !d.isRevoked); + }); + }); +} +/** + * Given a planchet, withdraw a coin from the exchange. + */ +function processPlanchet(ws, withdrawalGroupId, coinIdx) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const withdrawalGroup = yield ws.db.get(dbTypes.Stores.withdrawalGroups, withdrawalGroupId); + if (!withdrawalGroup) { + return; + } + if (withdrawalGroup.withdrawn[coinIdx]) { + return; + } + const planchet = withdrawalGroup.planchets[coinIdx]; + if (!planchet) { + console.log("processPlanchet: planchet not found"); + return; + } + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, withdrawalGroup.exchangeBaseUrl); + if (!exchange) { + console.error("db inconsistent: exchange for planchet not found"); + return; + } + const denom = yield ws.db.get(dbTypes.Stores.denominations, [ + withdrawalGroup.exchangeBaseUrl, + planchet.denomPub, + ]); + if (!denom) { + console.error("db inconsistent: denom for planchet not found"); + return; + } + const wd = {}; + wd.denom_pub_hash = planchet.denomPubHash; + wd.reserve_pub = planchet.reservePub; + wd.reserve_sig = planchet.withdrawSig; + wd.coin_ev = planchet.coinEv; + const reqUrl = new URL(`reserves/${planchet.reservePub}/withdraw`, exchange.baseUrl).href; + const resp = yield ws.http.postJson(reqUrl, wd); + const r = yield errors.scrutinizeTalerJsonResponse(resp, talerTypes.codecForWithdrawResponse()); + const denomSig = yield ws.cryptoApi.rsaUnblind(r.ev_sig, planchet.blindingKey, planchet.denomPub); + const isValid = yield ws.cryptoApi.rsaVerify(planchet.coinPub, denomSig, planchet.denomPub); + if (!isValid) { + throw Error("invalid RSA signature by the exchange"); + } + const coin = { + blindingKey: planchet.blindingKey, + coinPriv: planchet.coinPriv, + coinPub: planchet.coinPub, + currentAmount: planchet.coinValue, + denomPub: planchet.denomPub, + denomPubHash: planchet.denomPubHash, + denomSig, + exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl, + status: "fresh" /* Fresh */, + coinSource: { + type: "withdraw" /* Withdraw */, + coinIndex: coinIdx, + reservePub: planchet.reservePub, + withdrawalGroupId: withdrawalGroupId, + }, + suspended: false, + }; + let withdrawalGroupFinished = false; + const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.withdrawalGroups, dbTypes.Stores.reserves], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const ws = yield tx.get(dbTypes.Stores.withdrawalGroups, withdrawalGroupId); + if (!ws) { + return false; } - let totalFees = Amounts.getZero(currency); - let wireFee; - for (const fee of exchangeFees.feesForType[contractData.wireMethod] || []) { - if (fee.startStamp <= contractData.timestamp && - fee.endStamp >= contractData.timestamp) { - wireFee = fee.wireFee; - break; - } + if (ws.withdrawn[coinIdx]) { + // Already withdrawn + return false; } - if (wireFee) { - const amortizedWireFee = Amounts.divide(wireFee, contractData.wireFeeAmortization); - if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) { - totalFees = Amounts.add(amortizedWireFee, totalFees).amount; - remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount; + ws.withdrawn[coinIdx] = true; + delete ws.lastErrorPerCoin[coinIdx]; + let numDone = 0; + for (let i = 0; i < ws.withdrawn.length; i++) { + if (ws.withdrawn[i]) { + numDone++; } } - // Try if paying using this exchange works - const res = selectPayCoins(acis, remainingAmount, contractData.maxDepositFee); - if (res) { - return res; + if (numDone === ws.denoms.length) { + ws.timestampFinish = time.getTimestampNow(); + ws.lastError = undefined; + ws.retryInfo = dbTypes.initRetryInfo(false); + withdrawalGroupFinished = true; } + yield tx.put(dbTypes.Stores.withdrawalGroups, ws); + yield tx.add(dbTypes.Stores.coins, coin); + return true; + })); + if (success) { + ws.notify({ + type: "coin-withdrawn" /* CoinWithdrawn */, + }); + } + if (withdrawalGroupFinished) { + ws.notify({ + type: "withdraw-group-finished" /* WithdrawGroupFinished */, + withdrawalSource: withdrawalGroup.source, + }); } - return undefined; }); } /** - * Record all information that is necessary to - * pay for a proposal in the wallet's database. + * Get a list of denominations to withdraw from the given exchange for the + * given amount, making sure that all denominations' signatures are verified. + * + * Writes to the DB in order to record the result from verifying + * denominations. */ -function recordConfirmPay(ws, proposal, coinSelection, coinDepositPermissions, sessionIdOverride) { - return __awaiter(this, void 0, void 0, function* () { - const d = proposal.download; - if (!d) { - throw Error("proposal is in invalid state"); +function getVerifiedWithdrawDenomList(ws, exchangeBaseUrl, amount) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const exchange = yield ws.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); + if (!exchange) { + console.log("exchange not found"); + throw Error(`exchange ${exchangeBaseUrl} not found`); } - let sessionId; - if (sessionIdOverride) { - sessionId = sessionIdOverride; + const exchangeDetails = exchange.details; + if (!exchangeDetails) { + console.log("exchange details not available"); + throw Error(`exchange ${exchangeBaseUrl} details not available`); } - else { - sessionId = proposal.downloadSessionId; + console.log("getting possible denoms"); + const possibleDenoms = yield getPossibleDenoms(ws, exchange.baseUrl); + console.log("got possible denoms"); + let allValid = false; + let selectedDenoms; + do { + allValid = true; + selectedDenoms = getWithdrawDenomList(amount, possibleDenoms); + console.log("got withdraw denom list"); + for (const denom of selectedDenoms || []) { + if (denom.status === dbTypes.DenominationStatus.Unverified) { + console.log("checking validity", denom, exchangeDetails.masterPublicKey); + const valid = yield ws.cryptoApi.isValidDenom(denom, exchangeDetails.masterPublicKey); + console.log("done checking validity"); + if (!valid) { + denom.status = dbTypes.DenominationStatus.VerifiedBad; + allValid = false; + } + else { + denom.status = dbTypes.DenominationStatus.VerifiedGood; + } + yield ws.db.put(dbTypes.Stores.denominations, denom); + } + } + } while (selectedDenoms.length > 0 && !allValid); + console.log("returning denoms"); + return selectedDenoms; + }); +} +exports.getVerifiedWithdrawDenomList = getVerifiedWithdrawDenomList; +function incrementWithdrawalRetry(ws, withdrawalGroupId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.withdrawalGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const wsr = yield tx.get(dbTypes.Stores.withdrawalGroups, withdrawalGroupId); + if (!wsr) { + return; + } + if (!wsr.retryInfo) { + return; + } + wsr.retryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(wsr.retryInfo); + wsr.lastError = err; + yield tx.put(dbTypes.Stores.withdrawalGroups, wsr); + })); + ws.notify({ type: "withdraw-error" /* WithdrawOperationError */ }); + }); +} +function processWithdrawGroup(ws, withdrawalGroupId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementWithdrawalRetry(ws, withdrawalGroupId, e); + yield errors.guardOperationException(() => processWithdrawGroupImpl(ws, withdrawalGroupId, forceNow), onOpErr); + }); +} +exports.processWithdrawGroup = processWithdrawGroup; +function resetWithdrawalGroupRetry(ws, withdrawalGroupId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.withdrawalGroups, withdrawalGroupId, (x) => { + if (x.retryInfo.active) { + x.retryInfo = dbTypes.initRetryInfo(); + } + return x; + }); + }); +} +function processWithdrawGroupImpl(ws, withdrawalGroupId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + logger.trace("processing withdraw group", withdrawalGroupId); + if (forceNow) { + yield resetWithdrawalGroupRetry(ws, withdrawalGroupId); } - logger.trace(`recording payment with session ID ${sessionId}`); - const payReq = { - coins: coinDepositPermissions, - merchant_pub: d.contractData.merchantPub, - mode: "pay", - order_id: d.contractData.orderId, + const withdrawalGroup = yield ws.db.get(dbTypes.Stores.withdrawalGroups, withdrawalGroupId); + if (!withdrawalGroup) { + logger.trace("withdraw session doesn't exist"); + return; + } + const ps = withdrawalGroup.denoms.map((d, i) => processPlanchet(ws, withdrawalGroupId, i)); + yield Promise.all(ps); + return; + }); +} +function getExchangeWithdrawalInfo(ws, baseUrl, amount) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const exchangeInfo = yield exchanges.updateExchangeFromUrl(ws, baseUrl); + const exchangeDetails = exchangeInfo.details; + if (!exchangeDetails) { + throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); + } + const exchangeWireInfo = exchangeInfo.wireInfo; + if (!exchangeWireInfo) { + throw Error(`exchange ${exchangeInfo.baseUrl} wire details not available`); + } + const selectedDenoms = yield getVerifiedWithdrawDenomList(ws, baseUrl, amount); + let acc = Amounts.getZero(amount.currency); + for (const d of selectedDenoms) { + acc = Amounts.add(acc, d.feeWithdraw).amount; + } + const actualCoinCost = selectedDenoms + .map((d) => Amounts.add(d.value, d.feeWithdraw).amount) + .reduce((a, b) => Amounts.add(a, b).amount); + const exchangeWireAccounts = []; + for (const account of exchangeWireInfo.accounts) { + exchangeWireAccounts.push(account.payto_uri); + } + const { isTrusted, isAudited } = yield exchanges.getExchangeTrust(ws, exchangeInfo); + let earliestDepositExpiration = selectedDenoms[0].stampExpireDeposit; + for (let i = 1; i < selectedDenoms.length; i++) { + const expireDeposit = selectedDenoms[i].stampExpireDeposit; + if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) { + earliestDepositExpiration = expireDeposit; + } + } + const possibleDenoms = yield ws.db + .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, baseUrl) + .filter((d) => d.isOffered); + const trustedAuditorPubs = []; + const currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, amount.currency); + if (currencyRecord) { + trustedAuditorPubs.push(...currencyRecord.auditors.map((a) => a.auditorPub)); + } + let versionMatch; + if (exchangeDetails.protocolVersion) { + versionMatch = LibtoolVersion.compare(versions.WALLET_EXCHANGE_PROTOCOL_VERSION, exchangeDetails.protocolVersion); + if (versionMatch && + !versionMatch.compatible && + versionMatch.currentCmp === -1) { + console.warn(`wallet's support for exchange protocol version ${versions.WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + + `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`); + } + } + let tosAccepted = false; + if (exchangeInfo.termsOfServiceAcceptedTimestamp) { + if (exchangeInfo.termsOfServiceAcceptedEtag == + exchangeInfo.termsOfServiceLastEtag) { + tosAccepted = true; + } + } + const ret = { + earliestDepositExpiration, + exchangeInfo, + exchangeWireAccounts, + exchangeVersion: exchangeDetails.protocolVersion || "unknown", + isAudited, + isTrusted, + numOfferedDenoms: possibleDenoms.length, + overhead: Amounts.sub(amount, actualCoinCost).amount, + selectedDenoms, + trustedAuditorPubs, + versionMatch, + walletVersion: versions.WALLET_EXCHANGE_PROTOCOL_VERSION, + wireFees: exchangeWireInfo, + withdrawFee: acc, + termsOfServiceAccepted: tosAccepted, }; - const t = { - abortDone: false, - abortRequested: false, - contractTermsRaw: d.contractTermsRaw, - contractData: d.contractData, - lastSessionId: sessionId, - payReq, - timestampAccept: time.getTimestampNow(), - timestampLastRefundStatus: undefined, - proposalId: proposal.proposalId, - lastPayError: undefined, - lastRefundStatusError: undefined, - payRetryInfo: dbTypes.initRetryInfo(), - refundStatusRetryInfo: dbTypes.initRetryInfo(), - refundStatusRequested: false, - lastRefundApplyError: undefined, - refundApplyRetryInfo: dbTypes.initRetryInfo(), - timestampFirstSuccessfulPay: undefined, - autoRefundDeadline: undefined, - paymentSubmitPending: true, - refundState: { - refundGroups: [], - refundsDone: {}, - refundsFailed: {}, - refundsPending: {}, - }, + return ret; + }); +} +exports.getExchangeWithdrawalInfo = getExchangeWithdrawalInfo; +function getWithdrawDetailsForUri(ws, talerWithdrawUri, maybeSelectedExchange) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const info = yield getBankWithdrawalInfo(ws, talerWithdrawUri); + let rci = undefined; + if (maybeSelectedExchange) { + rci = yield getExchangeWithdrawalInfo(ws, maybeSelectedExchange, info.amount); + } + return { + bankWithdrawDetails: info, + exchangeWithdrawDetails: rci, }; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.purchases, dbTypes.Stores.proposals, dbTypes.Stores.refreshGroups], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.proposals, proposal.proposalId); - if (p) { - p.proposalStatus = "accepted" /* ACCEPTED */; - p.lastError = undefined; - p.retryInfo = dbTypes.initRetryInfo(false); - yield tx.put(dbTypes.Stores.proposals, p); - } - yield tx.put(dbTypes.Stores.purchases, t); - for (let i = 0; i < coinSelection.coinPubs.length; i++) { - const coin = yield tx.get(dbTypes.Stores.coins, coinSelection.coinPubs[i]); - if (!coin) { - throw Error("coin allocated for payment doesn't exist anymore"); - } - coin.status = dbTypes.CoinStatus.Dormant; - const remaining = Amounts.sub(coin.currentAmount, coinSelection.coinContributions[i]); - if (remaining.saturated) { - throw Error("not enough remaining balance on coin for payment"); - } - coin.currentAmount = remaining.amount; - yield tx.put(dbTypes.Stores.coins, coin); - } - const refreshCoinPubs = coinSelection.coinPubs.map(x => ({ coinPub: x })); - yield refresh.createRefreshGroup(tx, refreshCoinPubs, "pay" /* Pay */); - })); - ws.notify({ - type: "proposal-accepted" /* ProposalAccepted */, - proposalId: proposal.proposalId, - }); - return t; }); } -function getNextUrl(contractData) { - const f = contractData.fulfillmentUrl; - if (f.startsWith("http://") || f.startsWith("https://")) { - const fu = new URL(contractData.fulfillmentUrl); - fu.searchParams.set("order_id", contractData.orderId); - return fu.href; - } - else { - return f; +exports.getWithdrawDetailsForUri = getWithdrawDetailsForUri; + +}); + +unwrapExports(withdraw); +var withdraw_1 = withdraw.getWithdrawDenomList; +var withdraw_2 = withdraw.getBankWithdrawalInfo; +var withdraw_3 = withdraw.getVerifiedWithdrawDenomList; +var withdraw_4 = withdraw.processWithdrawGroup; +var withdraw_5 = withdraw.getExchangeWithdrawalInfo; +var withdraw_6 = withdraw.getWithdrawDetailsForUri; + +var http = createCommonjsModule(function (module, exports) { +/* + This file is part of TALER + (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + +var HttpResponseStatus; +(function (HttpResponseStatus) { + HttpResponseStatus[HttpResponseStatus["Ok"] = 200] = "Ok"; + HttpResponseStatus[HttpResponseStatus["Gone"] = 210] = "Gone"; +})(HttpResponseStatus = exports.HttpResponseStatus || (exports.HttpResponseStatus = {})); +/** + * Headers, roughly modeled after the fetch API's headers object. + */ +class Headers { + constructor() { + this.headerMap = new Map(); } -} -function abortFailedPayment(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); - if (!purchase) { - throw Error("Purchase not found, unable to abort with refund"); - } - if (purchase.timestampFirstSuccessfulPay) { - throw Error("Purchase already finished, not aborting"); - } - if (purchase.abortDone) { - console.warn("abort requested on already aborted purchase"); - return; - } - purchase.abortRequested = true; - // From now on, we can't retry payment anymore, - // so mark this in the DB in case the /pay abort - // does not complete on the first try. - yield ws.db.put(dbTypes.Stores.purchases, purchase); - let resp; - const abortReq = Object.assign(Object.assign({}, purchase.payReq), { mode: "abort-refund" }); - const payUrl = new URL("pay", purchase.contractData.merchantBaseUrl).href; - try { - resp = yield ws.http.postJson(payUrl, abortReq); + get(name) { + const r = this.headerMap.get(name.toLowerCase()); + if (r) { + return r; } - catch (e) { - // Gives the user the option to retry / abort and refresh - console.log("aborting payment failed", e); - throw e; + return null; + } + set(name, value) { + const normalizedName = name.toLowerCase(); + const existing = this.headerMap.get(normalizedName); + if (existing !== undefined) { + this.headerMap.set(normalizedName, existing + "," + value); } - if (resp.status !== 200) { - throw Error(`unexpected status for /pay (${resp.status})`); + else { + this.headerMap.set(normalizedName, value); } - const refundResponse = talerTypes.codecForMerchantRefundResponse().decode(yield resp.json()); - yield refund.acceptRefundResponse(ws, purchase.proposalId, refundResponse, "abort-refund" /* AbortRefund */); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!p) { - return; - } - p.abortDone = true; - yield tx.put(dbTypes.Stores.purchases, p); - })); - }); + } } -exports.abortFailedPayment = abortFailedPayment; -function incrementProposalRetry(ws, proposalId, err) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => __awaiter(this, void 0, void 0, function* () { - const pr = yield tx.get(dbTypes.Stores.proposals, proposalId); - if (!pr) { - return; +exports.Headers = Headers; +/** + * An implementation of the [[HttpRequestLibrary]] using the + * browser's XMLHttpRequest. + */ +class BrowserHttpLib { + req(method, url, requestBody, options) { + return new Promise((resolve, reject) => { + const myRequest = new XMLHttpRequest(); + myRequest.open(method, url); + if (options === null || options === void 0 ? void 0 : options.headers) { + for (const headerName in options.headers) { + myRequest.setRequestHeader(headerName, options.headers[headerName]); + } } - if (!pr.retryInfo) { - return; + if (requestBody) { + myRequest.send(requestBody); } - pr.retryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(pr.retryInfo); - pr.lastError = err; - yield tx.put(dbTypes.Stores.proposals, pr); - })); - ws.notify({ type: "proposal-error" /* ProposalOperationError */ }); - }); + else { + myRequest.send(); + } + myRequest.onerror = (e) => { + console.error("http request error"); + reject(Error("could not make XMLHttpRequest")); + }; + myRequest.addEventListener("readystatechange", (e) => { + if (myRequest.readyState === XMLHttpRequest.DONE) { + if (myRequest.status === 0) { + reject(Error("HTTP Request failed (status code 0, maybe URI scheme is wrong?)")); + return; + } + const makeJson = () => tslib_1.__awaiter(this, void 0, void 0, function* () { + let responseJson; + try { + responseJson = JSON.parse(myRequest.responseText); + } + catch (e) { + throw Error("Invalid JSON from HTTP response"); + } + if (responseJson === null || typeof responseJson !== "object") { + throw Error("Invalid JSON from HTTP response"); + } + return responseJson; + }); + const headers = myRequest.getAllResponseHeaders(); + const arr = headers.trim().split(/[\r\n]+/); + // Create a map of header names to values + const headerMap = new Headers(); + arr.forEach(function (line) { + const parts = line.split(": "); + const headerName = parts.shift(); + if (!headerName) { + console.error("invalid header"); + return; + } + const value = parts.join(": "); + headerMap.set(headerName, value); + }); + const resp = { + status: myRequest.status, + headers: headerMap, + json: makeJson, + text: () => tslib_1.__awaiter(this, void 0, void 0, function* () { return myRequest.responseText; }), + }; + resolve(resp); + } + }); + }); + } + get(url, opt) { + return this.req("get", url, undefined, opt); + } + postJson(url, body, opt) { + return this.req("post", url, JSON.stringify(body), opt); + } + stop() { + // Nothing to do + } } -function incrementPurchasePayRetry(ws, proposalId, err) { - return __awaiter(this, void 0, void 0, function* () { - console.log("incrementing purchase pay retry with error", err); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { +exports.BrowserHttpLib = BrowserHttpLib; + +}); + +unwrapExports(http); +var http_1 = http.HttpResponseStatus; +var http_2 = http.Headers; +var http_3 = http.BrowserHttpLib; + +var refund = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019-2019 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + + + + + + + + + + + + +const logger = new logging.Logger("refund.ts"); +function incrementPurchaseQueryRefundRetry(ws, proposalId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + console.log("incrementing purchase refund query retry with error", err); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { const pr = yield tx.get(dbTypes.Stores.purchases, proposalId); if (!pr) { return; } - if (!pr.payRetryInfo) { + if (!pr.refundStatusRetryInfo) { return; } - pr.payRetryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(pr.payRetryInfo); - pr.lastPayError = err; + pr.refundStatusRetryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(pr.refundStatusRetryInfo); + pr.lastRefundStatusError = err; yield tx.put(dbTypes.Stores.purchases, pr); })); - ws.notify({ type: "pay-error" /* PayOperationError */ }); - }); -} -function processDownloadProposal(ws, proposalId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (err) => incrementProposalRetry(ws, proposalId, err); - yield errors.guardOperationException(() => processDownloadProposalImpl(ws, proposalId, forceNow), onOpErr); + ws.notify({ type: "refund-status-error" /* RefundStatusOperationError */ }); }); } -exports.processDownloadProposal = processDownloadProposal; -function resetDownloadProposalRetry(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.proposals, proposalId, x => { - if (x.retryInfo.active) { - x.retryInfo = dbTypes.initRetryInfo(); +function incrementPurchaseApplyRefundRetry(ws, proposalId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + console.log("incrementing purchase refund apply retry with error", err); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const pr = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!pr) { + return; } - return x; - }); + if (!pr.refundApplyRetryInfo) { + return; + } + pr.refundApplyRetryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(pr.refundApplyRetryInfo); + pr.lastRefundApplyError = err; + yield tx.put(dbTypes.Stores.purchases, pr); + })); + ws.notify({ type: "refund-apply-error" /* RefundApplyOperationError */ }); }); } -function processDownloadProposalImpl(ws, proposalId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { - if (forceNow) { - yield resetDownloadProposalRetry(ws, proposalId); - } - const proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); - if (!proposal) { - return; - } - if (proposal.proposalStatus != "downloading" /* DOWNLOADING */) { - return; - } - const parsedUrl = new URL(taleruri.getOrderDownloadUrl(proposal.merchantBaseUrl, proposal.orderId)); - parsedUrl.searchParams.set("nonce", proposal.noncePub); - const urlWithNonce = parsedUrl.href; - console.log("downloading contract from '" + urlWithNonce + "'"); - let resp; - try { - resp = yield ws.http.get(urlWithNonce); +function getFullRefundFees(ws, refundPermissions) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (refundPermissions.length === 0) { + throw Error("no refunds given"); } - catch (e) { - console.log("contract download failed", e); - throw e; + const coin0 = yield ws.db.get(dbTypes.Stores.coins, refundPermissions[0].coin_pub); + if (!coin0) { + throw Error("coin not found"); } - if (resp.status !== 200) { - throw Error(`contract download failed with status ${resp.status}`); + let feeAcc = amounts.Amounts.getZero(amounts.Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency); + const denoms = yield ws.db + .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, coin0.exchangeBaseUrl) + .toArray(); + for (const rp of refundPermissions) { + const coin = yield ws.db.get(dbTypes.Stores.coins, rp.coin_pub); + if (!coin) { + throw Error("coin not found"); + } + const denom = yield ws.db.get(dbTypes.Stores.denominations, [ + coin0.exchangeBaseUrl, + coin.denomPub, + ]); + if (!denom) { + throw Error(`denom not found (${coin.denomPub})`); + } + // FIXME: this assumes that the refund already happened. + // When it hasn't, the refresh cost is inaccurate. To fix this, + // we need introduce a flag to tell if a coin was refunded or + // refreshed normally (and what about incremental refunds?) + const refundAmount = amounts.Amounts.parseOrThrow(rp.refund_amount); + const refundFee = amounts.Amounts.parseOrThrow(rp.refund_fee); + const refreshCost = refresh.getTotalRefreshCost(denoms, denom, amounts.Amounts.sub(refundAmount, refundFee).amount); + feeAcc = amounts.Amounts.add(feeAcc, refreshCost, refundFee).amount; } - const proposalResp = talerTypes.codecForProposal().decode(yield resp.json()); - const contractTermsHash = yield ws.cryptoApi.hashString(helpers.canonicalJson(proposalResp.contract_terms)); - const parsedContractTerms = talerTypes.codecForContractTerms().decode(proposalResp.contract_terms); - const fulfillmentUrl = parsedContractTerms.fulfillment_url; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals, dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.proposals, proposalId); + return feeAcc; + }); +} +exports.getFullRefundFees = getFullRefundFees; +function acceptRefundResponse(ws, proposalId, refundResponse, reason) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const refundPermissions = refundResponse.refund_permissions; + let numNewRefunds = 0; + const refundGroupId = talerCrypto.encodeCrock(naclFast.randomBytes(32)); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.purchases, proposalId); if (!p) { + console.error("purchase not found, not adding refunds"); return; } - if (p.proposalStatus !== "downloading" /* DOWNLOADING */) { + if (!p.refundStatusRequested) { return; } - const amount = Amounts.parseOrThrow(parsedContractTerms.amount); - let maxWireFee; - if (parsedContractTerms.max_wire_fee) { - maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); - } - else { - maxWireFee = Amounts.getZero(amount.currency); - } - p.download = { - contractData: { - amount, - contractTermsHash: contractTermsHash, - fulfillmentUrl: parsedContractTerms.fulfillment_url, - merchantBaseUrl: parsedContractTerms.merchant_base_url, - merchantPub: parsedContractTerms.merchant_pub, - merchantSig: proposalResp.sig, - orderId: parsedContractTerms.order_id, - summary: parsedContractTerms.summary, - autoRefund: parsedContractTerms.auto_refund, - maxWireFee, - payDeadline: parsedContractTerms.pay_deadline, - refundDeadline: parsedContractTerms.refund_deadline, - wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1, - allowedAuditors: parsedContractTerms.auditors.map(x => ({ - auditorBaseUrl: x.url, - auditorPub: x.master_pub, - })), - allowedExchanges: parsedContractTerms.exchanges.map(x => ({ - exchangeBaseUrl: x.url, - exchangePub: x.master_pub, - })), - timestamp: parsedContractTerms.timestamp, - wireMethod: parsedContractTerms.wire_method, - wireInfoHash: parsedContractTerms.h_wire, - maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee), - }, - contractTermsRaw: JSON.stringify(proposalResp.contract_terms), - }; - if (fulfillmentUrl.startsWith("http://") || - fulfillmentUrl.startsWith("https://")) { - const differentPurchase = yield tx.getIndexed(dbTypes.Stores.purchases.fulfillmentUrlIndex, fulfillmentUrl); - if (differentPurchase) { - console.log("repurchase detected"); - p.proposalStatus = "repurchase" /* REPURCHASE */; - p.repurchaseProposalId = differentPurchase.proposalId; - yield tx.put(dbTypes.Stores.proposals, p); - return; + for (const perm of refundPermissions) { + const isDone = p.refundState.refundsDone[perm.merchant_sig]; + const isPending = p.refundState.refundsPending[perm.merchant_sig]; + if (!isDone && !isPending) { + p.refundState.refundsPending[perm.merchant_sig] = { + perm, + refundGroupId, + }; + numNewRefunds++; } } - p.proposalStatus = "proposed" /* PROPOSED */; - yield tx.put(dbTypes.Stores.proposals, p); - })); - ws.notify({ - type: "proposal-downloaded" /* ProposalDownloaded */, - proposalId: proposal.proposalId, - }); - }); -} -/** - * Download a proposal and store it in the database. - * Returns an id for it to retrieve it later. - * - * @param sessionId Current session ID, if the proposal is being - * downloaded in the context of a session ID. - */ -function startDownloadProposal(ws, merchantBaseUrl, orderId, sessionId) { - return __awaiter(this, void 0, void 0, function* () { - const oldProposal = yield ws.db.getIndexed(dbTypes.Stores.proposals.urlAndOrderIdIndex, [merchantBaseUrl, orderId]); - if (oldProposal) { - yield processDownloadProposal(ws, oldProposal.proposalId); - return oldProposal.proposalId; - } - const { priv, pub } = yield ws.cryptoApi.createEddsaKeypair(); - const proposalId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); - const proposalRecord = { - download: undefined, - noncePriv: priv, - noncePub: pub, - timestamp: time.getTimestampNow(), - merchantBaseUrl, - orderId, - proposalId: proposalId, - proposalStatus: "downloading" /* DOWNLOADING */, - repurchaseProposalId: undefined, - retryInfo: dbTypes.initRetryInfo(), - lastError: undefined, - downloadSessionId: sessionId, - }; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => __awaiter(this, void 0, void 0, function* () { - const existingRecord = yield tx.getIndexed(dbTypes.Stores.proposals.urlAndOrderIdIndex, [merchantBaseUrl, orderId]); - if (existingRecord) { - // Created concurrently - return; - } - yield tx.put(dbTypes.Stores.proposals, proposalRecord); - })); - yield processDownloadProposal(ws, proposalId); - return proposalId; - }); -} -function submitPay(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); - if (!purchase) { - throw Error("Purchase not found: " + proposalId); - } - if (purchase.abortRequested) { - throw Error("not submitting payment for aborted purchase"); - } - const sessionId = purchase.lastSessionId; - let resp; - const payReq = Object.assign(Object.assign({}, purchase.payReq), { session_id: sessionId }); - console.log("paying with session ID", sessionId); - const payUrl = new URL("pay", purchase.contractData.merchantBaseUrl).href; - try { - console.log("pay req", payReq); - resp = yield ws.http.postJson(payUrl, payReq); - } - catch (e) { - // Gives the user the option to retry / abort and refresh - console.log("payment failed", e); - throw e; - } - if (resp.status !== 200) { - console.log(yield resp.json()); - throw Error(`unexpected status (${resp.status}) for /pay`); - } - const merchantResp = yield resp.json(); - console.log("got success from pay URL", merchantResp); - const now = time.getTimestampNow(); - const merchantPub = purchase.contractData.merchantPub; - const valid = yield ws.cryptoApi.isValidPaymentSignature(merchantResp.sig, purchase.contractData.contractTermsHash, merchantPub); - if (!valid) { - console.error("merchant payment signature invalid"); - // FIXME: properly display error - throw Error("merchant payment signature invalid"); - } - const isFirst = purchase.timestampFirstSuccessfulPay === undefined; - purchase.timestampFirstSuccessfulPay = now; - purchase.paymentSubmitPending = false; - purchase.lastPayError = undefined; - purchase.payRetryInfo = dbTypes.initRetryInfo(false); - if (isFirst) { - const ar = purchase.contractData.autoRefund; - if (ar) { - console.log("auto_refund present"); - purchase.refundStatusRequested = true; - purchase.refundStatusRetryInfo = dbTypes.initRetryInfo(); - purchase.lastRefundStatusError = undefined; - purchase.autoRefundDeadline = time.timestampAddDuration(now, ar); + // Are we done with querying yet, or do we need to do another round + // after a retry delay? + let queryDone = true; + if (numNewRefunds === 0) { + if (p.autoRefundDeadline && + p.autoRefundDeadline.t_ms > time.getTimestampNow().t_ms) { + queryDone = false; + } + } + if (queryDone) { + p.timestampLastRefundStatus = time.getTimestampNow(); + p.lastRefundStatusError = undefined; + p.refundStatusRetryInfo = dbTypes.initRetryInfo(); + p.refundStatusRequested = false; + console.log("refund query done"); + } + else { + // No error, but we need to try again! + p.timestampLastRefundStatus = time.getTimestampNow(); + p.refundStatusRetryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(p.refundStatusRetryInfo); + p.lastRefundStatusError = undefined; + console.log("refund query not done"); + } + if (numNewRefunds > 0) { + const now = time.getTimestampNow(); + p.lastRefundApplyError = undefined; + p.refundApplyRetryInfo = dbTypes.initRetryInfo(); + p.refundState.refundGroups.push({ + timestampQueried: now, + reason, + }); } + yield tx.put(dbTypes.Stores.purchases, p); + })); + ws.notify({ + type: "refund-queried" /* RefundQueried */, + }); + if (numNewRefunds > 0) { + yield processPurchaseApplyRefund(ws, proposalId); } - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases, dbTypes.Stores.payEvents], (tx) => __awaiter(this, void 0, void 0, function* () { - yield tx.put(dbTypes.Stores.purchases, purchase); - const payEvent = { - proposalId, - sessionId, - timestamp: now, - isReplay: !isFirst, - }; - yield tx.put(dbTypes.Stores.payEvents, payEvent); + }); +} +exports.acceptRefundResponse = acceptRefundResponse; +function startRefundQuery(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!p) { + console.log("no purchase found for refund URL"); + return false; + } + p.refundStatusRequested = true; + p.lastRefundStatusError = undefined; + p.refundStatusRetryInfo = dbTypes.initRetryInfo(); + yield tx.put(dbTypes.Stores.purchases, p); + return true; })); - const nextUrl = getNextUrl(purchase.contractData); - ws.cachedNextUrl[purchase.contractData.fulfillmentUrl] = { - nextUrl, - lastSessionId: sessionId, - }; - return { nextUrl }; + if (!success) { + return; + } + ws.notify({ + type: "refund-started" /* RefundStarted */, + }); + yield processPurchaseQueryRefund(ws, proposalId); }); } -exports.submitPay = submitPay; /** - * Check if a payment for the given taler://pay/ URI is possible. - * - * If the payment is possible, the signature are already generated but not - * yet send to the merchant. + * Accept a refund, return the contract hash for the contract + * that was involved in the refund. */ -function preparePayForUri(ws, talerPayUri) { - return __awaiter(this, void 0, void 0, function* () { - const uriResult = taleruri.parsePayUri(talerPayUri); - if (!uriResult) { - return { - status: "error", - error: "URI not supported", - }; - } - let proposalId = yield startDownloadProposal(ws, uriResult.merchantBaseUrl, uriResult.orderId, uriResult.sessionId); - let proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); - if (!proposal) { - throw Error(`could not get proposal ${proposalId}`); - } - if (proposal.proposalStatus === "repurchase" /* REPURCHASE */) { - const existingProposalId = proposal.repurchaseProposalId; - if (!existingProposalId) { - throw Error("invalid proposal state"); - } - console.log("using existing purchase for same product"); - proposal = yield ws.db.get(dbTypes.Stores.proposals, existingProposalId); - if (!proposal) { - throw Error("existing proposal is in wrong state"); - } - } - const d = proposal.download; - if (!d) { - console.error("bad proposal", proposal); - throw Error("proposal is in invalid state"); - } - const contractData = d.contractData; - const merchantSig = d.contractData.merchantSig; - if (!merchantSig) { - throw Error("BUG: proposal is in invalid state"); +function applyRefund(ws, talerRefundUri) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const parseResult = taleruri.parseRefundUri(talerRefundUri); + console.log("applying refund", parseResult); + if (!parseResult) { + throw Error("invalid refund URI"); } - proposalId = proposal.proposalId; - // First check if we already payed for it. - const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + const purchase = yield ws.db.getIndexed(dbTypes.Stores.purchases.orderIdIndex, [ + parseResult.merchantBaseUrl, + parseResult.orderId, + ]); if (!purchase) { - // If not already paid, check if we could pay for it. - const res = yield getCoinsForPayment(ws, contractData); - if (!res) { - console.log("not confirming payment, insufficient coins"); - return { - status: "insufficient-balance", - contractTermsRaw: d.contractTermsRaw, - proposalId: proposal.proposalId, - }; - } - const totalCost = yield getTotalPaymentCost(ws, res); - const totalFees = Amounts.sub(totalCost, res.paymentAmount).amount; - return { - status: "payment-possible", - contractTermsRaw: d.contractTermsRaw, - proposalId: proposal.proposalId, - totalFees, - }; - } - if (uriResult.sessionId && purchase.lastSessionId !== uriResult.sessionId) { - console.log("automatically re-submitting payment with different session ID"); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => __awaiter(this, void 0, void 0, function* () { - const p = yield tx.get(dbTypes.Stores.purchases, proposalId); - if (!p) { - return; - } - p.lastSessionId = uriResult.sessionId; - yield tx.put(dbTypes.Stores.purchases, p); - })); - yield submitPay(ws, proposalId); + throw Error(`no purchase for the taler://refund/ URI (${talerRefundUri}) was found`); } - return { - status: "paid", - contractTermsRaw: purchase.contractTermsRaw, - nextUrl: getNextUrl(purchase.contractData), - }; + logger.info("processing purchase for refund"); + yield startRefundQuery(ws, purchase.proposalId); + return { contractTermsHash: purchase.contractData.contractTermsHash }; }); } -exports.preparePayForUri = preparePayForUri; -/** - * Add a contract to the wallet and sign coins, and send them. - */ -function confirmPay(ws, proposalId, sessionIdOverride) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace(`executing confirmPay with proposalId ${proposalId} and sessionIdOverride ${sessionIdOverride}`); - const proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); - if (!proposal) { - throw Error(`proposal with id ${proposalId} not found`); +exports.applyRefund = applyRefund; +function processPurchaseQueryRefund(ws, proposalId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementPurchaseQueryRefundRetry(ws, proposalId, e); + yield errors.guardOperationException(() => processPurchaseQueryRefundImpl(ws, proposalId, forceNow), onOpErr); + }); +} +exports.processPurchaseQueryRefund = processPurchaseQueryRefund; +function resetPurchaseQueryRefundRetry(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, (x) => { + if (x.refundStatusRetryInfo.active) { + x.refundStatusRetryInfo = dbTypes.initRetryInfo(); + } + return x; + }); + }); +} +function processPurchaseQueryRefundImpl(ws, proposalId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (forceNow) { + yield resetPurchaseQueryRefundRetry(ws, proposalId); } - const d = proposal.download; - if (!d) { - throw Error("proposal is in invalid state"); + const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + if (!purchase) { + return; } - let purchase = yield ws.db.get(dbTypes.Stores.purchases, d.contractData.contractTermsHash); - if (purchase) { - if (sessionIdOverride !== undefined && - sessionIdOverride != purchase.lastSessionId) { - logger.trace(`changing session ID to ${sessionIdOverride}`); - yield ws.db.mutate(dbTypes.Stores.purchases, purchase.proposalId, x => { - x.lastSessionId = sessionIdOverride; - x.paymentSubmitPending = true; - return x; - }); - } - logger.trace("confirmPay: submitting payment for existing purchase"); - return submitPay(ws, proposalId); + if (!purchase.refundStatusRequested) { + return; } - logger.trace("confirmPay: purchase record does not exist yet"); - const res = yield getCoinsForPayment(ws, d.contractData); - logger.trace("coin selection result", res); - if (!res) { - // Should not happen, since checkPay should be called first - console.log("not confirming payment, insufficient coins"); - throw Error("insufficient balance"); + const refundUrlObj = new URL("refund", purchase.contractData.merchantBaseUrl); + refundUrlObj.searchParams.set("order_id", purchase.contractData.orderId); + const refundUrl = refundUrlObj.href; + let resp; + try { + resp = yield ws.http.get(refundUrl); } - const depositPermissions = []; - for (let i = 0; i < res.coinPubs.length; i++) { - const coin = yield ws.db.get(dbTypes.Stores.coins, res.coinPubs[i]); - if (!coin) { - throw Error("can't pay, allocated coin not found anymore"); - } - const denom = yield ws.db.get(dbTypes.Stores.denominations, [ - coin.exchangeBaseUrl, - coin.denomPub, - ]); - if (!denom) { - throw Error("can't pay, denomination of allocated coin not found anymore"); - } - const dp = yield ws.cryptoApi.signDepositPermission({ - coinPriv: coin.coinPriv, - coinPub: coin.coinPub, - contractTermsHash: d.contractData.contractTermsHash, - denomPub: coin.denomPub, - denomSig: coin.denomSig, - exchangeBaseUrl: coin.exchangeBaseUrl, - feeDeposit: denom.feeDeposit, - merchantPub: d.contractData.merchantPub, - refundDeadline: d.contractData.refundDeadline, - spendAmount: res.coinContributions[i], - timestamp: d.contractData.timestamp, - wireInfoHash: d.contractData.wireInfoHash, - }); - depositPermissions.push(dp); + catch (e) { + console.error("error downloading refund permission", e); + throw e; } - purchase = yield recordConfirmPay(ws, proposal, res, depositPermissions, sessionIdOverride); - logger.trace("confirmPay: submitting payment after creating purchase record"); - logger.trace("purchaseRecord:", purchase); - return submitPay(ws, proposalId); + if (resp.status !== 200) { + throw Error(`unexpected status code (${resp.status}) for /refund`); + } + const refundResponse = talerTypes.codecForMerchantRefundResponse().decode(yield resp.json()); + yield acceptRefundResponse(ws, proposalId, refundResponse, "normal-refund" /* NormalRefund */); }); } -exports.confirmPay = confirmPay; -function processPurchasePay(ws, proposalId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const onOpErr = (e) => incrementPurchasePayRetry(ws, proposalId, e); - yield errors.guardOperationException(() => processPurchasePayImpl(ws, proposalId, forceNow), onOpErr); +function processPurchaseApplyRefund(ws, proposalId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementPurchaseApplyRefundRetry(ws, proposalId, e); + yield errors.guardOperationException(() => processPurchaseApplyRefundImpl(ws, proposalId, forceNow), onOpErr); }); } -exports.processPurchasePay = processPurchasePay; -function resetPurchasePayRetry(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, x => { - if (x.payRetryInfo.active) { - x.payRetryInfo = dbTypes.initRetryInfo(); +exports.processPurchaseApplyRefund = processPurchaseApplyRefund; +function resetPurchaseApplyRefundRetry(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, (x) => { + if (x.refundApplyRetryInfo.active) { + x.refundApplyRetryInfo = dbTypes.initRetryInfo(); } return x; }); }); } -function processPurchasePayImpl(ws, proposalId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { +function processPurchaseApplyRefundImpl(ws, proposalId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (forceNow) { - yield resetPurchasePayRetry(ws, proposalId); + yield resetPurchaseApplyRefundRetry(ws, proposalId); } const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); if (!purchase) { + console.error("not submitting refunds, payment not found:"); return; } - if (!purchase.paymentSubmitPending) { + const pendingKeys = Object.keys(purchase.refundState.refundsPending); + if (pendingKeys.length === 0) { + console.log("no pending refunds"); return; } - logger.trace(`processing purchase pay ${proposalId}`); - yield submitPay(ws, proposalId); - }); -} -function refuseProposal(ws, proposalId) { - return __awaiter(this, void 0, void 0, function* () { - const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => __awaiter(this, void 0, void 0, function* () { - const proposal = yield tx.get(dbTypes.Stores.proposals, proposalId); - if (!proposal) { - logger.trace(`proposal ${proposalId} not found, won't refuse proposal`); - return false; + const newRefundsDone = {}; + const newRefundsFailed = {}; + for (const pk of pendingKeys) { + const info = purchase.refundState.refundsPending[pk]; + const perm = info.perm; + const req = { + coin_pub: perm.coin_pub, + h_contract_terms: purchase.contractData.contractTermsHash, + merchant_pub: purchase.contractData.merchantPub, + merchant_sig: perm.merchant_sig, + refund_amount: perm.refund_amount, + refund_fee: perm.refund_fee, + rtransaction_id: perm.rtransaction_id, + }; + console.log("sending refund permission", perm); + // FIXME: not correct once we support multiple exchanges per payment + const exchangeUrl = purchase.payReq.coins[0].exchange_url; + const reqUrl = new URL(`coins/${perm.coin_pub}/refund`, exchangeUrl); + const resp = yield ws.http.postJson(reqUrl.href, req); + console.log("sent refund permission"); + switch (resp.status) { + case http.HttpResponseStatus.Ok: + newRefundsDone[pk] = info; + break; + case http.HttpResponseStatus.Gone: + // We're too late, refund is expired. + newRefundsFailed[pk] = info; + break; + default: { + let body = null; + // FIXME: error handling! + body = yield resp.json(); + const m = "refund request (at exchange) failed"; + throw new errors.OperationFailedError({ + message: m, + type: "network", + details: { + body, + }, + }); + } } - if (proposal.proposalStatus !== "proposed" /* PROPOSED */) { - return false; + } + let allRefundsProcessed = false; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases, dbTypes.Stores.coins, dbTypes.Stores.refreshGroups, dbTypes.Stores.refundEvents], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!p) { + return; + } + // Groups that failed/succeeded + const groups = {}; + // Avoid duplicates + const refreshCoinsMap = {}; + const modCoin = (perm) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const c = yield tx.get(dbTypes.Stores.coins, perm.coin_pub); + if (!c) { + console.warn("coin not found, can't apply refund"); + return; + } + refreshCoinsMap[c.coinPub] = { coinPub: c.coinPub }; + logger.trace(`commiting refund ${perm.merchant_sig} to coin ${c.coinPub}`); + logger.trace(`coin amount before is ${amounts.Amounts.stringify(c.currentAmount)}`); + logger.trace(`refund amount (via merchant) is ${perm.refund_amount}`); + logger.trace(`refund fee (via merchant) is ${perm.refund_fee}`); + const refundAmount = amounts.Amounts.parseOrThrow(perm.refund_amount); + const refundFee = amounts.Amounts.parseOrThrow(perm.refund_fee); + c.status = "dormant" /* Dormant */; + c.currentAmount = amounts.Amounts.add(c.currentAmount, refundAmount).amount; + c.currentAmount = amounts.Amounts.sub(c.currentAmount, refundFee).amount; + logger.trace(`coin amount after is ${amounts.Amounts.stringify(c.currentAmount)}`); + yield tx.put(dbTypes.Stores.coins, c); + }); + for (const pk of Object.keys(newRefundsFailed)) { + if (p.refundState.refundsDone[pk]) { + // We already processed this one. + break; + } + const r = newRefundsFailed[pk]; + groups[r.refundGroupId] = true; + delete p.refundState.refundsPending[pk]; + p.refundState.refundsFailed[pk] = r; + } + for (const pk of Object.keys(newRefundsDone)) { + if (p.refundState.refundsDone[pk]) { + // We already processed this one. + break; + } + const r = newRefundsDone[pk]; + groups[r.refundGroupId] = true; + delete p.refundState.refundsPending[pk]; + p.refundState.refundsDone[pk] = r; + yield modCoin(r.perm); + } + const now = time.getTimestampNow(); + for (const g of Object.keys(groups)) { + let groupDone = true; + for (const pk of Object.keys(p.refundState.refundsPending)) { + const r = p.refundState.refundsPending[pk]; + if (r.refundGroupId == g) { + groupDone = false; + } + } + if (groupDone) { + const refundEvent = { + proposalId, + refundGroupId: g, + timestamp: now, + }; + yield tx.put(dbTypes.Stores.refundEvents, refundEvent); + } + } + if (Object.keys(p.refundState.refundsPending).length === 0) { + p.refundStatusRetryInfo = dbTypes.initRetryInfo(); + p.lastRefundStatusError = undefined; + allRefundsProcessed = true; + } + yield tx.put(dbTypes.Stores.purchases, p); + const coinsPubsToBeRefreshed = Object.values(refreshCoinsMap); + if (coinsPubsToBeRefreshed.length > 0) { + yield refresh.createRefreshGroup(tx, coinsPubsToBeRefreshed, "refund" /* Refund */); } - proposal.proposalStatus = "refused" /* REFUSED */; - yield tx.put(dbTypes.Stores.proposals, proposal); - return true; })); - if (success) { + if (allRefundsProcessed) { ws.notify({ - type: "wildcard" /* Wildcard */, + type: "refund-finished" /* RefundFinished */, }); } + ws.notify({ + type: "refunds-submitted" /* RefundsSubmitted */, + proposalId, + }); }); } -exports.refuseProposal = refuseProposal; - -}); - -unwrapExports(pay); -var pay_1 = pay.getTotalPaymentCost; -var pay_2 = pay.selectPayCoins; -var pay_3 = pay.abortFailedPayment; -var pay_4 = pay.processDownloadProposal; -var pay_5 = pay.submitPay; -var pay_6 = pay.preparePayForUri; -var pay_7 = pay.confirmPay; -var pay_8 = pay.processPurchasePay; -var pay_9 = pay.refuseProposal; - -var assertUnreachable_1 = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -Object.defineProperty(exports, "__esModule", { value: true }); -function assertUnreachable(x) { - throw new Error("Didn't expect to get here"); -} -exports.assertUnreachable = assertUnreachable; - -}); - -unwrapExports(assertUnreachable_1); -var assertUnreachable_2 = assertUnreachable_1.assertUnreachable; - -var ReserveTransaction = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * @author Florian Dold - */ -/** - * Imports. - */ - - -exports.codecForReserveWithdrawTransaction = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("amount", codec.codecForString) - .property("h_coin_envelope", codec.codecForString) - .property("h_denom_pub", codec.codecForString) - .property("reserve_sig", codec.codecForString) - .property("type", codec.makeCodecForConstString("WITHDRAW" /* Withdraw */)) - .property("withdraw_fee", codec.codecForString) - .build("ReserveWithdrawTransaction")); -exports.codecForReserveDepositTransaction = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("amount", codec.codecForString) - .property("sender_account_url", codec.codecForString) - .property("timestamp", time.codecForTimestamp) - .property("wire_reference", codec.codecForString) - .property("type", codec.makeCodecForConstString("DEPOSIT" /* Deposit */)) - .build("ReserveDepositTransaction")); -exports.codecForReserveClosingTransaction = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("amount", codec.codecForString) - .property("closing_fee", codec.codecForString) - .property("exchange_pub", codec.codecForString) - .property("exchange_sig", codec.codecForString) - .property("h_wire", codec.codecForString) - .property("timestamp", time.codecForTimestamp) - .property("type", codec.makeCodecForConstString("CLOSING" /* Closing */)) - .property("wtid", codec.codecForString) - .build("ReserveClosingTransaction")); -exports.codecForReservePaybackTransaction = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("amount", codec.codecForString) - .property("coin_pub", codec.codecForString) - .property("exchange_pub", codec.codecForString) - .property("exchange_sig", codec.codecForString) - .property("receiver_account_details", codec.codecForString) - .property("timestamp", time.codecForTimestamp) - .property("type", codec.makeCodecForConstString("PAYBACK" /* Payback */)) - .property("wire_transfer", codec.codecForString) - .build("ReservePaybackTransaction")); -exports.codecForReserveTransaction = () => codec.typecheckedCodec(codec.makeCodecForUnion() - .discriminateOn("type") - .alternative("WITHDRAW" /* Withdraw */, exports.codecForReserveWithdrawTransaction()) - .alternative("CLOSING" /* Closing */, exports.codecForReserveClosingTransaction()) - .alternative("PAYBACK" /* Payback */, exports.codecForReservePaybackTransaction()) - .alternative("DEPOSIT" /* Deposit */, exports.codecForReserveDepositTransaction()) - .build("ReserveTransaction")); }); -unwrapExports(ReserveTransaction); -var ReserveTransaction_1 = ReserveTransaction.codecForReserveWithdrawTransaction; -var ReserveTransaction_2 = ReserveTransaction.codecForReserveDepositTransaction; -var ReserveTransaction_3 = ReserveTransaction.codecForReserveClosingTransaction; -var ReserveTransaction_4 = ReserveTransaction.codecForReservePaybackTransaction; -var ReserveTransaction_5 = ReserveTransaction.codecForReserveTransaction; +unwrapExports(refund); +var refund_1 = refund.getFullRefundFees; +var refund_2 = refund.acceptRefundResponse; +var refund_3 = refund.applyRefund; +var refund_4 = refund.processPurchaseQueryRefund; +var refund_5 = refund.processPurchaseApplyRefund; -var ReserveStatus = createCommonjsModule(function (module, exports) { +var pay = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler (C) 2019 Taler Systems S.A. @@ -10478,63 +10221,20 @@ var ReserveStatus = createCommonjsModule(function (module, exports) { GNU Taler; see the file COPYING. If not, see */ Object.defineProperty(exports, "__esModule", { value: true }); + /** - * @author Florian Dold + * Implementation of the payment operation, including downloading and + * claiming of proposals. + * + * @author Florian Dold */ /** * Imports. */ -exports.codecForReserveStatus = () => codec.typecheckedCodec(codec.makeCodecForObject() - .property("balance", codec.codecForString) - .property("history", codec.makeCodecForList(ReserveTransaction.codecForReserveTransaction())) - .build("ReserveStatus")); - -}); - -unwrapExports(ReserveStatus); -var ReserveStatus_1 = ReserveStatus.codecForReserveStatus; - -var reserves = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); - - - - -const Amounts = __importStar(amounts); +const Amounts = tslib_1.__importStar(amounts); @@ -10542,577 +10242,886 @@ const Amounts = __importStar(amounts); - -const logger = new logging.Logger("reserves.ts"); -function resetReserveRetry(ws, reservePub) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, x => { - if (x.retryInfo.active) { - x.retryInfo = dbTypes.initRetryInfo(); +/** + * Logger. + */ +const logger = new logging.Logger("pay.ts"); +/** + * Compute the total cost of a payment to the customer. + * + * This includes the amount taken by the merchant, fees (wire/deposit) contributed + * by the customer, refreshing fees, fees for withdraw-after-refresh and "trimmings" + * of coins that are too small to spend. + */ +function getTotalPaymentCost(ws, pcs) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const costs = [ + pcs.paymentAmount, + pcs.customerDepositFees, + pcs.customerWireFees, + ]; + for (let i = 0; i < pcs.coinPubs.length; i++) { + const coin = yield ws.db.get(dbTypes.Stores.coins, pcs.coinPubs[i]); + if (!coin) { + throw Error("can't calculate payment cost, coin not found"); + } + const denom = yield ws.db.get(dbTypes.Stores.denominations, [ + coin.exchangeBaseUrl, + coin.denomPub, + ]); + if (!denom) { + throw Error("can't calculate payment cost, denomination for coin not found"); + } + const allDenoms = yield ws.db + .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, coin.exchangeBaseUrl) + .toArray(); + const amountLeft = Amounts.sub(denom.value, pcs.coinContributions[i]) + .amount; + const refreshCost = refresh.getTotalRefreshCost(allDenoms, denom, amountLeft); + costs.push(refreshCost); + } + return Amounts.sum(costs).amount; + }); +} +exports.getTotalPaymentCost = getTotalPaymentCost; +/** + * Given a list of available coins, select coins to spend under the merchant's + * constraints. + * + * This function is only exported for the sake of unit tests. + */ +function selectPayCoins(acis, paymentAmount, depositFeeLimit) { + if (acis.length === 0) { + return undefined; + } + const coinPubs = []; + const coinContributions = []; + // Sort by available amount (descending), deposit fee (ascending) and + // denomPub (ascending) if deposit fee is the same + // (to guarantee deterministic results) + acis.sort((o1, o2) => -Amounts.cmp(o1.availableAmount, o2.availableAmount) || + Amounts.cmp(o1.feeDeposit, o2.feeDeposit) || + helpers.strcmp(o1.denomPub, o2.denomPub)); + const currency = paymentAmount.currency; + let totalFees = Amounts.getZero(currency); + let amountPayRemaining = paymentAmount; + let amountDepositFeeLimitRemaining = depositFeeLimit; + const customerWireFees = Amounts.getZero(currency); + const customerDepositFees = Amounts.getZero(currency); + for (const aci of acis) { + // Don't use this coin if depositing it is more expensive than + // the amount it would give the merchant. + if (Amounts.cmp(aci.feeDeposit, aci.availableAmount) >= 0) { + continue; + } + if (amountPayRemaining.value === 0 && amountPayRemaining.fraction === 0) { + // We have spent enough! + break; + } + // How much does the user spend on deposit fees for this coin? + const depositFeeSpend = Amounts.sub(aci.feeDeposit, amountDepositFeeLimitRemaining).amount; + if (Amounts.isZero(depositFeeSpend)) { + // Fees are still covered by the merchant. + amountDepositFeeLimitRemaining = Amounts.sub(amountDepositFeeLimitRemaining, aci.feeDeposit).amount; + } + else { + amountDepositFeeLimitRemaining = Amounts.getZero(currency); + } + let coinSpend; + const amountActualAvailable = Amounts.sub(aci.availableAmount, depositFeeSpend).amount; + if (Amounts.cmp(amountActualAvailable, amountPayRemaining) > 0) { + // Partial spending, as the coin is worth more than the remaining + // amount to pay. + coinSpend = Amounts.add(amountPayRemaining, depositFeeSpend).amount; + // Make sure we contribute at least the deposit fee, otherwise + // contributing this coin would cause a loss for the merchant. + if (Amounts.cmp(coinSpend, aci.feeDeposit) < 0) { + coinSpend = aci.feeDeposit; + } + amountPayRemaining = Amounts.getZero(currency); + } + else { + // Spend the full remaining amount on the coin + coinSpend = aci.availableAmount; + amountPayRemaining = Amounts.add(amountPayRemaining, depositFeeSpend) + .amount; + amountPayRemaining = Amounts.sub(amountPayRemaining, aci.availableAmount) + .amount; + } + coinPubs.push(aci.coinPub); + coinContributions.push(coinSpend); + totalFees = Amounts.add(totalFees, depositFeeSpend).amount; + } + if (Amounts.isZero(amountPayRemaining)) { + return { + paymentAmount, + coinContributions, + coinPubs, + customerDepositFees, + customerWireFees, + }; + } + return undefined; +} +exports.selectPayCoins = selectPayCoins; +/** + * Select coins from the wallet's database that can be used + * to pay for the given contract. + * + * If payment is impossible, undefined is returned. + */ +function getCoinsForPayment(ws, contractData) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + let remainingAmount = contractData.amount; + const exchanges = yield ws.db.iter(dbTypes.Stores.exchanges).toArray(); + for (const exchange of exchanges) { + let isOkay = false; + const exchangeDetails = exchange.details; + if (!exchangeDetails) { + continue; + } + const exchangeFees = exchange.wireInfo; + if (!exchangeFees) { + continue; + } + // is the exchange explicitly allowed? + for (const allowedExchange of contractData.allowedExchanges) { + if (allowedExchange.exchangePub === exchangeDetails.masterPublicKey) { + isOkay = true; + break; + } + } + // is the exchange allowed because of one of its auditors? + if (!isOkay) { + for (const allowedAuditor of contractData.allowedAuditors) { + for (const auditor of exchangeDetails.auditors) { + if (auditor.auditor_pub === allowedAuditor.auditorPub) { + isOkay = true; + break; + } + } + if (isOkay) { + break; + } + } + } + if (!isOkay) { + continue; + } + const coins = yield ws.db + .iterIndex(dbTypes.Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl) + .toArray(); + if (!coins || coins.length === 0) { + continue; + } + // Denomination of the first coin, we assume that all other + // coins have the same currency + const firstDenom = yield ws.db.get(dbTypes.Stores.denominations, [ + exchange.baseUrl, + coins[0].denomPub, + ]); + if (!firstDenom) { + throw Error("db inconsistent"); + } + const currency = firstDenom.value.currency; + const acis = []; + for (const coin of coins) { + const denom = yield ws.db.get(dbTypes.Stores.denominations, [ + exchange.baseUrl, + coin.denomPub, + ]); + if (!denom) { + throw Error("db inconsistent"); + } + if (denom.value.currency !== currency) { + console.warn(`same pubkey for different currencies at exchange ${exchange.baseUrl}`); + continue; + } + if (coin.suspended) { + continue; + } + if (coin.status !== "fresh" /* Fresh */) { + continue; + } + acis.push({ + availableAmount: coin.currentAmount, + coinPub: coin.coinPub, + denomPub: coin.denomPub, + feeDeposit: denom.feeDeposit, + }); + } + let totalFees = Amounts.getZero(currency); + let wireFee; + for (const fee of exchangeFees.feesForType[contractData.wireMethod] || []) { + if (fee.startStamp <= contractData.timestamp && + fee.endStamp >= contractData.timestamp) { + wireFee = fee.wireFee; + break; + } } - return x; - }); + if (wireFee) { + const amortizedWireFee = Amounts.divide(wireFee, contractData.wireFeeAmortization); + if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) { + totalFees = Amounts.add(amortizedWireFee, totalFees).amount; + remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount; + } + } + // Try if paying using this exchange works + const res = selectPayCoins(acis, remainingAmount, contractData.maxDepositFee); + if (res) { + return res; + } + } + return undefined; }); } /** - * Create a reserve, but do not flag it as confirmed yet. - * - * Adds the corresponding exchange as a trusted exchange if it is neither - * audited nor trusted already. + * Record all information that is necessary to + * pay for a proposal in the wallet's database. */ -function createReserve(ws, req) { - return __awaiter(this, void 0, void 0, function* () { - const keypair = yield ws.cryptoApi.createEddsaKeypair(); - const now = time.getTimestampNow(); - const canonExchange = helpers.canonicalizeBaseUrl(req.exchange); - let reserveStatus; - if (req.bankWithdrawStatusUrl) { - reserveStatus = dbTypes.ReserveRecordStatus.REGISTERING_BANK; +function recordConfirmPay(ws, proposal, coinSelection, coinDepositPermissions, sessionIdOverride) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const d = proposal.download; + if (!d) { + throw Error("proposal is in invalid state"); + } + let sessionId; + if (sessionIdOverride) { + sessionId = sessionIdOverride; } else { - reserveStatus = dbTypes.ReserveRecordStatus.UNCONFIRMED; + sessionId = proposal.downloadSessionId; } - const currency = req.amount.currency; - const reserveRecord = { - timestampCreated: now, - amountWithdrawAllocated: Amounts.getZero(currency), - amountWithdrawCompleted: Amounts.getZero(currency), - amountWithdrawRemaining: Amounts.getZero(currency), - exchangeBaseUrl: canonExchange, - hasPayback: false, - amountInitiallyRequested: req.amount, - reservePriv: keypair.priv, - reservePub: keypair.pub, - senderWire: req.senderWire, - timestampConfirmed: undefined, - timestampReserveInfoPosted: undefined, - bankWithdrawStatusUrl: req.bankWithdrawStatusUrl, - exchangeWire: req.exchangeWire, - reserveStatus, - lastSuccessfulStatusQuery: undefined, - retryInfo: dbTypes.initRetryInfo(), - lastError: undefined, - reserveTransactions: [], + logger.trace(`recording payment with session ID ${sessionId}`); + const payReq = { + coins: coinDepositPermissions, + merchant_pub: d.contractData.merchantPub, + mode: "pay", + order_id: d.contractData.orderId, }; - const senderWire = req.senderWire; - if (senderWire) { - const rec = { - paytoUri: senderWire, - }; - yield ws.db.put(dbTypes.Stores.senderWires, rec); + const t = { + abortDone: false, + abortRequested: false, + contractTermsRaw: d.contractTermsRaw, + contractData: d.contractData, + lastSessionId: sessionId, + payReq, + timestampAccept: time.getTimestampNow(), + timestampLastRefundStatus: undefined, + proposalId: proposal.proposalId, + lastPayError: undefined, + lastRefundStatusError: undefined, + payRetryInfo: dbTypes.initRetryInfo(), + refundStatusRetryInfo: dbTypes.initRetryInfo(), + refundStatusRequested: false, + lastRefundApplyError: undefined, + refundApplyRetryInfo: dbTypes.initRetryInfo(), + timestampFirstSuccessfulPay: undefined, + autoRefundDeadline: undefined, + paymentSubmitPending: true, + refundState: { + refundGroups: [], + refundsDone: {}, + refundsFailed: {}, + refundsPending: {}, + }, + }; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.purchases, dbTypes.Stores.proposals, dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.proposals, proposal.proposalId); + if (p) { + p.proposalStatus = "accepted" /* ACCEPTED */; + p.lastError = undefined; + p.retryInfo = dbTypes.initRetryInfo(false); + yield tx.put(dbTypes.Stores.proposals, p); + } + yield tx.put(dbTypes.Stores.purchases, t); + for (let i = 0; i < coinSelection.coinPubs.length; i++) { + const coin = yield tx.get(dbTypes.Stores.coins, coinSelection.coinPubs[i]); + if (!coin) { + throw Error("coin allocated for payment doesn't exist anymore"); + } + coin.status = "dormant" /* Dormant */; + const remaining = Amounts.sub(coin.currentAmount, coinSelection.coinContributions[i]); + if (remaining.saturated) { + throw Error("not enough remaining balance on coin for payment"); + } + coin.currentAmount = remaining.amount; + yield tx.put(dbTypes.Stores.coins, coin); + } + const refreshCoinPubs = coinSelection.coinPubs.map((x) => ({ + coinPub: x, + })); + yield refresh.createRefreshGroup(tx, refreshCoinPubs, "pay" /* Pay */); + })); + ws.notify({ + type: "proposal-accepted" /* ProposalAccepted */, + proposalId: proposal.proposalId, + }); + return t; + }); +} +function getNextUrl(contractData) { + const f = contractData.fulfillmentUrl; + if (f.startsWith("http://") || f.startsWith("https://")) { + const fu = new URL(contractData.fulfillmentUrl); + fu.searchParams.set("order_id", contractData.orderId); + return fu.href; + } + else { + return f; + } +} +function abortFailedPayment(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + if (!purchase) { + throw Error("Purchase not found, unable to abort with refund"); } - const exchangeInfo = yield exchanges.updateExchangeFromUrl(ws, req.exchange); - const exchangeDetails = exchangeInfo.details; - if (!exchangeDetails) { - console.log(exchangeDetails); - throw Error("exchange not updated"); + if (purchase.timestampFirstSuccessfulPay) { + throw Error("Purchase already finished, not aborting"); } - const { isAudited, isTrusted } = yield exchanges.getExchangeTrust(ws, exchangeInfo); - let currencyRecord = yield ws.db.get(dbTypes.Stores.currencies, exchangeDetails.currency); - if (!currencyRecord) { - currencyRecord = { - auditors: [], - exchanges: [], - fractionalDigits: 2, - name: exchangeDetails.currency, - }; + if (purchase.abortDone) { + console.warn("abort requested on already aborted purchase"); + return; } - if (!isAudited && !isTrusted) { - currencyRecord.exchanges.push({ - baseUrl: req.exchange, - exchangePub: exchangeDetails.masterPublicKey, - }); + purchase.abortRequested = true; + // From now on, we can't retry payment anymore, + // so mark this in the DB in case the /pay abort + // does not complete on the first try. + yield ws.db.put(dbTypes.Stores.purchases, purchase); + let resp; + const abortReq = Object.assign(Object.assign({}, purchase.payReq), { mode: "abort-refund" }); + const payUrl = new URL("pay", purchase.contractData.merchantBaseUrl).href; + try { + resp = yield ws.http.postJson(payUrl, abortReq); } - const cr = currencyRecord; - const resp = yield ws.db.runWithWriteTransaction([dbTypes.Stores.currencies, dbTypes.Stores.reserves, dbTypes.Stores.bankWithdrawUris], (tx) => __awaiter(this, void 0, void 0, function* () { - // Check if we have already created a reserve for that bankWithdrawStatusUrl - if (reserveRecord.bankWithdrawStatusUrl) { - const bwi = yield tx.get(dbTypes.Stores.bankWithdrawUris, reserveRecord.bankWithdrawStatusUrl); - if (bwi) { - const otherReserve = yield tx.get(dbTypes.Stores.reserves, bwi.reservePub); - if (otherReserve) { - logger.trace("returning existing reserve for bankWithdrawStatusUri"); - return { - exchange: otherReserve.exchangeBaseUrl, - reservePub: otherReserve.reservePub, - }; - } - } - yield tx.put(dbTypes.Stores.bankWithdrawUris, { - reservePub: reserveRecord.reservePub, - talerWithdrawUri: reserveRecord.bankWithdrawStatusUrl, - }); + catch (e) { + // Gives the user the option to retry / abort and refresh + console.log("aborting payment failed", e); + throw e; + } + if (resp.status !== 200) { + throw Error(`unexpected status for /pay (${resp.status})`); + } + const refundResponse = talerTypes.codecForMerchantRefundResponse().decode(yield resp.json()); + yield refund.acceptRefundResponse(ws, purchase.proposalId, refundResponse, "abort-refund" /* AbortRefund */); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!p) { + return; } - yield tx.put(dbTypes.Stores.currencies, cr); - yield tx.put(dbTypes.Stores.reserves, reserveRecord); - const r = { - exchange: canonExchange, - reservePub: keypair.pub, - }; - return r; + p.abortDone = true; + yield tx.put(dbTypes.Stores.purchases, p); })); - ws.notify({ type: "reserve-created" /* ReserveCreated */ }); - // Asynchronously process the reserve, but return - // to the caller already. - processReserve(ws, resp.reservePub, true).catch(e => { - console.error("Processing reserve failed:", e); - }); - return resp; }); } -exports.createReserve = createReserve; -/** - * First fetch information requred to withdraw from the reserve, - * then deplete the reserve, withdrawing coins until it is empty. - * - * The returned promise resolves once the reserve is set to the - * state DORMANT. - */ -function processReserve(ws, reservePub, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - return ws.memoProcessReserve.memo(reservePub, () => __awaiter(this, void 0, void 0, function* () { - const onOpError = (err) => incrementReserveRetry(ws, reservePub, err); - yield errors.guardOperationException(() => processReserveImpl(ws, reservePub, forceNow), onOpError); +exports.abortFailedPayment = abortFailedPayment; +function incrementProposalRetry(ws, proposalId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const pr = yield tx.get(dbTypes.Stores.proposals, proposalId); + if (!pr) { + return; + } + if (!pr.retryInfo) { + return; + } + pr.retryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(pr.retryInfo); + pr.lastError = err; + yield tx.put(dbTypes.Stores.proposals, pr); + })); + ws.notify({ type: "proposal-error" /* ProposalOperationError */ }); + }); +} +function incrementPurchasePayRetry(ws, proposalId, err) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + console.log("incrementing purchase pay retry with error", err); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const pr = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!pr) { + return; + } + if (!pr.payRetryInfo) { + return; + } + pr.payRetryInfo.retryCounter++; + dbTypes.updateRetryInfoTimeout(pr.payRetryInfo); + pr.lastPayError = err; + yield tx.put(dbTypes.Stores.purchases, pr); })); + ws.notify({ type: "pay-error" /* PayOperationError */ }); }); } -exports.processReserve = processReserve; -function registerReserveWithBank(ws, reservePub) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - let reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - switch ((_a = reserve) === null || _a === void 0 ? void 0 : _a.reserveStatus) { - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - case dbTypes.ReserveRecordStatus.REGISTERING_BANK: - break; - default: - return; +function processDownloadProposal(ws, proposalId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (err) => incrementProposalRetry(ws, proposalId, err); + yield errors.guardOperationException(() => processDownloadProposalImpl(ws, proposalId, forceNow), onOpErr); + }); +} +exports.processDownloadProposal = processDownloadProposal; +function resetDownloadProposalRetry(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.proposals, proposalId, (x) => { + if (x.retryInfo.active) { + x.retryInfo = dbTypes.initRetryInfo(); + } + return x; + }); + }); +} +function processDownloadProposalImpl(ws, proposalId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (forceNow) { + yield resetDownloadProposalRetry(ws, proposalId); } - const bankStatusUrl = reserve.bankWithdrawStatusUrl; - if (!bankStatusUrl) { + const proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); + if (!proposal) { return; } - console.log("making selection"); - if (reserve.timestampReserveInfoPosted) { - throw Error("bank claims that reserve info selection is not done"); + if (proposal.proposalStatus != "downloading" /* DOWNLOADING */) { + return; } - const bankResp = yield ws.http.postJson(bankStatusUrl, { - reserve_pub: reservePub, - selected_exchange: reserve.exchangeWire, - }); - yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, r => { - switch (r.reserveStatus) { - case dbTypes.ReserveRecordStatus.REGISTERING_BANK: - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - break; - default: + const parsedUrl = new URL(taleruri.getOrderDownloadUrl(proposal.merchantBaseUrl, proposal.orderId)); + parsedUrl.searchParams.set("nonce", proposal.noncePub); + const urlWithNonce = parsedUrl.href; + console.log("downloading contract from '" + urlWithNonce + "'"); + let resp; + try { + resp = yield ws.http.get(urlWithNonce); + } + catch (e) { + console.log("contract download failed", e); + throw e; + } + if (resp.status !== 200) { + throw Error(`contract download failed with status ${resp.status}`); + } + const proposalResp = talerTypes.codecForProposal().decode(yield resp.json()); + const contractTermsHash = yield ws.cryptoApi.hashString(helpers.canonicalJson(proposalResp.contract_terms)); + const parsedContractTerms = talerTypes.codecForContractTerms().decode(proposalResp.contract_terms); + const fulfillmentUrl = parsedContractTerms.fulfillment_url; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals, dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.proposals, proposalId); + if (!p) { + return; + } + if (p.proposalStatus !== "downloading" /* DOWNLOADING */) { + return; + } + const amount = Amounts.parseOrThrow(parsedContractTerms.amount); + let maxWireFee; + if (parsedContractTerms.max_wire_fee) { + maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); + } + else { + maxWireFee = Amounts.getZero(amount.currency); + } + p.download = { + contractData: { + amount, + contractTermsHash: contractTermsHash, + fulfillmentUrl: parsedContractTerms.fulfillment_url, + merchantBaseUrl: parsedContractTerms.merchant_base_url, + merchantPub: parsedContractTerms.merchant_pub, + merchantSig: proposalResp.sig, + orderId: parsedContractTerms.order_id, + summary: parsedContractTerms.summary, + autoRefund: parsedContractTerms.auto_refund, + maxWireFee, + payDeadline: parsedContractTerms.pay_deadline, + refundDeadline: parsedContractTerms.refund_deadline, + wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1, + allowedAuditors: parsedContractTerms.auditors.map((x) => ({ + auditorBaseUrl: x.url, + auditorPub: x.master_pub, + })), + allowedExchanges: parsedContractTerms.exchanges.map((x) => ({ + exchangeBaseUrl: x.url, + exchangePub: x.master_pub, + })), + timestamp: parsedContractTerms.timestamp, + wireMethod: parsedContractTerms.wire_method, + wireInfoHash: parsedContractTerms.h_wire, + maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee), + }, + contractTermsRaw: JSON.stringify(proposalResp.contract_terms), + }; + if (fulfillmentUrl.startsWith("http://") || + fulfillmentUrl.startsWith("https://")) { + const differentPurchase = yield tx.getIndexed(dbTypes.Stores.purchases.fulfillmentUrlIndex, fulfillmentUrl); + if (differentPurchase) { + console.log("repurchase detected"); + p.proposalStatus = "repurchase" /* REPURCHASE */; + p.repurchaseProposalId = differentPurchase.proposalId; + yield tx.put(dbTypes.Stores.proposals, p); return; + } } - r.timestampReserveInfoPosted = time.getTimestampNow(); - r.reserveStatus = dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK; - r.retryInfo = dbTypes.initRetryInfo(); - return r; + p.proposalStatus = "proposed" /* PROPOSED */; + yield tx.put(dbTypes.Stores.proposals, p); + })); + ws.notify({ + type: "proposal-downloaded" /* ProposalDownloaded */, + proposalId: proposal.proposalId, }); - ws.notify({ type: "wildcard" /* Wildcard */ }); - return processReserveBankStatus(ws, reservePub); }); } -function processReserveBankStatus(ws, reservePub) { - return __awaiter(this, void 0, void 0, function* () { - const onOpError = (err) => incrementReserveRetry(ws, reservePub, err); - yield errors.guardOperationException(() => processReserveBankStatusImpl(ws, reservePub), onOpError); +/** + * Download a proposal and store it in the database. + * Returns an id for it to retrieve it later. + * + * @param sessionId Current session ID, if the proposal is being + * downloaded in the context of a session ID. + */ +function startDownloadProposal(ws, merchantBaseUrl, orderId, sessionId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const oldProposal = yield ws.db.getIndexed(dbTypes.Stores.proposals.urlAndOrderIdIndex, [merchantBaseUrl, orderId]); + if (oldProposal) { + yield processDownloadProposal(ws, oldProposal.proposalId); + return oldProposal.proposalId; + } + const { priv, pub } = yield ws.cryptoApi.createEddsaKeypair(); + const proposalId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); + const proposalRecord = { + download: undefined, + noncePriv: priv, + noncePub: pub, + timestamp: time.getTimestampNow(), + merchantBaseUrl, + orderId, + proposalId: proposalId, + proposalStatus: "downloading" /* DOWNLOADING */, + repurchaseProposalId: undefined, + retryInfo: dbTypes.initRetryInfo(), + lastError: undefined, + downloadSessionId: sessionId, + }; + yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const existingRecord = yield tx.getIndexed(dbTypes.Stores.proposals.urlAndOrderIdIndex, [merchantBaseUrl, orderId]); + if (existingRecord) { + // Created concurrently + return; + } + yield tx.put(dbTypes.Stores.proposals, proposalRecord); + })); + yield processDownloadProposal(ws, proposalId); + return proposalId; }); } -exports.processReserveBankStatus = processReserveBankStatus; -function processReserveBankStatusImpl(ws, reservePub) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - let reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - switch ((_a = reserve) === null || _a === void 0 ? void 0 : _a.reserveStatus) { - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - case dbTypes.ReserveRecordStatus.REGISTERING_BANK: - break; - default: - return; +function submitPay(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + if (!purchase) { + throw Error("Purchase not found: " + proposalId); } - const bankStatusUrl = reserve.bankWithdrawStatusUrl; - if (!bankStatusUrl) { - return; + if (purchase.abortRequested) { + throw Error("not submitting payment for aborted purchase"); } - let status; + const sessionId = purchase.lastSessionId; + let resp; + const payReq = Object.assign(Object.assign({}, purchase.payReq), { session_id: sessionId }); + console.log("paying with session ID", sessionId); + const payUrl = new URL("pay", purchase.contractData.merchantBaseUrl).href; try { - const statusResp = yield ws.http.get(bankStatusUrl); - if (statusResp.status !== 200) { - throw Error(`unexpected status ${statusResp.status} for bank status query`); - } - status = talerTypes.codecForWithdrawOperationStatusResponse().decode(yield statusResp.json()); + console.log("pay req", payReq); + resp = yield ws.http.postJson(payUrl, payReq); } catch (e) { + // Gives the user the option to retry / abort and refresh + console.log("payment failed", e); throw e; } - ws.notify({ type: "wildcard" /* Wildcard */ }); - if (status.selection_done) { - if (reserve.reserveStatus === dbTypes.ReserveRecordStatus.REGISTERING_BANK) { - yield registerReserveWithBank(ws, reservePub); - return yield processReserveBankStatus(ws, reservePub); - } - } - else { - yield registerReserveWithBank(ws, reservePub); - return yield processReserveBankStatus(ws, reservePub); - } - if (status.transfer_done) { - yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, r => { - switch (r.reserveStatus) { - case dbTypes.ReserveRecordStatus.REGISTERING_BANK: - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - break; - default: - return; - } - const now = time.getTimestampNow(); - r.timestampConfirmed = now; - r.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; - r.retryInfo = dbTypes.initRetryInfo(); - return r; - }); - yield processReserveImpl(ws, reservePub, true); - } - else { - yield ws.db.mutate(dbTypes.Stores.reserves, reservePub, r => { - switch (r.reserveStatus) { - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - break; - default: - return; - } - r.bankWithdrawConfirmUrl = status.confirm_transfer_url; - return r; - }); - yield incrementReserveRetry(ws, reservePub, undefined); + if (resp.status !== 200) { + console.log(yield resp.json()); + throw Error(`unexpected status (${resp.status}) for /pay`); } - ws.notify({ type: "wildcard" /* Wildcard */ }); - }); -} -function incrementReserveRetry(ws, reservePub, err) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.reserves], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.reserves, reservePub); - if (!r) { - return; - } - if (!r.retryInfo) { - return; + const merchantResp = yield resp.json(); + console.log("got success from pay URL", merchantResp); + const now = time.getTimestampNow(); + const merchantPub = purchase.contractData.merchantPub; + const valid = yield ws.cryptoApi.isValidPaymentSignature(merchantResp.sig, purchase.contractData.contractTermsHash, merchantPub); + if (!valid) { + console.error("merchant payment signature invalid"); + // FIXME: properly display error + throw Error("merchant payment signature invalid"); + } + const isFirst = purchase.timestampFirstSuccessfulPay === undefined; + purchase.timestampFirstSuccessfulPay = now; + purchase.paymentSubmitPending = false; + purchase.lastPayError = undefined; + purchase.payRetryInfo = dbTypes.initRetryInfo(false); + if (isFirst) { + const ar = purchase.contractData.autoRefund; + if (ar) { + console.log("auto_refund present"); + purchase.refundStatusRequested = true; + purchase.refundStatusRetryInfo = dbTypes.initRetryInfo(); + purchase.lastRefundStatusError = undefined; + purchase.autoRefundDeadline = time.timestampAddDuration(now, ar); } - console.log("updating retry info"); - console.log("before", r.retryInfo); - r.retryInfo.retryCounter++; - dbTypes.updateRetryInfoTimeout(r.retryInfo); - console.log("after", r.retryInfo); - r.lastError = err; - yield tx.put(dbTypes.Stores.reserves, r); - })); - if (err) { - ws.notify({ - type: "reserve-error" /* ReserveOperationError */, - operationError: err, - }); } + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases, dbTypes.Stores.payEvents], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.put(dbTypes.Stores.purchases, purchase); + const payEvent = { + proposalId, + sessionId, + timestamp: now, + isReplay: !isFirst, + }; + yield tx.put(dbTypes.Stores.payEvents, payEvent); + })); + const nextUrl = getNextUrl(purchase.contractData); + ws.cachedNextUrl[purchase.contractData.fulfillmentUrl] = { + nextUrl, + lastSessionId: sessionId, + }; + return { nextUrl }; }); } +exports.submitPay = submitPay; /** - * Update the information about a reserve that is stored in the wallet - * by quering the reserve's exchange. + * Check if a payment for the given taler://pay/ URI is possible. + * + * If the payment is possible, the signature are already generated but not + * yet send to the merchant. */ -function updateReserve(ws, reservePub) { - return __awaiter(this, void 0, void 0, function* () { - const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - if (!reserve) { - throw Error("reserve not in db"); - } - if (reserve.timestampConfirmed === undefined) { - throw Error("reserve not confirmed yet"); +function preparePayForUri(ws, talerPayUri) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const uriResult = taleruri.parsePayUri(talerPayUri); + if (!uriResult) { + return { + status: "error", + error: "URI not supported", + }; } - if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.QUERYING_STATUS) { - return; + let proposalId = yield startDownloadProposal(ws, uriResult.merchantBaseUrl, uriResult.orderId, uriResult.sessionId); + let proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); + if (!proposal) { + throw Error(`could not get proposal ${proposalId}`); } - const reqUrl = new URL("reserve/status", reserve.exchangeBaseUrl); - reqUrl.searchParams.set("reserve_pub", reservePub); - let resp; - try { - resp = yield ws.http.get(reqUrl.href); - console.log("got reserve/status response", yield resp.json()); - if (resp.status === 404) { - const m = "reserve not known to the exchange yet"; - throw new errors.OperationFailedError(m, { - type: "waiting", - message: m, - details: {}, - }); + if (proposal.proposalStatus === "repurchase" /* REPURCHASE */) { + const existingProposalId = proposal.repurchaseProposalId; + if (!existingProposalId) { + throw Error("invalid proposal state"); } - if (resp.status !== 200) { - throw Error(`unexpected status code ${resp.status} for reserve/status`); + console.log("using existing purchase for same product"); + proposal = yield ws.db.get(dbTypes.Stores.proposals, existingProposalId); + if (!proposal) { + throw Error("existing proposal is in wrong state"); } } - catch (e) { - logger.trace("caught exception for reserve/status"); - const m = e.message; - yield incrementReserveRetry(ws, reservePub, { - type: "network", - details: {}, - message: m, - }); - throw new errors.OperationFailedAndReportedError(m); + const d = proposal.download; + if (!d) { + console.error("bad proposal", proposal); + throw Error("proposal is in invalid state"); } - const respJson = yield resp.json(); - const reserveInfo = ReserveStatus.codecForReserveStatus().decode(respJson); - const balance = Amounts.parseOrThrow(reserveInfo.balance); - yield ws.db.runWithWriteTransaction([dbTypes.Stores.reserves, dbTypes.Stores.reserveUpdatedEvents], (tx) => __awaiter(this, void 0, void 0, function* () { - const r = yield tx.get(dbTypes.Stores.reserves, reservePub); - if (!r) { - return; - } - if (r.reserveStatus !== dbTypes.ReserveRecordStatus.QUERYING_STATUS) { - return; - } - const newHistoryTransactions = reserveInfo.history.slice(r.reserveTransactions.length); - const reserveUpdateId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); - // FIXME: check / compare history! - if (!r.lastSuccessfulStatusQuery) { - // FIXME: check if this matches initial expectations - r.amountWithdrawRemaining = balance; - const reserveUpdate = { - reservePub: r.reservePub, - timestamp: time.getTimestampNow(), - amountReserveBalance: Amounts.toString(balance), - amountExpected: Amounts.toString(reserve.amountInitiallyRequested), - newHistoryTransactions, - reserveUpdateId, + const contractData = d.contractData; + const merchantSig = d.contractData.merchantSig; + if (!merchantSig) { + throw Error("BUG: proposal is in invalid state"); + } + proposalId = proposal.proposalId; + // First check if we already payed for it. + const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + if (!purchase) { + // If not already paid, check if we could pay for it. + const res = yield getCoinsForPayment(ws, contractData); + if (!res) { + console.log("not confirming payment, insufficient coins"); + return { + status: "insufficient-balance", + contractTermsRaw: d.contractTermsRaw, + proposalId: proposal.proposalId, }; - yield tx.put(dbTypes.Stores.reserveUpdatedEvents, reserveUpdate); } - else { - const expectedBalance = Amounts.sub(r.amountWithdrawAllocated, r.amountWithdrawCompleted); - const cmp = Amounts.cmp(balance, expectedBalance.amount); - if (cmp == 0) { - // Nothing changed. + const totalCost = yield getTotalPaymentCost(ws, res); + const totalFees = Amounts.sub(totalCost, res.paymentAmount).amount; + return { + status: "payment-possible", + contractTermsRaw: d.contractTermsRaw, + proposalId: proposal.proposalId, + totalFees, + }; + } + if (uriResult.sessionId && purchase.lastSessionId !== uriResult.sessionId) { + console.log("automatically re-submitting payment with different session ID"); + yield ws.db.runWithWriteTransaction([dbTypes.Stores.purchases], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const p = yield tx.get(dbTypes.Stores.purchases, proposalId); + if (!p) { return; } - if (cmp > 0) { - const extra = Amounts.sub(balance, expectedBalance.amount).amount; - r.amountWithdrawRemaining = Amounts.add(r.amountWithdrawRemaining, extra).amount; - } - const reserveUpdate = { - reservePub: r.reservePub, - timestamp: time.getTimestampNow(), - amountReserveBalance: Amounts.toString(balance), - amountExpected: Amounts.toString(expectedBalance.amount), - newHistoryTransactions, - reserveUpdateId, - }; - yield tx.put(dbTypes.Stores.reserveUpdatedEvents, reserveUpdate); - } - r.lastSuccessfulStatusQuery = time.getTimestampNow(); - r.reserveStatus = dbTypes.ReserveRecordStatus.WITHDRAWING; - r.retryInfo = dbTypes.initRetryInfo(); - r.reserveTransactions = reserveInfo.history; - yield tx.put(dbTypes.Stores.reserves, r); - })); - ws.notify({ type: "reserve-updated" /* ReserveUpdated */ }); + p.lastSessionId = uriResult.sessionId; + yield tx.put(dbTypes.Stores.purchases, p); + })); + yield submitPay(ws, proposalId); + } + return { + status: "paid", + contractTermsRaw: purchase.contractTermsRaw, + nextUrl: getNextUrl(purchase.contractData), + }; }); } -function processReserveImpl(ws, reservePub, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { - const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - if (!reserve) { - console.log("not processing reserve: reserve does not exist"); - return; +exports.preparePayForUri = preparePayForUri; +/** + * Add a contract to the wallet and sign coins, and send them. + */ +function confirmPay(ws, proposalId, sessionIdOverride) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + logger.trace(`executing confirmPay with proposalId ${proposalId} and sessionIdOverride ${sessionIdOverride}`); + const proposal = yield ws.db.get(dbTypes.Stores.proposals, proposalId); + if (!proposal) { + throw Error(`proposal with id ${proposalId} not found`); } - if (!forceNow) { - const now = time.getTimestampNow(); - if (reserve.retryInfo.nextRetry.t_ms > now.t_ms) { - logger.trace("processReserve retry not due yet"); - return; + const d = proposal.download; + if (!d) { + throw Error("proposal is in invalid state"); + } + let purchase = yield ws.db.get(dbTypes.Stores.purchases, d.contractData.contractTermsHash); + if (purchase) { + if (sessionIdOverride !== undefined && + sessionIdOverride != purchase.lastSessionId) { + logger.trace(`changing session ID to ${sessionIdOverride}`); + yield ws.db.mutate(dbTypes.Stores.purchases, purchase.proposalId, (x) => { + x.lastSessionId = sessionIdOverride; + x.paymentSubmitPending = true; + return x; + }); } + logger.trace("confirmPay: submitting payment for existing purchase"); + return submitPay(ws, proposalId); } - else { - yield resetReserveRetry(ws, reservePub); + logger.trace("confirmPay: purchase record does not exist yet"); + const res = yield getCoinsForPayment(ws, d.contractData); + logger.trace("coin selection result", res); + if (!res) { + // Should not happen, since checkPay should be called first + console.log("not confirming payment, insufficient coins"); + throw Error("insufficient balance"); } - logger.trace(`Processing reserve ${reservePub} with status ${reserve.reserveStatus}`); - switch (reserve.reserveStatus) { - case dbTypes.ReserveRecordStatus.UNCONFIRMED: - // nothing to do - break; - case dbTypes.ReserveRecordStatus.REGISTERING_BANK: - yield processReserveBankStatus(ws, reservePub); - return yield processReserveImpl(ws, reservePub, true); - case dbTypes.ReserveRecordStatus.QUERYING_STATUS: - yield updateReserve(ws, reservePub); - return yield processReserveImpl(ws, reservePub, true); - case dbTypes.ReserveRecordStatus.WITHDRAWING: - yield depleteReserve(ws, reservePub); - break; - case dbTypes.ReserveRecordStatus.DORMANT: - // nothing to do - break; - case dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK: - yield processReserveBankStatus(ws, reservePub); - break; - default: - console.warn("unknown reserve record status:", reserve.reserveStatus); - assertUnreachable_1.assertUnreachable(reserve.reserveStatus); - break; + const depositPermissions = []; + for (let i = 0; i < res.coinPubs.length; i++) { + const coin = yield ws.db.get(dbTypes.Stores.coins, res.coinPubs[i]); + if (!coin) { + throw Error("can't pay, allocated coin not found anymore"); + } + const denom = yield ws.db.get(dbTypes.Stores.denominations, [ + coin.exchangeBaseUrl, + coin.denomPub, + ]); + if (!denom) { + throw Error("can't pay, denomination of allocated coin not found anymore"); + } + const dp = yield ws.cryptoApi.signDepositPermission({ + coinPriv: coin.coinPriv, + coinPub: coin.coinPub, + contractTermsHash: d.contractData.contractTermsHash, + denomPub: coin.denomPub, + denomSig: coin.denomSig, + exchangeBaseUrl: coin.exchangeBaseUrl, + feeDeposit: denom.feeDeposit, + merchantPub: d.contractData.merchantPub, + refundDeadline: d.contractData.refundDeadline, + spendAmount: res.coinContributions[i], + timestamp: d.contractData.timestamp, + wireInfoHash: d.contractData.wireInfoHash, + }); + depositPermissions.push(dp); } + purchase = yield recordConfirmPay(ws, proposal, res, depositPermissions, sessionIdOverride); + logger.trace("confirmPay: submitting payment after creating purchase record"); + logger.trace("purchaseRecord:", purchase); + return submitPay(ws, proposalId); }); } -function confirmReserve(ws, req) { - return __awaiter(this, void 0, void 0, function* () { - const now = time.getTimestampNow(); - yield ws.db.mutate(dbTypes.Stores.reserves, req.reservePub, reserve => { - if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.UNCONFIRMED) { - return; +exports.confirmPay = confirmPay; +function processPurchasePay(ws, proposalId, forceNow = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const onOpErr = (e) => incrementPurchasePayRetry(ws, proposalId, e); + yield errors.guardOperationException(() => processPurchasePayImpl(ws, proposalId, forceNow), onOpErr); + }); +} +exports.processPurchasePay = processPurchasePay; +function resetPurchasePayRetry(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.purchases, proposalId, (x) => { + if (x.payRetryInfo.active) { + x.payRetryInfo = dbTypes.initRetryInfo(); } - reserve.timestampConfirmed = now; - reserve.reserveStatus = dbTypes.ReserveRecordStatus.QUERYING_STATUS; - reserve.retryInfo = dbTypes.initRetryInfo(); - return reserve; - }); - ws.notify({ type: "reserve-updated" /* ReserveUpdated */ }); - processReserve(ws, req.reservePub, true).catch(e => { - console.log("processing reserve failed:", e); + return x; }); }); } -exports.confirmReserve = confirmReserve; -/** - * Withdraw coins from a reserve until it is empty. - * - * When finished, marks the reserve as depleted by setting - * the depleted timestamp. - */ -function depleteReserve(ws, reservePub) { - return __awaiter(this, void 0, void 0, function* () { - const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - if (!reserve) { - return; +function processPurchasePayImpl(ws, proposalId, forceNow) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (forceNow) { + yield resetPurchasePayRetry(ws, proposalId); } - if (reserve.reserveStatus !== dbTypes.ReserveRecordStatus.WITHDRAWING) { + const purchase = yield ws.db.get(dbTypes.Stores.purchases, proposalId); + if (!purchase) { return; } - logger.trace(`depleting reserve ${reservePub}`); - const withdrawAmount = reserve.amountWithdrawRemaining; - logger.trace(`getting denom list`); - const denomsForWithdraw = yield withdraw.getVerifiedWithdrawDenomList(ws, reserve.exchangeBaseUrl, withdrawAmount); - logger.trace(`got denom list`); - if (denomsForWithdraw.length === 0) { - const m = `Unable to withdraw from reserve, no denominations are available to withdraw.`; - yield incrementReserveRetry(ws, reserve.reservePub, { - type: "internal", - message: m, - details: {}, - }); - console.log(m); - throw new errors.OperationFailedAndReportedError(m); - } - logger.trace("selected denominations"); - const withdrawalSessionId = talerCrypto.encodeCrock(naclFast.randomBytes(32)); - const totalCoinValue = Amounts.sum(denomsForWithdraw.map(x => x.value)) - .amount; - const withdrawalRecord = { - withdrawSessionId: withdrawalSessionId, - exchangeBaseUrl: reserve.exchangeBaseUrl, - source: { - type: "reserve", - reservePub: reserve.reservePub, - }, - rawWithdrawalAmount: withdrawAmount, - timestampStart: time.getTimestampNow(), - denoms: denomsForWithdraw.map(x => x.denomPub), - withdrawn: denomsForWithdraw.map(x => false), - planchets: denomsForWithdraw.map(x => undefined), - totalCoinValue, - retryInfo: dbTypes.initRetryInfo(), - lastErrorPerCoin: {}, - lastError: undefined, - }; - const totalCoinWithdrawFee = Amounts.sum(denomsForWithdraw.map(x => x.feeWithdraw)).amount; - const totalWithdrawAmount = Amounts.add(totalCoinValue, totalCoinWithdrawFee) - .amount; - function mutateReserve(r) { - const remaining = Amounts.sub(r.amountWithdrawRemaining, totalWithdrawAmount); - if (remaining.saturated) { - console.error("can't create planchets, saturated"); - throw query.TransactionAbort; - } - const allocated = Amounts.add(r.amountWithdrawAllocated, totalWithdrawAmount); - if (allocated.saturated) { - console.error("can't create planchets, saturated"); - throw query.TransactionAbort; - } - r.amountWithdrawRemaining = remaining.amount; - r.amountWithdrawAllocated = allocated.amount; - r.reserveStatus = dbTypes.ReserveRecordStatus.DORMANT; - r.retryInfo = dbTypes.initRetryInfo(false); - return r; + if (!purchase.paymentSubmitPending) { + return; } - const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.withdrawalSession, dbTypes.Stores.reserves], (tx) => __awaiter(this, void 0, void 0, function* () { - const myReserve = yield tx.get(dbTypes.Stores.reserves, reservePub); - if (!myReserve) { + logger.trace(`processing purchase pay ${proposalId}`); + yield submitPay(ws, proposalId); + }); +} +function refuseProposal(ws, proposalId) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const success = yield ws.db.runWithWriteTransaction([dbTypes.Stores.proposals], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const proposal = yield tx.get(dbTypes.Stores.proposals, proposalId); + if (!proposal) { + logger.trace(`proposal ${proposalId} not found, won't refuse proposal`); return false; } - if (myReserve.reserveStatus !== dbTypes.ReserveRecordStatus.WITHDRAWING) { + if (proposal.proposalStatus !== "proposed" /* PROPOSED */) { return false; } - yield tx.mutate(dbTypes.Stores.reserves, reserve.reservePub, mutateReserve); - yield tx.put(dbTypes.Stores.withdrawalSession, withdrawalRecord); + proposal.proposalStatus = "refused" /* REFUSED */; + yield tx.put(dbTypes.Stores.proposals, proposal); return true; })); if (success) { - console.log("processing new withdraw session"); ws.notify({ - type: "withdraw-session-created" /* WithdrawSessionCreated */, - withdrawSessionId: withdrawalSessionId, + type: "wildcard" /* Wildcard */, }); - yield withdraw.processWithdrawSession(ws, withdrawalSessionId); } - else { - console.trace("withdraw session already existed"); - } - }); -} -function createTalerWithdrawReserve(ws, talerWithdrawUri, selectedExchange) { - return __awaiter(this, void 0, void 0, function* () { - const withdrawInfo = yield withdraw.getBankWithdrawalInfo(ws, talerWithdrawUri); - const exchangeWire = yield exchanges.getExchangePaytoUri(ws, selectedExchange, withdrawInfo.wireTypes); - const reserve = yield createReserve(ws, { - amount: withdrawInfo.amount, - bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl, - exchange: selectedExchange, - senderWire: withdrawInfo.senderWire, - exchangeWire: exchangeWire, - }); - // We do this here, as the reserve should be registered before we return, - // so that we can redirect the user to the bank's status page. - yield processReserveBankStatus(ws, reserve.reservePub); - console.log("acceptWithdrawal: returning"); - return { - reservePub: reserve.reservePub, - confirmTransferUrl: withdrawInfo.confirmTransferUrl, - }; }); } -exports.createTalerWithdrawReserve = createTalerWithdrawReserve; +exports.refuseProposal = refuseProposal; }); -unwrapExports(reserves); -var reserves_1 = reserves.createReserve; -var reserves_2 = reserves.processReserve; -var reserves_3 = reserves.processReserveBankStatus; -var reserves_4 = reserves.confirmReserve; -var reserves_5 = reserves.createTalerWithdrawReserve; +unwrapExports(pay); +var pay_1 = pay.getTotalPaymentCost; +var pay_2 = pay.selectPayCoins; +var pay_3 = pay.abortFailedPayment; +var pay_4 = pay.processDownloadProposal; +var pay_5 = pay.submitPay; +var pay_6 = pay.preparePayForUri; +var pay_7 = pay.confirmPay; +var pay_8 = pay.processPurchasePay; +var pay_9 = pay.refuseProposal; var timer = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2017-2019 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ Object.defineProperty(exports, "__esModule", { value: true }); class IntervalHandle { constructor(h) { @@ -11140,9 +11149,12 @@ exports.performanceNow = (() => { return t[0] * 1e9 + t[1]; }; } - else { + else if (typeof performance !== "undefined") { return () => performance.now(); } + else { + return () => 0; + } })(); /** * Call a function every time the delay given in milliseconds passes. @@ -11251,15 +11263,9 @@ var cryptoApi = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const timer$1 = __importStar(timer); + +const timer$1 = tslib_1.__importStar(timer); /** * Number of different priorities. Each priority p * must be 0 <= p < NUM_PRIO. @@ -11268,7 +11274,7 @@ const NUM_PRIO = 5; class BrowserCryptoWorkerFactory { startWorker() { const workerCtor = Worker; - const workerPath = "/dist/cryptoWorker-bundle.js"; + const workerPath = "/dist/webextension/browserWorkerEntry.js"; return new workerCtor(workerPath); } getConcurrency() { @@ -11319,7 +11325,7 @@ class CryptoApi { * Terminate all worker threads. */ terminateWorkers() { - for (let worker of this.workers) { + for (const worker of this.workers) { if (worker.w) { CryptoApi.enableTracing && console.log("terminating worker"); worker.w.terminate(); @@ -11354,11 +11360,15 @@ class CryptoApi { } ws.currentWorkItem = work; this.numBusy++; + let worker; if (!ws.w) { - const w = this.workerFactory.startWorker(); - w.onmessage = (m) => this.handleWorkerMessage(ws, m); - w.onerror = (e) => this.handleWorkerError(ws, e); - ws.w = w; + worker = this.workerFactory.startWorker(); + worker.onmessage = (m) => this.handleWorkerMessage(ws, m); + worker.onerror = (e) => this.handleWorkerError(ws, e); + ws.w = worker; + } + else { + worker = ws.w; } const msg = { args: work.args, @@ -11367,7 +11377,7 @@ class CryptoApi { }; this.resetWorkerTimeout(ws); work.startTime = timer$1.performanceNow(); - setImmediate(() => ws.w.postMessage(msg)); + setTimeout(() => worker.postMessage(msg), 0); } resetWorkerTimeout(ws) { if (ws.terminationTimerHandle !== null) { @@ -11392,8 +11402,10 @@ class CryptoApi { } console.error(e.message); try { - ws.w.terminate(); - ws.w = null; + if (ws.w) { + ws.w.terminate(); + ws.w = null; + } } catch (e) { console.error(e); @@ -11411,6 +11423,9 @@ class CryptoApi { const q = this.workQueues[NUM_PRIO - i - 1]; if (q.length !== 0) { const work = q.shift(); + if (!work) { + continue; + } this.wake(ws, work); return; } @@ -11435,8 +11450,7 @@ class CryptoApi { return; } CryptoApi.enableTracing && - console.log(`rpc ${currentWorkItem.operation} took ${timer$1.performanceNow() - - currentWorkItem.startTime}ms`); + console.log(`rpc ${currentWorkItem.operation} took ${timer$1.performanceNow() - currentWorkItem.startTime}ms`); currentWorkItem.resolve(msg.data.result); } doRpc(operation, priority, ...args) { @@ -11478,8 +11492,8 @@ class CryptoApi { hashString(str) { return this.doRpc("hashString", 1, str); } - hashDenomPub(denomPub) { - return this.doRpc("hashDenomPub", 1, denomPub); + hashEncoded(encodedBytes) { + return this.doRpc("hashEncoded", 1, encodedBytes); } isValidDenom(denom, masterPub) { return this.doRpc("isValidDenom", 2, denom, masterPub); @@ -11505,8 +11519,8 @@ class CryptoApi { isValidWireAccount(paytoUri, sig, masterPub) { return this.doRpc("isValidWireAccount", 4, paytoUri, sig, masterPub); } - createPaybackRequest(coin) { - return this.doRpc("createPaybackRequest", 1, coin); + createRecoupRequest(coin) { + return this.doRpc("createRecoupRequest", 1, coin); } createRefreshSession(exchangeBaseUrl, kappa, meltCoin, newCoinDenoms, meltFee) { return this.doRpc("createRefreshSession", 4, exchangeBaseUrl, kappa, meltCoin, newCoinDenoms, meltFee); @@ -11647,6 +11661,7 @@ class InternalWalletState { this.memoGetPending = new asyncMemo.AsyncOpMemoSingle(); this.memoGetBalance = new asyncMemo.AsyncOpMemoSingle(); this.memoProcessRefresh = new asyncMemo.AsyncOpMemoMap(); + this.memoProcessRecoup = new asyncMemo.AsyncOpMemoMap(); this.listeners = []; this.cryptoApi = new cryptoApi.CryptoApi(cryptoWorkerFactory); } @@ -11654,9 +11669,9 @@ class InternalWalletState { logger.trace("Notification", n); for (const l of this.listeners) { const nc = JSON.parse(JSON.stringify(n)); - setImmediate(() => { + setTimeout(() => { l(nc); - }); + }, 0); } } addNotificationListener(f) { @@ -11686,32 +11701,18 @@ var history = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); + + + /** * Create an event ID from the type and the primary key for the event. */ function makeEventId(type, ...args) { - return type + ";" + args.map(x => encodeURIComponent(x)).join(";"); + return type + ";" + args.map((x) => encodeURIComponent(x)).join(";"); } function getOrderShortInfo(proposal) { const download = proposal.download; @@ -11719,7 +11720,7 @@ function getOrderShortInfo(proposal) { return undefined; } return { - amount: Amounts.toString(download.contractData.amount), + amount: amounts.Amounts.stringify(download.contractData.amount), fulfillmentUrl: download.contractData.fulfillmentUrl, orderId: download.contractData.orderId, merchantBaseUrl: download.contractData.merchantBaseUrl, @@ -11728,8 +11729,8 @@ function getOrderShortInfo(proposal) { }; } function collectProposalHistory(tx, history, historyQuery) { - return __awaiter(this, void 0, void 0, function* () { - tx.iter(dbTypes.Stores.proposals).forEachAsync((proposal) => __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + tx.iter(dbTypes.Stores.proposals).forEachAsync((proposal) => tslib_1.__awaiter(this, void 0, void 0, function* () { const status = proposal.proposalStatus; switch (status) { case "accepted" /* ACCEPTED */: @@ -11797,7 +11798,7 @@ function collectProposalHistory(tx, history, historyQuery) { * Retrive the full event history for this wallet. */ function getHistory(ws, historyQuery) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const history = []; // FIXME: do pagination instead of generating the full history // We uniquely identify history rows via their timestamp. @@ -11814,12 +11815,13 @@ function getHistory(ws, historyQuery) { dbTypes.Stores.refreshGroups, dbTypes.Stores.reserves, dbTypes.Stores.tips, - dbTypes.Stores.withdrawalSession, + dbTypes.Stores.withdrawalGroups, dbTypes.Stores.payEvents, dbTypes.Stores.refundEvents, dbTypes.Stores.reserveUpdatedEvents, - ], (tx) => __awaiter(this, void 0, void 0, function* () { - tx.iter(dbTypes.Stores.exchanges).forEach(exchange => { + dbTypes.Stores.recoupGroups, + ], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + tx.iter(dbTypes.Stores.exchanges).forEach((exchange) => { history.push({ type: "exchange-added" /* ExchangeAdded */, builtIn: false, @@ -11828,7 +11830,7 @@ function getHistory(ws, historyQuery) { timestamp: exchange.timestampAdded, }); }); - tx.iter(dbTypes.Stores.exchangeUpdatedEvents).forEach(eu => { + tx.iter(dbTypes.Stores.exchangeUpdatedEvents).forEach((eu) => { history.push({ type: "exchange-updated" /* ExchangeUpdated */, eventId: makeEventId("exchange-updated" /* ExchangeUpdated */, eu.exchangeBaseUrl), @@ -11836,8 +11838,7 @@ function getHistory(ws, historyQuery) { timestamp: eu.timestamp, }); }); - tx.iter(dbTypes.Stores.withdrawalSession).forEach(wsr => { - var _a; + tx.iter(dbTypes.Stores.withdrawalGroups).forEach((wsr) => { if (wsr.timestampFinish) { const cs = []; wsr.planchets.forEach((x) => { @@ -11846,20 +11847,20 @@ function getHistory(ws, historyQuery) { } }); let verboseDetails = undefined; - if ((_a = historyQuery) === null || _a === void 0 ? void 0 : _a.verboseDetails) { + if (historyQuery === null || historyQuery === void 0 ? void 0 : historyQuery.extraDebug) { verboseDetails = { coins: cs.map((x) => ({ - value: Amounts.toString(x.coinValue), + value: amounts.Amounts.stringify(x.coinValue), denomPub: x.denomPub, })), }; } history.push({ type: "withdrawn" /* Withdrawn */, - withdrawSessionId: wsr.withdrawSessionId, - eventId: makeEventId("withdrawn" /* Withdrawn */, wsr.withdrawSessionId), - amountWithdrawnEffective: Amounts.toString(wsr.totalCoinValue), - amountWithdrawnRaw: Amounts.toString(wsr.rawWithdrawalAmount), + withdrawalGroupId: wsr.withdrawalGroupId, + eventId: makeEventId("withdrawn" /* Withdrawn */, wsr.withdrawalGroupId), + amountWithdrawnEffective: amounts.Amounts.stringify(wsr.totalCoinValue), + amountWithdrawnRaw: amounts.Amounts.stringify(wsr.rawWithdrawalAmount), exchangeBaseUrl: wsr.exchangeBaseUrl, timestamp: wsr.timestampFinish, withdrawalSource: wsr.source, @@ -11868,8 +11869,7 @@ function getHistory(ws, historyQuery) { } }); yield collectProposalHistory(tx, history); - yield tx.iter(dbTypes.Stores.payEvents).forEachAsync((pe) => __awaiter(this, void 0, void 0, function* () { - var _a; + yield tx.iter(dbTypes.Stores.payEvents).forEachAsync((pe) => tslib_1.__awaiter(this, void 0, void 0, function* () { const proposal = yield tx.get(dbTypes.Stores.proposals, pe.proposalId); if (!proposal) { return; @@ -11883,7 +11883,7 @@ function getHistory(ws, historyQuery) { return; } let verboseDetails = undefined; - if ((_a = historyQuery) === null || _a === void 0 ? void 0 : _a.verboseDetails) { + if (historyQuery === null || historyQuery === void 0 ? void 0 : historyQuery.extraDebug) { const coins = []; for (const x of purchase.payReq.coins) { const c = yield tx.get(dbTypes.Stores.coins, x.coin_pub); @@ -11891,7 +11891,10 @@ function getHistory(ws, historyQuery) { // FIXME: what to do here?? continue; } - const d = yield tx.get(dbTypes.Stores.denominations, [c.exchangeBaseUrl, c.denomPub]); + const d = yield tx.get(dbTypes.Stores.denominations, [ + c.exchangeBaseUrl, + c.denomPub, + ]); if (!d) { // FIXME: what to do here?? continue; @@ -11899,12 +11902,12 @@ function getHistory(ws, historyQuery) { coins.push({ contribution: x.contribution, denomPub: c.denomPub, - value: Amounts.toString(d.value), + value: amounts.Amounts.stringify(d.value), }); } verboseDetails = { coins }; } - const amountPaidWithFees = Amounts.sum(purchase.payReq.coins.map(x => Amounts.parseOrThrow(x.contribution))).amount; + const amountPaidWithFees = amounts.Amounts.sum(purchase.payReq.coins.map((x) => amounts.Amounts.parseOrThrow(x.contribution))).amount; history.push({ type: "payment-sent" /* PaymentSent */, eventId: makeEventId("payment-sent" /* PaymentSent */, pe.proposalId), @@ -11913,12 +11916,11 @@ function getHistory(ws, historyQuery) { sessionId: pe.sessionId, timestamp: pe.timestamp, numCoins: purchase.payReq.coins.length, - amountPaidWithFees: Amounts.toString(amountPaidWithFees), + amountPaidWithFees: amounts.Amounts.stringify(amountPaidWithFees), verboseDetails, }); })); - yield tx.iter(dbTypes.Stores.refreshGroups).forEachAsync((rg) => __awaiter(this, void 0, void 0, function* () { - var _b; + yield tx.iter(dbTypes.Stores.refreshGroups).forEachAsync((rg) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (!rg.timestampFinished) { return; } @@ -11945,16 +11947,16 @@ function getHistory(ws, historyQuery) { amountsRaw.push(c.currentAmount); } } - let amountRefreshedRaw = Amounts.sum(amountsRaw).amount; + const amountRefreshedRaw = amounts.Amounts.sum(amountsRaw).amount; let amountRefreshedEffective; if (amountsEffective.length == 0) { - amountRefreshedEffective = Amounts.getZero(amountRefreshedRaw.currency); + amountRefreshedEffective = amounts.Amounts.getZero(amountRefreshedRaw.currency); } else { - amountRefreshedEffective = Amounts.sum(amountsEffective).amount; + amountRefreshedEffective = amounts.Amounts.sum(amountsEffective).amount; } let verboseDetails = undefined; - if ((_b = historyQuery) === null || _b === void 0 ? void 0 : _b.verboseDetails) { + if (historyQuery === null || historyQuery === void 0 ? void 0 : historyQuery.extraDebug) { const outputCoins = []; for (const rs of rg.refreshSessionPerCoin) { if (!rs) { @@ -11964,13 +11966,16 @@ function getHistory(ws, historyQuery) { if (!nd) { continue; } - const d = yield tx.get(dbTypes.Stores.denominations, [rs.exchangeBaseUrl, nd]); + const d = yield tx.get(dbTypes.Stores.denominations, [ + rs.exchangeBaseUrl, + nd, + ]); if (!d) { continue; } outputCoins.push({ denomPub: d.denomPub, - value: Amounts.toString(d.value), + value: amounts.Amounts.stringify(d.value), }); } } @@ -11984,15 +11989,15 @@ function getHistory(ws, historyQuery) { eventId: makeEventId("refreshed" /* Refreshed */, rg.refreshGroupId), timestamp: rg.timestampFinished, refreshReason: rg.reason, - amountRefreshedEffective: Amounts.toString(amountRefreshedEffective), - amountRefreshedRaw: Amounts.toString(amountRefreshedRaw), + amountRefreshedEffective: amounts.Amounts.stringify(amountRefreshedEffective), + amountRefreshedRaw: amounts.Amounts.stringify(amountRefreshedRaw), numInputCoins, numOutputCoins, numRefreshedInputCoins, verboseDetails, }); })); - tx.iter(dbTypes.Stores.reserveUpdatedEvents).forEachAsync((ru) => __awaiter(this, void 0, void 0, function* () { + tx.iter(dbTypes.Stores.reserveUpdatedEvents).forEachAsync((ru) => tslib_1.__awaiter(this, void 0, void 0, function* () { const reserve = yield tx.get(dbTypes.Stores.reserves, ru.reservePub); if (!reserve) { return; @@ -12009,32 +12014,33 @@ function getHistory(ws, historyQuery) { type: "manual" /* Manual */, }; } + const s = reserveHistoryUtil.summarizeReserveHistory(reserve.reserveTransactions, reserve.currency); history.push({ type: "reserve-balance-updated" /* ReserveBalanceUpdated */, eventId: makeEventId("reserve-balance-updated" /* ReserveBalanceUpdated */, ru.reserveUpdateId), - amountExpected: ru.amountExpected, - amountReserveBalance: ru.amountReserveBalance, timestamp: ru.timestamp, - newHistoryTransactions: ru.newHistoryTransactions, reserveShortInfo: { exchangeBaseUrl: reserve.exchangeBaseUrl, reserveCreationDetail, reservePub: reserve.reservePub, }, + reserveAwaitedAmount: amounts.Amounts.stringify(s.awaitedReserveAmount), + reserveBalance: amounts.Amounts.stringify(s.computedReserveBalance), + reserveUnclaimedAmount: amounts.Amounts.stringify(s.unclaimedReserveAmount), }); })); - tx.iter(dbTypes.Stores.tips).forEach(tip => { + tx.iter(dbTypes.Stores.tips).forEach((tip) => { if (tip.acceptedTimestamp) { history.push({ type: "tip-accepted" /* TipAccepted */, eventId: makeEventId("tip-accepted" /* TipAccepted */, tip.tipId), timestamp: tip.acceptedTimestamp, tipId: tip.tipId, - tipAmountRaw: Amounts.toString(tip.amount), + tipAmountRaw: amounts.Amounts.stringify(tip.amount), }); } }); - tx.iter(dbTypes.Stores.refundEvents).forEachAsync((re) => __awaiter(this, void 0, void 0, function* () { + tx.iter(dbTypes.Stores.refundEvents).forEachAsync((re) => tslib_1.__awaiter(this, void 0, void 0, function* () { const proposal = yield tx.get(dbTypes.Stores.proposals, re.proposalId); if (!proposal) { return; @@ -12048,54 +12054,199 @@ function getHistory(ws, historyQuery) { return; } const purchaseAmount = purchase.contractData.amount; - let amountRefundedRaw = Amounts.getZero(purchaseAmount.currency); - let amountRefundedInvalid = Amounts.getZero(purchaseAmount.currency); - let amountRefundedEffective = Amounts.getZero(purchaseAmount.currency); + let amountRefundedRaw = amounts.Amounts.getZero(purchaseAmount.currency); + let amountRefundedInvalid = amounts.Amounts.getZero(purchaseAmount.currency); + let amountRefundedEffective = amounts.Amounts.getZero(purchaseAmount.currency); Object.keys(purchase.refundState.refundsDone).forEach((x, i) => { const r = purchase.refundState.refundsDone[x]; if (r.refundGroupId !== re.refundGroupId) { return; } - const refundAmount = Amounts.parseOrThrow(r.perm.refund_amount); - const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); - amountRefundedRaw = Amounts.add(amountRefundedRaw, refundAmount) + const refundAmount = amounts.Amounts.parseOrThrow(r.perm.refund_amount); + const refundFee = amounts.Amounts.parseOrThrow(r.perm.refund_fee); + amountRefundedRaw = amounts.Amounts.add(amountRefundedRaw, refundAmount) .amount; - amountRefundedEffective = Amounts.add(amountRefundedEffective, refundAmount).amount; - amountRefundedEffective = Amounts.sub(amountRefundedEffective, refundFee).amount; + amountRefundedEffective = amounts.Amounts.add(amountRefundedEffective, refundAmount).amount; + amountRefundedEffective = amounts.Amounts.sub(amountRefundedEffective, refundFee).amount; }); Object.keys(purchase.refundState.refundsFailed).forEach((x, i) => { const r = purchase.refundState.refundsFailed[x]; if (r.refundGroupId !== re.refundGroupId) { return; } - const ra = Amounts.parseOrThrow(r.perm.refund_amount); - const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); - amountRefundedRaw = Amounts.add(amountRefundedRaw, ra).amount; - amountRefundedInvalid = Amounts.add(amountRefundedInvalid, ra).amount; - amountRefundedEffective = Amounts.sub(amountRefundedEffective, refundFee).amount; - }); - history.push({ - type: "refund" /* Refund */, - eventId: makeEventId("refund" /* Refund */, re.refundGroupId), - refundGroupId: re.refundGroupId, - orderShortInfo, - timestamp: re.timestamp, - amountRefundedEffective: Amounts.toString(amountRefundedEffective), - amountRefundedRaw: Amounts.toString(amountRefundedRaw), - amountRefundedInvalid: Amounts.toString(amountRefundedInvalid), - }); - })); + const ra = amounts.Amounts.parseOrThrow(r.perm.refund_amount); + const refundFee = amounts.Amounts.parseOrThrow(r.perm.refund_fee); + amountRefundedRaw = amounts.Amounts.add(amountRefundedRaw, ra).amount; + amountRefundedInvalid = amounts.Amounts.add(amountRefundedInvalid, ra).amount; + amountRefundedEffective = amounts.Amounts.sub(amountRefundedEffective, refundFee).amount; + }); + history.push({ + type: "refund" /* Refund */, + eventId: makeEventId("refund" /* Refund */, re.refundGroupId), + refundGroupId: re.refundGroupId, + orderShortInfo, + timestamp: re.timestamp, + amountRefundedEffective: amounts.Amounts.stringify(amountRefundedEffective), + amountRefundedRaw: amounts.Amounts.stringify(amountRefundedRaw), + amountRefundedInvalid: amounts.Amounts.stringify(amountRefundedInvalid), + }); + })); + tx.iter(dbTypes.Stores.recoupGroups).forEach((rg) => { + if (rg.timestampFinished) { + let verboseDetails = undefined; + if (historyQuery === null || historyQuery === void 0 ? void 0 : historyQuery.extraDebug) { + verboseDetails = { + oldAmountPerCoin: rg.oldAmountPerCoin.map(amounts.Amounts.stringify), + }; + } + history.push({ + type: "funds-recouped" /* FundsRecouped */, + timestamp: rg.timestampFinished, + eventId: makeEventId("funds-recouped" /* FundsRecouped */, rg.recoupGroupId), + numCoinsRecouped: rg.coinPubs.length, + verboseDetails, + }); + } + }); + })); + history.sort((h1, h2) => time.timestampCmp(h1.timestamp, h2.timestamp)); + return { history }; + }); +} +exports.getHistory = getHistory; + +}); + +unwrapExports(history); +var history_1 = history.getHistory; + +var balance = createCommonjsModule(function (module, exports) { +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +Object.defineProperty(exports, "__esModule", { value: true }); + + +const Amounts = tslib_1.__importStar(amounts); + +const logger = new logging.Logger("withdraw.ts"); +/** + * Get balance information. + */ +function getBalancesInsideTransaction(ws, tx) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + /** + * Add amount to a balance field, both for + * the slicing by exchange and currency. + */ + function addTo(balance, field, amount, exchange) { + const z = Amounts.getZero(amount.currency); + const balanceIdentity = { + available: z, + paybackAmount: z, + pendingIncoming: z, + pendingPayment: z, + pendingIncomingDirty: z, + pendingIncomingRefresh: z, + pendingIncomingWithdraw: z, + }; + let entryCurr = balance.byCurrency[amount.currency]; + if (!entryCurr) { + balance.byCurrency[amount.currency] = entryCurr = Object.assign({}, balanceIdentity); + } + let entryEx = balance.byExchange[exchange]; + if (!entryEx) { + balance.byExchange[exchange] = entryEx = Object.assign({}, balanceIdentity); + } + entryCurr[field] = Amounts.add(entryCurr[field], amount).amount; + entryEx[field] = Amounts.add(entryEx[field], amount).amount; + } + const balanceStore = { + byCurrency: {}, + byExchange: {}, + }; + yield tx.iter(dbTypes.Stores.coins).forEach((c) => { + if (c.suspended) { + return; + } + if (c.status === "fresh" /* Fresh */) { + addTo(balanceStore, "available", c.currentAmount, c.exchangeBaseUrl); + } + }); + yield tx.iter(dbTypes.Stores.refreshGroups).forEach((r) => { + // Don't count finished refreshes, since the refresh already resulted + // in coins being added to the wallet. + if (r.timestampFinished) { + return; + } + for (let i = 0; i < r.oldCoinPubs.length; i++) { + const session = r.refreshSessionPerCoin[i]; + if (session) { + addTo(balanceStore, "pendingIncoming", session.amountRefreshOutput, session.exchangeBaseUrl); + addTo(balanceStore, "pendingIncomingRefresh", session.amountRefreshOutput, session.exchangeBaseUrl); + } + } + }); + yield tx.iter(dbTypes.Stores.withdrawalGroups).forEach((wds) => { + let w = wds.totalCoinValue; + for (let i = 0; i < wds.planchets.length; i++) { + if (wds.withdrawn[i]) { + const p = wds.planchets[i]; + if (p) { + w = Amounts.sub(w, p.coinValue).amount; + } + } + } + addTo(balanceStore, "pendingIncoming", w, wds.exchangeBaseUrl); + }); + yield tx.iter(dbTypes.Stores.purchases).forEach((t) => { + if (t.timestampFirstSuccessfulPay) { + return; + } + for (const c of t.payReq.coins) { + addTo(balanceStore, "pendingPayment", Amounts.parseOrThrow(c.contribution), c.exchange_url); + } + }); + return balanceStore; + }); +} +exports.getBalancesInsideTransaction = getBalancesInsideTransaction; +/** + * Get detailed balance information, sliced by exchange and by currency. + */ +function getBalances(ws) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + logger.trace("starting to compute balance"); + return yield ws.db.runWithReadTransaction([ + dbTypes.Stores.coins, + dbTypes.Stores.refreshGroups, + dbTypes.Stores.reserves, + dbTypes.Stores.purchases, + dbTypes.Stores.withdrawalGroups, + ], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + return getBalancesInsideTransaction(ws, tx); })); - history.sort((h1, h2) => time.timestampCmp(h1.timestamp, h2.timestamp)); - return { history }; }); } -exports.getHistory = getHistory; +exports.getBalances = getBalances; }); -unwrapExports(history); -var history_1 = history.getHistory; +unwrapExports(balance); +var balance_1 = balance.getBalancesInsideTransaction; +var balance_2 = balance.getBalances; var pending = createCommonjsModule(function (module, exports) { /* @@ -12113,33 +12264,26 @@ var pending = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * Imports. */ + function updateRetryDelay(oldDelay, now, retryTimestamp) { const remaining = time.getDurationRemaining(retryTimestamp, now); const nextDelay = time.durationMin(oldDelay, remaining); return nextDelay; } function gatherExchangePending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (onlyDue) { // FIXME: exchanges should also be updated regularly return; } - yield tx.iter(dbTypes.Stores.exchanges).forEach(e => { + yield tx.iter(dbTypes.Stores.exchanges).forEach((e) => { switch (e.updateStatus) { case "finished" /* Finished */: if (e.lastError) { @@ -12177,7 +12321,7 @@ function gatherExchangePending(tx, now, resp, onlyDue = false) { resp.pendingOperations.push({ type: "exchange-update" /* ExchangeUpdate */, givesLifeness: false, - stage: "fetch-keys", + stage: "fetch-keys" /* FetchKeys */, exchangeBaseUrl: e.baseUrl, lastError: e.lastError, reason: e.updateReason || "unknown", @@ -12187,7 +12331,7 @@ function gatherExchangePending(tx, now, resp, onlyDue = false) { resp.pendingOperations.push({ type: "exchange-update" /* ExchangeUpdate */, givesLifeness: false, - stage: "fetch-wire", + stage: "fetch-wire" /* FetchWire */, exchangeBaseUrl: e.baseUrl, lastError: e.lastError, reason: e.updateReason || "unknown", @@ -12197,7 +12341,7 @@ function gatherExchangePending(tx, now, resp, onlyDue = false) { resp.pendingOperations.push({ type: "exchange-update" /* ExchangeUpdate */, givesLifeness: false, - stage: "finalize-update", + stage: "finalize-update" /* FinalizeUpdate */, exchangeBaseUrl: e.baseUrl, lastError: e.lastError, reason: e.updateReason || "unknown", @@ -12219,10 +12363,12 @@ function gatherExchangePending(tx, now, resp, onlyDue = false) { }); } function gatherReservePending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { // FIXME: this should be optimized by using an index for "onlyDue==true". - yield tx.iter(dbTypes.Stores.reserves).forEach(reserve => { - const reserveType = reserve.bankWithdrawStatusUrl ? "taler-bank" : "manual"; + yield tx.iter(dbTypes.Stores.reserves).forEach((reserve) => { + const reserveType = reserve.bankWithdrawStatusUrl + ? "taler-bank-withdraw" /* TalerBankWithdraw */ + : "manual" /* Manual */; if (!reserve.retryInfo.active) { return; } @@ -12278,8 +12424,8 @@ function gatherReservePending(tx, now, resp, onlyDue = false) { }); } function gatherRefreshPending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.refreshGroups).forEach(r => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.refreshGroups).forEach((r) => { if (r.timestampFinished) { return; } @@ -12298,8 +12444,8 @@ function gatherRefreshPending(tx, now, resp, onlyDue = false) { }); } function gatherWithdrawalPending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.withdrawalSession).forEach(wsr => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.withdrawalGroups).forEach((wsr) => { if (wsr.timestampFinish) { return; } @@ -12315,25 +12461,37 @@ function gatherWithdrawalPending(tx, now, resp, onlyDue = false) { numCoinsTotal, numCoinsWithdrawn, source: wsr.source, - withdrawSessionId: wsr.withdrawSessionId, + withdrawalGroupId: wsr.withdrawalGroupId, + lastError: wsr.lastError, }); }); }); } function gatherProposalPending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.proposals).forEach(proposal => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.proposals).forEach((proposal) => { if (proposal.proposalStatus == "proposed" /* PROPOSED */) { if (onlyDue) { return; } - resp.pendingOperations.push({ - type: "proposal-choice" /* ProposalChoice */, - givesLifeness: false, - merchantBaseUrl: proposal.download.contractData.merchantBaseUrl, - proposalId: proposal.proposalId, - proposalTimestamp: proposal.timestamp, - }); + const dl = proposal.download; + if (!dl) { + resp.pendingOperations.push({ + type: "bug" /* Bug */, + message: "proposal is in invalid state", + details: {}, + givesLifeness: false, + }); + } + else { + resp.pendingOperations.push({ + type: "proposal-choice" /* ProposalChoice */, + givesLifeness: false, + merchantBaseUrl: dl.contractData.merchantBaseUrl, + proposalId: proposal.proposalId, + proposalTimestamp: proposal.timestamp, + }); + } } else if (proposal.proposalStatus == "downloading" /* DOWNLOADING */) { resp.nextRetryDelay = updateRetryDelay(resp.nextRetryDelay, now, proposal.retryInfo.nextRetry); @@ -12355,8 +12513,8 @@ function gatherProposalPending(tx, now, resp, onlyDue = false) { }); } function gatherTipPending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.tips).forEach(tip => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.tips).forEach((tip) => { if (tip.pickedUp) { return; } @@ -12377,8 +12535,8 @@ function gatherTipPending(tx, now, resp, onlyDue = false) { }); } function gatherPurchasePending(tx, now, resp, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.purchases).forEach(pr => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.purchases).forEach((pr) => { if (pr.paymentSubmitPending) { resp.nextRetryDelay = updateRetryDelay(resp.nextRetryDelay, now, pr.payRetryInfo.nextRetry); if (!onlyDue || pr.payRetryInfo.nextRetry.t_ms <= now.t_ms) { @@ -12423,24 +12581,47 @@ function gatherPurchasePending(tx, now, resp, onlyDue = false) { }); }); } -function getPendingOperations(ws, onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - const resp = { - nextRetryDelay: { d_ms: Number.MAX_SAFE_INTEGER }, - onlyDue: onlyDue, - pendingOperations: [], - }; +function gatherRecoupPending(tx, now, resp, onlyDue = false) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield tx.iter(dbTypes.Stores.recoupGroups).forEach((rg) => { + if (rg.timestampFinished) { + return; + } + resp.nextRetryDelay = updateRetryDelay(resp.nextRetryDelay, now, rg.retryInfo.nextRetry); + if (onlyDue && rg.retryInfo.nextRetry.t_ms > now.t_ms) { + return; + } + resp.pendingOperations.push({ + type: "recoup" /* Recoup */, + givesLifeness: true, + recoupGroupId: rg.recoupGroupId, + retryInfo: rg.retryInfo, + lastError: rg.lastError, + }); + }); + }); +} +function getPendingOperations(ws, { onlyDue = false } = {}) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const now = time.getTimestampNow(); - yield ws.db.runWithReadTransaction([ + return yield ws.db.runWithReadTransaction([ dbTypes.Stores.exchanges, dbTypes.Stores.reserves, dbTypes.Stores.refreshGroups, dbTypes.Stores.coins, - dbTypes.Stores.withdrawalSession, + dbTypes.Stores.withdrawalGroups, dbTypes.Stores.proposals, dbTypes.Stores.tips, dbTypes.Stores.purchases, - ], (tx) => __awaiter(this, void 0, void 0, function* () { + dbTypes.Stores.recoupGroups, + ], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const walletBalance = yield balance.getBalancesInsideTransaction(ws, tx); + const resp = { + nextRetryDelay: { d_ms: Number.MAX_SAFE_INTEGER }, + onlyDue: onlyDue, + walletBalance, + pendingOperations: [], + }; yield gatherExchangePending(tx, now, resp, onlyDue); yield gatherReservePending(tx, now, resp, onlyDue); yield gatherRefreshPending(tx, now, resp, onlyDue); @@ -12448,8 +12629,9 @@ function getPendingOperations(ws, onlyDue = false) { yield gatherProposalPending(tx, now, resp, onlyDue); yield gatherTipPending(tx, now, resp, onlyDue); yield gatherPurchasePending(tx, now, resp, onlyDue); + yield gatherRecoupPending(tx, now, resp, onlyDue); + return resp; })); - return resp; }); } exports.getPendingOperations = getPendingOperations; @@ -12459,134 +12641,6 @@ exports.getPendingOperations = getPendingOperations; unwrapExports(pending); var pending_1 = pending.getPendingOperations; -var balance = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); - -const Amounts = __importStar(amounts); - -const logger = new logging.Logger("withdraw.ts"); -/** - * Get detailed balance information, sliced by exchange and by currency. - */ -function getBalances(ws) { - return __awaiter(this, void 0, void 0, function* () { - logger.trace("starting to compute balance"); - /** - * Add amount to a balance field, both for - * the slicing by exchange and currency. - */ - function addTo(balance, field, amount, exchange) { - const z = Amounts.getZero(amount.currency); - const balanceIdentity = { - available: z, - paybackAmount: z, - pendingIncoming: z, - pendingPayment: z, - pendingIncomingDirty: z, - pendingIncomingRefresh: z, - pendingIncomingWithdraw: z, - }; - let entryCurr = balance.byCurrency[amount.currency]; - if (!entryCurr) { - balance.byCurrency[amount.currency] = entryCurr = Object.assign({}, balanceIdentity); - } - let entryEx = balance.byExchange[exchange]; - if (!entryEx) { - balance.byExchange[exchange] = entryEx = Object.assign({}, balanceIdentity); - } - entryCurr[field] = Amounts.add(entryCurr[field], amount).amount; - entryEx[field] = Amounts.add(entryEx[field], amount).amount; - } - const balanceStore = { - byCurrency: {}, - byExchange: {}, - }; - yield ws.db.runWithReadTransaction([dbTypes.Stores.coins, dbTypes.Stores.refreshGroups, dbTypes.Stores.reserves, dbTypes.Stores.purchases, dbTypes.Stores.withdrawalSession], (tx) => __awaiter(this, void 0, void 0, function* () { - yield tx.iter(dbTypes.Stores.coins).forEach(c => { - if (c.suspended) { - return; - } - if (c.status === dbTypes.CoinStatus.Fresh) { - addTo(balanceStore, "available", c.currentAmount, c.exchangeBaseUrl); - } - }); - yield tx.iter(dbTypes.Stores.refreshGroups).forEach(r => { - // Don't count finished refreshes, since the refresh already resulted - // in coins being added to the wallet. - if (r.timestampFinished) { - return; - } - for (let i = 0; i < r.oldCoinPubs.length; i++) { - const session = r.refreshSessionPerCoin[i]; - if (session) { - addTo(balanceStore, "pendingIncoming", session.amountRefreshOutput, session.exchangeBaseUrl); - addTo(balanceStore, "pendingIncomingRefresh", session.amountRefreshOutput, session.exchangeBaseUrl); - } - } - }); - yield tx.iter(dbTypes.Stores.withdrawalSession).forEach(wds => { - let w = wds.totalCoinValue; - for (let i = 0; i < wds.planchets.length; i++) { - if (wds.withdrawn[i]) { - const p = wds.planchets[i]; - if (p) { - w = Amounts.sub(w, p.coinValue).amount; - } - } - } - addTo(balanceStore, "pendingIncoming", w, wds.exchangeBaseUrl); - }); - yield tx.iter(dbTypes.Stores.purchases).forEach(t => { - if (t.timestampFirstSuccessfulPay) { - return; - } - for (const c of t.payReq.coins) { - addTo(balanceStore, "pendingPayment", Amounts.parseOrThrow(c.contribution), c.exchange_url); - } - }); - })); - logger.trace("computed balances:", balanceStore); - return balanceStore; - }); -} -exports.getBalances = getBalances; - -}); - -unwrapExports(balance); -var balance_1 = balance.getBalances; - var tip = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler @@ -12603,26 +12657,11 @@ var tip = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); + +const Amounts = tslib_1.__importStar(amounts); @@ -12630,7 +12669,7 @@ const Amounts = __importStar(amounts); function getTipStatus(ws, talerTipUri) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const res = taleruri.parseTipUri(talerTipUri); if (!res) { throw Error("invalid taler://tip URI"); @@ -12646,7 +12685,7 @@ function getTipStatus(ws, talerTipUri) { console.log("resp:", respJson); const tipPickupStatus = talerTypes.codecForTipPickupGetResponse().decode(respJson); console.log("status", tipPickupStatus); - let amount = Amounts.parseOrThrow(tipPickupStatus.amount); + const amount = Amounts.parseOrThrow(tipPickupStatus.amount); let tipRecord = yield ws.db.get(dbTypes.Stores.tips, [ res.merchantTipId, res.merchantOrigin, @@ -12692,8 +12731,8 @@ function getTipStatus(ws, talerTipUri) { } exports.getTipStatus = getTipStatus; function incrementTipRetry(ws, refreshSessionId, err) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.runWithWriteTransaction([dbTypes.Stores.tips], (tx) => __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.tips], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { const t = yield tx.get(dbTypes.Stores.tips, refreshSessionId); if (!t) { return; @@ -12710,15 +12749,15 @@ function incrementTipRetry(ws, refreshSessionId, err) { }); } function processTip(ws, tipId, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const onOpErr = (e) => incrementTipRetry(ws, tipId, e); yield errors.guardOperationException(() => processTipImpl(ws, tipId, forceNow), onOpErr); }); } exports.processTip = processTip; function resetTipRetry(ws, tipId) { - return __awaiter(this, void 0, void 0, function* () { - yield ws.db.mutate(dbTypes.Stores.tips, tipId, x => { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield ws.db.mutate(dbTypes.Stores.tips, tipId, (x) => { if (x.retryInfo.active) { x.retryInfo = dbTypes.initRetryInfo(); } @@ -12727,7 +12766,7 @@ function resetTipRetry(ws, tipId) { }); } function processTipImpl(ws, tipId, forceNow) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (forceNow) { yield resetTipRetry(ws, tipId); } @@ -12742,8 +12781,8 @@ function processTipImpl(ws, tipId, forceNow) { if (!tipRecord.planchets) { yield exchanges.updateExchangeFromUrl(ws, tipRecord.exchangeUrl); const denomsForWithdraw = yield withdraw.getVerifiedWithdrawDenomList(ws, tipRecord.exchangeUrl, tipRecord.amount); - const planchets = yield Promise.all(denomsForWithdraw.map(d => ws.cryptoApi.createTipPlanchet(d))); - yield ws.db.mutate(dbTypes.Stores.tips, tipId, r => { + const planchets = yield Promise.all(denomsForWithdraw.map((d) => ws.cryptoApi.createTipPlanchet(d))); + yield ws.db.mutate(dbTypes.Stores.tips, tipId, (r) => { if (!r.planchets) { r.planchets = planchets; } @@ -12759,7 +12798,7 @@ function processTipImpl(ws, tipId, forceNow) { } console.log("got planchets for tip!"); // Planchets in the form that the merchant expects - const planchetsDetail = tipRecord.planchets.map(p => ({ + const planchetsDetail = tipRecord.planchets.map((p) => ({ coin_ev: p.coinEv, denom_pub_hash: p.denomPubHash, })); @@ -12784,6 +12823,7 @@ function processTipImpl(ws, tipId, forceNow) { const planchets = []; for (let i = 0; i < tipRecord.planchets.length; i++) { const tipPlanchet = tipRecord.planchets[i]; + const coinEvHash = yield ws.cryptoApi.hashEncoded(tipPlanchet.coinEv); const planchet = { blindingKey: tipPlanchet.blindingKey, coinEv: tipPlanchet.coinEv, @@ -12795,29 +12835,30 @@ function processTipImpl(ws, tipId, forceNow) { reservePub: response.reserve_pub, withdrawSig: response.reserve_sigs[i].reserve_sig, isFromTip: true, + coinEvHash, }; planchets.push(planchet); } - const withdrawalSessionId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); - const withdrawalSession = { - denoms: planchets.map(x => x.denomPub), + const withdrawalGroupId = talerCrypto.encodeCrock(talerCrypto.getRandomBytes(32)); + const withdrawalGroup = { + denoms: planchets.map((x) => x.denomPub), exchangeBaseUrl: tipRecord.exchangeUrl, planchets: planchets, source: { - type: "tip", + type: "tip" /* Tip */, tipId: tipRecord.tipId, }, timestampStart: time.getTimestampNow(), - withdrawSessionId: withdrawalSessionId, + withdrawalGroupId: withdrawalGroupId, rawWithdrawalAmount: tipRecord.amount, - withdrawn: planchets.map(x => false), - totalCoinValue: Amounts.sum(planchets.map(p => p.coinValue)).amount, + withdrawn: planchets.map((x) => false), + totalCoinValue: Amounts.sum(planchets.map((p) => p.coinValue)).amount, lastErrorPerCoin: {}, retryInfo: dbTypes.initRetryInfo(), timestampFinish: undefined, lastError: undefined, }; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.tips, dbTypes.Stores.withdrawalSession], (tx) => __awaiter(this, void 0, void 0, function* () { + yield ws.db.runWithWriteTransaction([dbTypes.Stores.tips, dbTypes.Stores.withdrawalGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { const tr = yield tx.get(dbTypes.Stores.tips, tipId); if (!tr) { return; @@ -12828,14 +12869,14 @@ function processTipImpl(ws, tipId, forceNow) { tr.pickedUp = true; tr.retryInfo = dbTypes.initRetryInfo(false); yield tx.put(dbTypes.Stores.tips, tr); - yield tx.put(dbTypes.Stores.withdrawalSession, withdrawalSession); + yield tx.put(dbTypes.Stores.withdrawalGroups, withdrawalGroup); })); - yield withdraw.processWithdrawSession(ws, withdrawalSessionId); + yield withdraw.processWithdrawGroup(ws, withdrawalGroupId); return; }); } function acceptTip(ws, tipId) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const tipRecord = yield ws.db.get(dbTypes.Stores.tips, tipId); if (!tipRecord) { console.log("tip not found"); @@ -12856,96 +12897,6 @@ var tip_1 = tip.getTipStatus; var tip_2 = tip.processTip; var tip_3 = tip.acceptTip; -var payback_1 = createCommonjsModule(function (module, exports) { -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); - - - - -const logger = new logging.Logger("payback.ts"); -function payback(ws, coinPub) { - return __awaiter(this, void 0, void 0, function* () { - let coin = yield ws.db.get(dbTypes.Stores.coins, coinPub); - if (!coin) { - throw Error(`Coin ${coinPub} not found, can't request payback`); - } - const reservePub = coin.reservePub; - if (!reservePub) { - throw Error(`Can't request payback for a refreshed coin`); - } - const reserve = yield ws.db.get(dbTypes.Stores.reserves, reservePub); - if (!reserve) { - throw Error(`Reserve of coin ${coinPub} not found`); - } - switch (coin.status) { - case dbTypes.CoinStatus.Dormant: - throw Error(`Can't do payback for coin ${coinPub} since it's dormant`); - } - coin.status = dbTypes.CoinStatus.Dormant; - // Even if we didn't get the payback yet, we suspend withdrawal, since - // technically we might update reserve status before we get the response - // from the reserve for the payback request. - reserve.hasPayback = true; - yield ws.db.runWithWriteTransaction([dbTypes.Stores.coins, dbTypes.Stores.reserves], (tx) => __awaiter(this, void 0, void 0, function* () { - yield tx.put(dbTypes.Stores.coins, coin); - yield tx.put(dbTypes.Stores.reserves, reserve); - })); - ws.notify({ - type: "payback-started" /* PaybackStarted */, - }); - const paybackRequest = yield ws.cryptoApi.createPaybackRequest(coin); - const reqUrl = new URL("payback", coin.exchangeBaseUrl); - const resp = yield ws.http.postJson(reqUrl.href, paybackRequest); - if (resp.status !== 200) { - throw Error(); - } - const paybackConfirmation = talerTypes.codecForRecoupConfirmation().decode(yield resp.json()); - if (paybackConfirmation.reserve_pub !== coin.reservePub) { - throw Error(`Coin's reserve doesn't match reserve on payback`); - } - coin = yield ws.db.get(dbTypes.Stores.coins, coinPub); - if (!coin) { - throw Error(`Coin ${coinPub} not found, can't confirm payback`); - } - coin.status = dbTypes.CoinStatus.Dormant; - yield ws.db.put(dbTypes.Stores.coins, coin); - ws.notify({ - type: "payback-finished" /* PaybackFinished */, - }); - yield exchanges.updateExchangeFromUrl(ws, coin.exchangeBaseUrl, true); - }); -} -exports.payback = payback; - -}); - -unwrapExports(payback_1); -var payback_2 = payback_1.payback; - var wallet = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler @@ -12962,24 +12913,9 @@ var wallet = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); + + @@ -13001,6 +12937,7 @@ const withdraw_2 = withdraw; + const builtinCurrencies = [ { auditors: [ @@ -13043,7 +12980,7 @@ class Wallet { * Execute one operation based on the pending operation info record. */ processOnePendingOperation(pending, forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { console.log("running pending", pending); switch (pending.type) { case "bug" /* Bug */: @@ -13059,7 +12996,7 @@ class Wallet { yield reserves.processReserve(this.ws, pending.reservePub, forceNow); break; case "withdraw" /* Withdraw */: - yield withdraw_2.processWithdrawSession(this.ws, pending.withdrawSessionId, forceNow); + yield withdraw_2.processWithdrawGroup(this.ws, pending.withdrawalGroupId, forceNow); break; case "proposal-choice" /* ProposalChoice */: // Nothing to do, user needs to accept/reject @@ -13082,6 +13019,9 @@ class Wallet { case "refund-apply" /* RefundApply */: yield refund.processPurchaseApplyRefund(this.ws, pending.proposalId, forceNow); break; + case "recoup" /* Recoup */: + yield recoup.processRecoupGroup(this.ws, pending.recoupGroupId, forceNow); + break; default: assertUnreachable_1.assertUnreachable(pending); } @@ -13091,15 +13031,20 @@ class Wallet { * Process pending operations. */ runPending(forceNow = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const onlyDue = !forceNow; - const pendingOpsResponse = yield this.getPendingOperations(onlyDue); + const pendingOpsResponse = yield this.getPendingOperations({ onlyDue }); for (const p of pendingOpsResponse.pendingOperations) { try { yield this.processOnePendingOperation(p, forceNow); } catch (e) { - console.error(e); + if (e instanceof errors.OperationFailedAndReportedError) { + console.error("Operation failed:", JSON.stringify(e.operationError, undefined, 2)); + } + else { + console.error(e); + } } } }); @@ -13110,17 +13055,17 @@ class Wallet { * returns without resolving to an exception. */ runUntilDone() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const p = new Promise((resolve, reject) => { // Run this asynchronously - this.addNotificationListener(n => { + this.addNotificationListener((n) => { if (n.type === "waiting-for-retry" /* WaitingForRetry */ && n.numGivingLiveness == 0) { logger.trace("no liveness-giving operations left, returning"); resolve(); } }); - this.runRetryLoop().catch(e => { + this.runRetryLoop().catch((e) => { console.log("exception in wallet retry loop"); reject(e); }); @@ -13129,22 +13074,22 @@ class Wallet { }); } /** - * Run the wallet until there are no more pending operations that give - * liveness left. The wallet will be in a stopped state when this function - * returns without resolving to an exception. - */ + * Run the wallet until there are no more pending operations that give + * liveness left. The wallet will be in a stopped state when this function + * returns without resolving to an exception. + */ runUntilDoneAndStop() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const p = new Promise((resolve, reject) => { // Run this asynchronously - this.addNotificationListener(n => { + this.addNotificationListener((n) => { if (n.type === "waiting-for-retry" /* WaitingForRetry */ && n.numGivingLiveness == 0) { logger.trace("no liveness-giving operations left, stopping"); this.stop(); } }); - this.runRetryLoop().catch(e => { + this.runRetryLoop().catch((e) => { console.log("exception in wallet retry loop"); reject(e); }); @@ -13157,9 +13102,9 @@ class Wallet { * a loop until the wallet is stopped explicitly. */ runRetryLoop() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { // Make sure we only run one main loop at a time. - return this.memoRunRetryLoop.memo(() => __awaiter(this, void 0, void 0, function* () { + return this.memoRunRetryLoop.memo(() => tslib_1.__awaiter(this, void 0, void 0, function* () { try { yield this.runRetryLoopImpl(); } @@ -13171,13 +13116,13 @@ class Wallet { }); } runRetryLoopImpl() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { while (!this.stopped) { console.log("running wallet retry loop iteration"); - let pending = yield this.getPendingOperations(true); + const pending = yield this.getPendingOperations({ onlyDue: true }); console.log("pending ops", JSON.stringify(pending, undefined, 2)); if (pending.pendingOperations.length === 0) { - const allPending = yield this.getPendingOperations(false); + const allPending = yield this.getPendingOperations({ onlyDue: false }); let numPending = 0; let numGivingLiveness = 0; for (const p of allPending.pendingOperations) { @@ -13229,16 +13174,16 @@ class Wallet { * already been applied. */ fillDefaults() { - return __awaiter(this, void 0, void 0, function* () { - yield this.db.runWithWriteTransaction([dbTypes.Stores.config, dbTypes.Stores.currencies], (tx) => __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield this.db.runWithWriteTransaction([dbTypes.Stores.config, dbTypes.Stores.currencies], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { let applied = false; - yield tx.iter(dbTypes.Stores.config).forEach(x => { + yield tx.iter(dbTypes.Stores.config).forEach((x) => { if (x.key == "currencyDefaultsApplied" && x.value == true) { applied = true; } }); if (!applied) { - for (let c of builtinCurrencies) { + for (const c of builtinCurrencies) { yield tx.put(dbTypes.Stores.currencies, c); } } @@ -13252,7 +13197,7 @@ class Wallet { * yet send to the merchant. */ preparePayForUri(talerPayUri) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return pay.preparePayForUri(this.ws, talerPayUri); }); } @@ -13260,7 +13205,7 @@ class Wallet { * Add a contract to the wallet and sign coins, and send them. */ confirmPay(proposalId, sessionIdOverride) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return yield pay.confirmPay(this.ws, proposalId, sessionIdOverride); } @@ -13277,7 +13222,7 @@ class Wallet { * state DORMANT. */ processReserve(reservePub) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return yield reserves.processReserve(this.ws, reservePub); } @@ -13293,7 +13238,7 @@ class Wallet { * audited nor trusted already. */ createReserve(req) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return reserves_2.createReserve(this.ws, req); } @@ -13312,7 +13257,7 @@ class Wallet { * an unconfirmed reserve should be hidden. */ confirmReserve(req) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return reserves_2.confirmReserve(this.ws, req); } @@ -13325,12 +13270,12 @@ class Wallet { * Check if and how an exchange is trusted and/or audited. */ getExchangeTrust(exchangeInfo) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return exchanges.getExchangeTrust(this.ws, exchangeInfo); }); } getWithdrawDetailsForUri(talerWithdrawUri, maybeSelectedExchange) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return withdraw.getWithdrawDetailsForUri(this.ws, talerWithdrawUri, maybeSelectedExchange); }); } @@ -13340,7 +13285,7 @@ class Wallet { * exchange entry in then DB. */ updateExchangeFromUrl(baseUrl, force = false) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return exchanges.updateExchangeFromUrl(this.ws, baseUrl, force); } @@ -13353,14 +13298,14 @@ class Wallet { * Get detailed balance information, sliced by exchange and by currency. */ getBalances() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.ws.memoGetBalance.memo(() => balance.getBalances(this.ws)); }); } refresh(oldCoinPub) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { - const refreshGroupId = yield this.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups], (tx) => __awaiter(this, void 0, void 0, function* () { + const refreshGroupId = yield this.db.runWithWriteTransaction([dbTypes.Stores.refreshGroups], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield refresh.createRefreshGroup(tx, [{ coinPub: oldCoinPub }], "manual" /* Manual */); })); yield refresh.processRefreshGroup(this.ws, refreshGroupId.refreshGroupId); @@ -13371,7 +13316,7 @@ class Wallet { }); } findExchange(exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.db.get(dbTypes.Stores.exchanges, exchangeBaseUrl); }); } @@ -13379,22 +13324,22 @@ class Wallet { * Retrive the full event history for this wallet. */ getHistory(historyQuery) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return history.getHistory(this.ws, historyQuery); }); } - getPendingOperations(onlyDue = false) { - return __awaiter(this, void 0, void 0, function* () { - return this.ws.memoGetPending.memo(() => pending.getPendingOperations(this.ws, onlyDue)); + getPendingOperations({ onlyDue = false } = {}) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + return this.ws.memoGetPending.memo(() => pending.getPendingOperations(this.ws, { onlyDue })); }); } acceptExchangeTermsOfService(exchangeBaseUrl, etag) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return exchanges.acceptExchangeTermsOfService(this.ws, exchangeBaseUrl, etag); }); } getDenoms(exchangeUrl) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const denoms = yield this.db .iterIndex(dbTypes.Stores.denominations.exchangeBaseUrlIndex, exchangeUrl) .toArray(); @@ -13402,50 +13347,45 @@ class Wallet { }); } getExchanges() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.db.iter(dbTypes.Stores.exchanges).toArray(); }); } getCurrencies() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.db.iter(dbTypes.Stores.currencies).toArray(); }); } updateCurrency(currencyRecord) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { logger.trace("updating currency to", currencyRecord); yield this.db.put(dbTypes.Stores.currencies, currencyRecord); }); } getReserves(exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { - return yield this.db - .iter(dbTypes.Stores.reserves) - .filter(r => r.exchangeBaseUrl === exchangeBaseUrl); + return tslib_1.__awaiter(this, void 0, void 0, function* () { + if (exchangeBaseUrl) { + return yield this.db + .iter(dbTypes.Stores.reserves) + .filter((r) => r.exchangeBaseUrl === exchangeBaseUrl); + } + else { + return yield this.db.iter(dbTypes.Stores.reserves).toArray(); + } }); } getCoinsForExchange(exchangeBaseUrl) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.db .iter(dbTypes.Stores.coins) - .filter(c => c.exchangeBaseUrl === exchangeBaseUrl); + .filter((c) => c.exchangeBaseUrl === exchangeBaseUrl); }); } getCoins() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.db.iter(dbTypes.Stores.coins).toArray(); }); } - payback(coinPub) { - return __awaiter(this, void 0, void 0, function* () { - return payback_1.payback(this.ws, coinPub); - }); - } - getPaybackReserves() { - return __awaiter(this, void 0, void 0, function* () { - return yield this.db.iter(dbTypes.Stores.reserves).filter(r => r.hasPayback); - }); - } /** * Stop ongoing processing. */ @@ -13455,22 +13395,22 @@ class Wallet { this.ws.cryptoApi.stop(); } getSenderWireInfos() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const m = {}; - yield this.db.iter(dbTypes.Stores.exchanges).forEach(x => { + yield this.db.iter(dbTypes.Stores.exchanges).forEach((x) => { const wi = x.wireInfo; if (!wi) { return; } const s = (m[x.baseUrl] = m[x.baseUrl] || new Set()); - Object.keys(wi.feesForType).map(k => s.add(k)); + Object.keys(wi.feesForType).map((k) => s.add(k)); }); const exchangeWireTypes = {}; - Object.keys(m).map(e => { + Object.keys(m).map((e) => { exchangeWireTypes[e] = Array.from(m[e]); }); const senderWiresSet = new Set(); - yield this.db.iter(dbTypes.Stores.senderWires).forEach(x => { + yield this.db.iter(dbTypes.Stores.senderWires).forEach((x) => { senderWiresSet.add(x.paytoUri); }); const senderWires = Array.from(senderWiresSet); @@ -13484,7 +13424,7 @@ class Wallet { * Trigger paying coins back into the user's account. */ returnCoins(req) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { throw Error("not implemented"); }); } @@ -13493,22 +13433,22 @@ class Wallet { * that was involved in the refund. */ applyRefund(talerRefundUri) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return refund.applyRefund(this.ws, talerRefundUri); }); } getPurchase(contractTermsHash) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.db.get(dbTypes.Stores.purchases, contractTermsHash); }); } getFullRefundFees(refundPermissions) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return refund.getFullRefundFees(this.ws, refundPermissions); }); } acceptTip(talerTipUri) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return tip.acceptTip(this.ws, talerTipUri); } @@ -13518,12 +13458,12 @@ class Wallet { }); } getTipStatus(talerTipUri) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return tip.getTipStatus(this.ws, talerTipUri); }); } abortFailedPayment(contractTermsHash) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return pay.abortFailedPayment(this.ws, contractTermsHash); } @@ -13537,7 +13477,7 @@ class Wallet { * confirmation from the bank.). */ handleNotifyReserve() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const reserves = yield this.db.iter(dbTypes.Stores.reserves).toArray(); for (const r of reserves) { if (r.reserveStatus === dbTypes.ReserveRecordStatus.WAIT_CONFIRM_BANK) { @@ -13556,7 +13496,7 @@ class Wallet { * based on the current system time. */ collectGarbage() { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { // FIXME(#5845) // We currently do not garbage-collect the wallet database. This might change // after the feature has been properly re-designed, and we have come up with a @@ -13564,7 +13504,7 @@ class Wallet { }); } acceptWithdrawal(talerWithdrawUri, selectedExchange) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return reserves.createTalerWithdrawReserve(this.ws, talerWithdrawUri, selectedExchange); } @@ -13573,26 +13513,37 @@ class Wallet { } }); } + updateReserve(reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield reserves.forceQueryReserve(this.ws, reservePub); + return yield this.ws.db.get(dbTypes.Stores.reserves, reservePub); + }); + } + getReserve(reservePub) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + return yield this.ws.db.get(dbTypes.Stores.reserves, reservePub); + }); + } refuseProposal(proposalId) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return pay.refuseProposal(this.ws, proposalId); }); } getPurchaseDetails(hc) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const purchase = yield this.db.get(dbTypes.Stores.purchases, hc); if (!purchase) { throw Error("unknown purchase"); } - const refundsDoneAmounts = Object.values(purchase.refundState.refundsDone).map(x => Amounts.parseOrThrow(x.perm.refund_amount)); - const refundsPendingAmounts = Object.values(purchase.refundState.refundsPending).map(x => Amounts.parseOrThrow(x.perm.refund_amount)); - const totalRefundAmount = Amounts.sum([ + const refundsDoneAmounts = Object.values(purchase.refundState.refundsDone).map((x) => amounts.Amounts.parseOrThrow(x.perm.refund_amount)); + const refundsPendingAmounts = Object.values(purchase.refundState.refundsPending).map((x) => amounts.Amounts.parseOrThrow(x.perm.refund_amount)); + const totalRefundAmount = amounts.Amounts.sum([ ...refundsDoneAmounts, ...refundsPendingAmounts, ]).amount; - const refundsDoneFees = Object.values(purchase.refundState.refundsDone).map(x => Amounts.parseOrThrow(x.perm.refund_amount)); - const refundsPendingFees = Object.values(purchase.refundState.refundsPending).map(x => Amounts.parseOrThrow(x.perm.refund_amount)); - const totalRefundFees = Amounts.sum([ + const refundsDoneFees = Object.values(purchase.refundState.refundsDone).map((x) => amounts.Amounts.parseOrThrow(x.perm.refund_amount)); + const refundsPendingFees = Object.values(purchase.refundState.refundsPending).map((x) => amounts.Amounts.parseOrThrow(x.perm.refund_amount)); + const totalRefundFees = amounts.Amounts.sum([ ...refundsDoneFees, ...refundsPendingFees, ]).amount; @@ -13608,6 +13559,66 @@ class Wallet { benchmarkCrypto(repetitions) { return this.ws.cryptoApi.benchmark(repetitions); } + setCoinSuspended(coinPub, suspended) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + yield this.db.runWithWriteTransaction([dbTypes.Stores.coins], (tx) => tslib_1.__awaiter(this, void 0, void 0, function* () { + const c = yield tx.get(dbTypes.Stores.coins, coinPub); + if (!c) { + logger.warn(`coin ${coinPub} not found, won't suspend`); + return; + } + c.suspended = suspended; + yield tx.put(dbTypes.Stores.coins, c); + })); + }); + } + /** + * Dump the public information of coins we have in an easy-to-process format. + */ + dumpCoins() { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const coins = yield this.db.iter(dbTypes.Stores.coins).toArray(); + const coinsJson = { coins: [] }; + for (const c of coins) { + const denom = yield this.db.get(dbTypes.Stores.denominations, [ + c.exchangeBaseUrl, + c.denomPub, + ]); + if (!denom) { + console.error("no denom session found for coin"); + continue; + } + const cs = c.coinSource; + let refreshParentCoinPub; + if (cs.type == "refresh" /* Refresh */) { + refreshParentCoinPub = cs.oldCoinPub; + } + let withdrawalReservePub; + if (cs.type == "withdraw" /* Withdraw */) { + const ws = yield this.db.get(dbTypes.Stores.withdrawalGroups, cs.withdrawalGroupId); + if (!ws) { + console.error("no withdrawal session found for coin"); + continue; + } + if (ws.source.type == "reserve") { + withdrawalReservePub = ws.source.reservePub; + } + } + coinsJson.coins.push({ + coin_pub: c.coinPub, + denom_pub: c.denomPub, + denom_pub_hash: c.denomPubHash, + denom_value: amounts.Amounts.stringify(denom.value), + exchange_base_url: c.exchangeBaseUrl, + refresh_parent_coin_pub: refreshParentCoinPub, + remaining_value: amounts.Amounts.stringify(c.currentAmount), + withdrawal_reserve_pub: withdrawalReservePub, + coin_suspended: c.suspended, + }); + } + return coinsJson; + }); + } } exports.Wallet = Wallet; @@ -13796,7 +13807,7 @@ unwrapExports(errors$1); var errors_1$1 = errors$1.AbortError; var errors_2$1 = errors$1.ConstraintError; var errors_3$1 = errors$1.DataCloneError; -var errors_4 = errors$1.DataError; +var errors_4$1 = errors$1.DataError; var errors_5 = errors$1.InvalidAccessError; var errors_6 = errors$1.InvalidStateError; var errors_7 = errors$1.NotFoundError; @@ -19843,18 +19854,6 @@ var bind = function bind(fn, thisArg) { }; }; -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh - * @license MIT - */ - -var isBuffer = function isBuffer (obj) { - return obj != null && obj.constructor != null && - typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) -}; - /*global toString:true*/ // utils is a library of generic helper functions non-specific to axios @@ -19871,6 +19870,27 @@ function isArray(val) { return toString.call(val) === '[object Array]'; } +/** + * Determine if a value is undefined + * + * @param {Object} val The value to test + * @returns {boolean} True if the value is undefined, otherwise false + */ +function isUndefined(val) { + return typeof val === 'undefined'; +} + +/** + * Determine if a value is a Buffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Buffer, otherwise false + */ +function isBuffer(val) { + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) + && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val); +} + /** * Determine if a value is an ArrayBuffer * @@ -19927,16 +19947,6 @@ function isNumber(val) { return typeof val === 'number'; } -/** - * Determine if a value is undefined - * - * @param {Object} val The value to test - * @returns {boolean} True if the value is undefined, otherwise false - */ -function isUndefined(val) { - return typeof val === 'undefined'; -} - /** * Determine if a value is an Object * @@ -20330,959 +20340,987 @@ var normalizeHeaderName = function normalizeHeaderName(headers, normalizedName) delete headers[name]; } }); -}; - -/** - * Update an Error with the specified config, error code, and response. - * - * @param {Error} error The error to update. - * @param {Object} config The config. - * @param {string} [code] The error code (for example, 'ECONNABORTED'). - * @param {Object} [request] The request. - * @param {Object} [response] The response. - * @returns {Error} The error. - */ -var enhanceError = function enhanceError(error, config, code, request, response) { - error.config = config; - if (code) { - error.code = code; - } - - error.request = request; - error.response = response; - error.isAxiosError = true; - - error.toJSON = function() { - return { - // Standard - message: this.message, - name: this.name, - // Microsoft - description: this.description, - number: this.number, - // Mozilla - fileName: this.fileName, - lineNumber: this.lineNumber, - columnNumber: this.columnNumber, - stack: this.stack, - // Axios - config: this.config, - code: this.code - }; - }; - return error; -}; - -/** - * Create an Error with the specified message, config, error code, request and response. - * - * @param {string} message The error message. - * @param {Object} config The config. - * @param {string} [code] The error code (for example, 'ECONNABORTED'). - * @param {Object} [request] The request. - * @param {Object} [response] The response. - * @returns {Error} The created error. - */ -var createError = function createError(message, config, code, request, response) { - var error = new Error(message); - return enhanceError(error, config, code, request, response); -}; - -/** - * Resolve or reject a Promise based on response status. - * - * @param {Function} resolve A function that resolves the promise. - * @param {Function} reject A function that rejects the promise. - * @param {object} response The response. - */ -var settle = function settle(resolve, reject, response) { - var validateStatus = response.config.validateStatus; - if (!validateStatus || validateStatus(response.status)) { - resolve(response); - } else { - reject(createError( - 'Request failed with status code ' + response.status, - response.config, - null, - response.request, - response - )); - } -}; - -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - -var ms = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} +}; /** - * Short format for `ms`. + * Update an Error with the specified config, error code, and response. * - * @param {Number} ms - * @return {String} - * @api private + * @param {Error} error The error to update. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The error. */ - -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; - } - if (ms >= m) { - return Math.round(ms / m) + 'm'; - } - if (ms >= s) { - return Math.round(ms / s) + 's'; +var enhanceError = function enhanceError(error, config, code, request, response) { + error.config = config; + if (code) { + error.code = code; } - return ms + 'ms'; -} + + error.request = request; + error.response = response; + error.isAxiosError = true; + + error.toJSON = function() { + return { + // Standard + message: this.message, + name: this.name, + // Microsoft + description: this.description, + number: this.number, + // Mozilla + fileName: this.fileName, + lineNumber: this.lineNumber, + columnNumber: this.columnNumber, + stack: this.stack, + // Axios + config: this.config, + code: this.code + }; + }; + return error; +}; /** - * Long format for `ms`. + * Create an Error with the specified message, config, error code, request and response. * - * @param {Number} ms - * @return {String} - * @api private + * @param {string} message The error message. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The created error. */ - -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} +var createError = function createError(message, config, code, request, response) { + var error = new Error(message); + return enhanceError(error, config, code, request, response); +}; /** - * Pluralization helper. + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. */ - -function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; +var settle = function settle(resolve, reject, response) { + var validateStatus = response.config.validateStatus; + if (!validateStatus || validateStatus(response.status)) { + resolve(response); + } else { + reject(createError( + 'Request failed with status code ' + response.status, + response.config, + null, + response.request, + response + )); } - return Math.ceil(ms / n) + ' ' + name + 's'; -} +}; -var debug = createCommonjsModule(function (module, exports) { /** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. + * Determines whether the specified URL is absolute * - * Expose `debug()` as the module. + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false */ - -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = ms; +var isAbsoluteURL = function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); +}; /** - * Active `debug` instances. + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL */ -exports.instances = []; +var combineURLs = function combineURLs(baseURL, relativeURL) { + return relativeURL + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') + : baseURL; +}; /** - * The currently active debug mode names, and names to skip. + * Creates a new URL by combining the baseURL with the requestedURL, + * only when the requestedURL is not already an absolute URL. + * If the requestURL is absolute, this function returns the requestedURL untouched. + * + * @param {string} baseURL The base URL + * @param {string} requestedURL Absolute or relative URL to combine + * @returns {string} The combined full path */ +var buildFullPath = function buildFullPath(baseURL, requestedURL) { + if (baseURL && !isAbsoluteURL(requestedURL)) { + return combineURLs(baseURL, requestedURL); + } + return requestedURL; +}; -exports.names = []; -exports.skips = []; +// Headers whose duplicates are ignored by node +// c.f. https://nodejs.org/api/http.html#http_message_headers +var ignoreDuplicateOf = [ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' +]; /** - * Map of special "%n" handling functions, for the debug "format" argument. + * Parse headers into an object * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} headers Headers needing to be parsed + * @returns {Object} Headers parsed into an object */ +var parseHeaders = function parseHeaders(headers) { + var parsed = {}; + var key; + var val; + var i; + + if (!headers) { return parsed; } + + utils.forEach(headers.split('\n'), function parser(line) { + i = line.indexOf(':'); + key = utils.trim(line.substr(0, i)).toLowerCase(); + val = utils.trim(line.substr(i + 1)); + + if (key) { + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { + return; + } + if (key === 'set-cookie') { + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + }); + + return parsed; +}; + +var isURLSameOrigin = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs have full support of the APIs needed to test + // whether the request URL is of the same origin as current location. + (function standardBrowserEnv() { + var msie = /(msie|trident)/i.test(navigator.userAgent); + var urlParsingNode = document.createElement('a'); + var originURL; + + /** + * Parse a URL to discover it's components + * + * @param {String} url The URL to be parsed + * @returns {Object} + */ + function resolveURL(url) { + var href = url; + + if (msie) { + // IE needs attribute set twice to normalize properties + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') ? + urlParsingNode.pathname : + '/' + urlParsingNode.pathname + }; + } + + originURL = resolveURL(window.location.href); + + /** + * Determine if a URL shares the same origin as the current location + * + * @param {String} requestURL The URL to test + * @returns {boolean} True if URL shares the same origin, otherwise false + */ + return function isURLSameOrigin(requestURL) { + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; + return (parsed.protocol === originURL.protocol && + parsed.host === originURL.host); + }; + })() : + + // Non standard browser envs (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return function isURLSameOrigin() { + return true; + }; + })() +); -exports.formatters = {}; +var cookies = ( + utils.isStandardBrowserEnv() ? -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ + // Standard browser envs support document.cookie + (function standardBrowserEnv() { + return { + write: function write(name, value, expires, path, domain, secure) { + var cookie = []; + cookie.push(name + '=' + encodeURIComponent(value)); -function selectColor(namespace) { - var hash = 0, i; + if (utils.isNumber(expires)) { + cookie.push('expires=' + new Date(expires).toGMTString()); + } - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } + if (utils.isString(path)) { + cookie.push('path=' + path); + } - return exports.colors[Math.abs(hash) % exports.colors.length]; -} + if (utils.isString(domain)) { + cookie.push('domain=' + domain); + } -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ + if (secure === true) { + cookie.push('secure'); + } -function createDebug(namespace) { + document.cookie = cookie.join('; '); + }, - var prevTime; + read: function read(name) { + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); + return (match ? decodeURIComponent(match[3]) : null); + }, - function debug() { - // disabled? - if (!debug.enabled) return; + remove: function remove(name) { + this.write(name, '', Date.now() - 86400000); + } + }; + })() : - var self = debug; + // Non standard browser env (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return { + write: function write() {}, + read: function read() { return null; }, + remove: function remove() {} + }; + })() +); - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; +var xhr = function xhrAdapter(config) { + return new Promise(function dispatchXhrRequest(resolve, reject) { + var requestData = config.data; + var requestHeaders = config.headers; - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; + if (utils.isFormData(requestData)) { + delete requestHeaders['Content-Type']; // Let the browser set it } - args[0] = exports.coerce(args[0]); + var request = new XMLHttpRequest(); - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); + // HTTP basic authentication + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password || ''; + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); } - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); + var fullPath = buildFullPath(config.baseURL, config.url); + request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true); - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); + // Set the request timeout in MS + request.timeout = config.timeout; - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); + // Listen for ready state + request.onreadystatechange = function handleLoad() { + if (!request || request.readyState !== 4) { + return; + } - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } + // The request errored out and we didn't get a response, this will be + // handled by onerror instead + // With one exception: request that using file: protocol, most browsers + // will return status as 0 even though it's a successful request + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { + return; + } - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - debug.destroy = destroy; + // Prepare the response + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; + var response = { + data: responseData, + status: request.status, + statusText: request.statusText, + headers: responseHeaders, + config: config, + request: request + }; - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } + settle(resolve, reject, response); - exports.instances.push(debug); + // Clean up request + request = null; + }; - return debug; -} + // Handle browser request cancellation (as opposed to a manual cancellation) + request.onabort = function handleAbort() { + if (!request) { + return; + } -function destroy () { - var index = exports.instances.indexOf(this); - if (index !== -1) { - exports.instances.splice(index, 1); - return true; - } else { - return false; - } -} + reject(createError('Request aborted', config, 'ECONNABORTED', request)); -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ + // Clean up request + request = null; + }; -function enable(namespaces) { - exports.save(namespaces); + // Handle low level network errors + request.onerror = function handleError() { + // Real errors are hidden from us by the browser + // onerror should only fire if it's a network error + reject(createError('Network Error', config, null, request)); - exports.names = []; - exports.skips = []; + // Clean up request + request = null; + }; - var i; - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; + // Handle timeout + request.ontimeout = function handleTimeout() { + var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded'; + if (config.timeoutErrorMessage) { + timeoutErrorMessage = config.timeoutErrorMessage; + } + reject(createError(timeoutErrorMessage, config, 'ECONNABORTED', + request)); - for (i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } + // Clean up request + request = null; + }; - for (i = 0; i < exports.instances.length; i++) { - var instance = exports.instances[i]; - instance.enabled = exports.enabled(instance.namespace); - } -} + // Add xsrf header + // This is only done if running in a standard browser environment. + // Specifically not if we're in a web worker, or react-native. + if (utils.isStandardBrowserEnv()) { + var cookies$1 = cookies; -/** - * Disable debug output. - * - * @api public - */ + // Add xsrf header + var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? + cookies$1.read(config.xsrfCookieName) : + undefined; -function disable() { - exports.enable(''); -} + if (xsrfValue) { + requestHeaders[config.xsrfHeaderName] = xsrfValue; + } + } -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ + // Add headers to the request + if ('setRequestHeader' in request) { + utils.forEach(requestHeaders, function setRequestHeader(val, key) { + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { + // Remove Content-Type if data is undefined + delete requestHeaders[key]; + } else { + // Otherwise add header to the request + request.setRequestHeader(key, val); + } + }); + } -function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; + // Add withCredentials to request if needed + if (!utils.isUndefined(config.withCredentials)) { + request.withCredentials = !!config.withCredentials; } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; + + // Add responseType to request if needed + if (config.responseType) { + try { + request.responseType = config.responseType; + } catch (e) { + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. + if (config.responseType !== 'json') { + throw e; + } + } } - } - return false; -} -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ + // Handle progress if needed + if (typeof config.onDownloadProgress === 'function') { + request.addEventListener('progress', config.onDownloadProgress); + } -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} -}); -var debug_1 = debug.coerce; -var debug_2 = debug.disable; -var debug_3 = debug.enable; -var debug_4 = debug.enabled; -var debug_5 = debug.humanize; -var debug_6 = debug.instances; -var debug_7 = debug.names; -var debug_8 = debug.skips; -var debug_9 = debug.formatters; + // Not all browsers support upload events + if (typeof config.onUploadProgress === 'function' && request.upload) { + request.upload.addEventListener('progress', config.onUploadProgress); + } -var browser = createCommonjsModule(function (module, exports) { -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (!request) { + return; + } -exports = module.exports = debug; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); + request.abort(); + reject(cancel); + // Clean up request + request = null; + }); + } -/** - * Colors. - */ + if (requestData === undefined) { + requestData = null; + } -exports.colors = [ - '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', - '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', - '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', - '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', - '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', - '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', - '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', - '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', - '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', - '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', - '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' -]; + // Send the request + request.send(requestData); + }); +}; /** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors + * Helpers. */ -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // Internet Explorer and Edge do not support colors. - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; /** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public */ -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; +var ms = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); }; - /** - * Colorize log arguments if enabled. + * Parse the given `str` and return milliseconds. * - * @api public + * @param {String} str + * @return {Number} + * @api private */ -function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit'); - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } } /** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". + * Short format for `ms`. * - * @api public + * @param {Number} ms + * @return {String} + * @api private */ -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; } /** - * Save `namespaces`. + * Long format for `ms`. * - * @param {String} namespaces + * @param {Number} ms + * @return {String} * @api private */ -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; } /** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private + * Pluralization helper. */ -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; +function plural(ms, n, name) { + if (ms < n) { + return; } - - return r; + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; } +var debug = createCommonjsModule(function (module, exports) { /** - * Enable namespaces listed in `localStorage.debug` initially. + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. */ -exports.enable(load()); +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = ms; /** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. + * Active `debug` instances. + */ +exports.instances = []; + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. * - * @return {LocalStorage} + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + +exports.formatters = {}; + +/** + * Select a color. + * @param {String} namespace + * @return {Number} * @api private */ -function localstorage() { - try { - return window.localStorage; - } catch (e) {} +function selectColor(namespace) { + var hash = 0, i; + + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return exports.colors[Math.abs(hash) % exports.colors.length]; } -}); -var browser_1 = browser.log; -var browser_2 = browser.formatArgs; -var browser_3 = browser.save; -var browser_4 = browser.load; -var browser_5 = browser.useColors; -var browser_6 = browser.storage; -var browser_7 = browser.colors; -var hasFlag = (flag, argv) => { - argv = argv || process.argv; - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const pos = argv.indexOf(prefix + flag); - const terminatorPos = argv.indexOf('--'); - return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); -}; +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ -const {env} = process; +function createDebug(namespace) { -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = 1; -} -if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === true || env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === false || env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } -} + var prevTime; -function translateLevel(level) { - if (level === 0) { - return false; - } + function debug() { + // disabled? + if (!debug.enabled) return; - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} + var self = debug; -function supportsColor(stream) { - if (forceColor === 0) { - return 0; - } + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } - if (hasFlag('color=256')) { - return 2; - } + args[0] = exports.coerce(args[0]); - if (stream && !stream.isTTY && forceColor === undefined) { - return 0; - } + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); + } - const min = forceColor || 0; + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); - if (env.TERM === 'dumb') { - return min; - } + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); - if (process.platform === 'win32') { - // Node.js 7.5.0 is the first version of Node.js to include a patch to - // libuv that enables 256 color output on Windows. Anything earlier and it - // won't work. However, here we target Node.js 8 at minimum as it is an LTS - // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows - // release that supports 256 colors. Windows 10 build 14931 is the first release - // that supports 16m/TrueColor. - const osRelease = os.release().split('.'); - if ( - Number(process.versions.node.split('.')[0]) >= 8 && - Number(osRelease[0]) >= 10 && - Number(osRelease[2]) >= 10586 - ) { - return Number(osRelease[2]) >= 14931 ? 3 : 2; - } + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); - return 1; - } + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; - } + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; - return min; - } + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } - if ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; - } + exports.instances.push(debug); - if (env.COLORTERM === 'truecolor') { - return 3; - } + return debug; +} - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); +function destroy () { + var index = exports.instances.indexOf(this); + if (index !== -1) { + exports.instances.splice(index, 1); + return true; + } else { + return false; + } +} - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default - } - } +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ - if (/-256(color)?$/i.test(env.TERM)) { - return 2; - } +function enable(namespaces) { + exports.save(namespaces); - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } + exports.names = []; + exports.skips = []; - if ('COLORTERM' in env) { - return 1; - } + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; - return min; -} + for (i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } -function getSupportLevel(stream) { - const level = supportsColor(stream); - return translateLevel(level); + for (i = 0; i < exports.instances.length; i++) { + var instance = exports.instances[i]; + instance.enabled = exports.enabled(instance.namespace); + } } -var supportsColor_1 = { - supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) -}; +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} -var node = createCommonjsModule(function (module, exports) { /** - * Module dependencies. + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public */ +function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} +}); +var debug_1 = debug.coerce; +var debug_2 = debug.disable; +var debug_3 = debug.enable; +var debug_4 = debug.enabled; +var debug_5 = debug.humanize; +var debug_6 = debug.instances; +var debug_7 = debug.names; +var debug_8 = debug.skips; +var debug_9 = debug.formatters; +var browser = createCommonjsModule(function (module, exports) { /** - * This is the Node.js implementation of `debug()`. + * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug; -exports.init = init; exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); /** * Colors. */ -exports.colors = [ 6, 2, 3, 4, 5, 1 ]; - -try { - var supportsColor = supportsColor_1; - if (supportsColor && supportsColor.level >= 2) { - exports.colors = [ - 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, - 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, - 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, - 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, - 205, 206, 207, 208, 209, 214, 215, 220, 221 - ]; - } -} catch (err) { - // swallow - we only care if `supports-color` is available; it doesn't have to be. -} +exports.colors = [ + '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', + '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', + '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', + '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', + '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', + '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', + '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', + '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', + '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', + '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', + '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' +]; /** - * Build up the default `inspectOpts` object from the environment variables. + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + * TODO: add a `localStorage` variable to explicitly enable/disable colors */ -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); - - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); - - obj[prop] = val; - return obj; -}, {}); +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; + } -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(process.stderr.fd); + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } /** - * Map %o to `util.inspect()`, all on a single line. + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } }; -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ - -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; /** - * Adds ANSI color escape codes if enabled. + * Colorize log arguments if enabled. * * @api public */ -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; +function formatArgs(args) { + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return; - if (useColors) { - var c = this.color; - var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); - var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = getDate() + name + ' ' + args[0]; - } -} + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); -function getDate() { - if (exports.inspectOpts.hideDate) { - return ''; - } else { - return new Date().toISOString() + ' '; - } + args.splice(lastC, 0, c); } /** - * Invokes `util.format()` with the specified arguments and writes to stderr. + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public */ function log() { - return process.stderr.write(util.format.apply(util, arguments) + '\n'); + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); } /** @@ -21293,13 +21331,13 @@ function log() { */ function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} } /** @@ -21310,1093 +21348,1113 @@ function save(namespaces) { */ function load() { - return process.env.DEBUG; -} - -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ - -function init (debug) { - debug.inspectOpts = {}; + var r; + try { + r = exports.storage.debug; + } catch(e) {} - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; } + + return r; } /** - * Enable namespaces listed in `process.env.DEBUG` initially. + * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); -}); -var node_1 = node.init; -var node_2 = node.log; -var node_3 = node.formatArgs; -var node_4 = node.save; -var node_5 = node.load; -var node_6 = node.useColors; -var node_7 = node.colors; -var node_8 = node.inspectOpts; -var src = createCommonjsModule(function (module) { /** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private */ -if (typeof process === 'undefined' || process.type === 'renderer') { - module.exports = browser; -} else { - module.exports = node; +function localstorage() { + try { + return window.localStorage; + } catch (e) {} } }); +var browser_1 = browser.log; +var browser_2 = browser.formatArgs; +var browser_3 = browser.save; +var browser_4 = browser.load; +var browser_5 = browser.useColors; +var browser_6 = browser.storage; +var browser_7 = browser.colors; -var Writable = stream.Writable; -var debug$1 = src("follow-redirects"); +var hasFlag = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +}; -// RFC7231§4.2.1: Of the request methods defined by this specification, -// the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe. -var SAFE_METHODS = { GET: true, HEAD: true, OPTIONS: true, TRACE: true }; +const {env} = process; -// Create handlers that pass events from native requests -var eventHandlers = Object.create(null); -["abort", "aborted", "error", "socket", "timeout"].forEach(function (event) { - eventHandlers[event] = function (arg) { - this._redirectable.emit(event, arg); - }; -}); +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} -// An HTTP(S) request that can be redirected -function RedirectableRequest(options, responseCallback) { - // Initialize the request - Writable.call(this); - options.headers = options.headers || {}; - this._options = options; - this._redirectCount = 0; - this._redirects = []; - this._requestBodyLength = 0; - this._requestBodyBuffers = []; +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} - // Since http.request treats host as an alias of hostname, - // but the url module interprets host as hostname plus port, - // eliminate the host property to avoid confusion. - if (options.host) { - // Use hostname if set, because it has precedence - if (!options.hostname) { - options.hostname = options.host; - } - delete options.host; - } +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if ('GITHUB_ACTIONS' in env) { + return 1; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } - // Attach a callback if passed - if (responseCallback) { - this.on("response", responseCallback); - } + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } - // React to responses of native requests - var self = this; - this._onNativeResponse = function (response) { - self._processResponse(response); - }; + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } - // Complete the URL object when necessary - if (!options.pathname && options.path) { - var searchPos = options.path.indexOf("?"); - if (searchPos < 0) { - options.pathname = options.path; - } - else { - options.pathname = options.path.substring(0, searchPos); - options.search = options.path.substring(searchPos); - } - } + if ('COLORTERM' in env) { + return 1; + } - // Perform the first request - this._performRequest(); + return min; } -RedirectableRequest.prototype = Object.create(Writable.prototype); -// Writes buffered data to the current native request -RedirectableRequest.prototype.write = function (data, encoding, callback) { - // Validate input and shift parameters if necessary - if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) { - throw new Error("data should be a string, Buffer or Uint8Array"); - } - if (typeof encoding === "function") { - callback = encoding; - encoding = null; - } +function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY); + return translateLevel(level); +} - // Ignore empty buffers, since writing them doesn't invoke the callback - // https://github.com/nodejs/node/issues/22066 - if (data.length === 0) { - if (callback) { - callback(); - } - return; - } - // Only write when we don't exceed the maximum body length - if (this._requestBodyLength + data.length <= this._options.maxBodyLength) { - this._requestBodyLength += data.length; - this._requestBodyBuffers.push({ data: data, encoding: encoding }); - this._currentRequest.write(data, encoding, callback); - } - // Error when we exceed the maximum body length - else { - this.emit("error", new Error("Request body larger than maxBodyLength limit")); - this.abort(); - } +var supportsColor_1 = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) }; -// Ends the current native request -RedirectableRequest.prototype.end = function (data, encoding, callback) { - // Shift parameters if necessary - if (typeof data === "function") { - callback = data; - data = encoding = null; - } - else if (typeof encoding === "function") { - callback = encoding; - encoding = null; - } - - // Write data and end - var currentRequest = this._currentRequest; - this.write(data || "", encoding, function () { - currentRequest.end(null, null, callback); - }); -}; +var node = createCommonjsModule(function (module, exports) { +/** + * Module dependencies. + */ -// Sets a header value on the current native request -RedirectableRequest.prototype.setHeader = function (name, value) { - this._options.headers[name] = value; - this._currentRequest.setHeader(name, value); -}; -// Clears a header value on the current native request -RedirectableRequest.prototype.removeHeader = function (name) { - delete this._options.headers[name]; - this._currentRequest.removeHeader(name); -}; -// Proxy all other public ClientRequest methods -[ - "abort", "flushHeaders", "getHeader", - "setNoDelay", "setSocketKeepAlive", "setTimeout", -].forEach(function (method) { - RedirectableRequest.prototype[method] = function (a, b) { - return this._currentRequest[method](a, b); - }; -}); -// Proxy all public ClientRequest properties -["aborted", "connection", "socket"].forEach(function (property) { - Object.defineProperty(RedirectableRequest.prototype, property, { - get: function () { return this._currentRequest[property]; }, - }); -}); +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ -// Executes the next native request (initial or redirect) -RedirectableRequest.prototype._performRequest = function () { - // Load the native protocol - var protocol = this._options.protocol; - var nativeProtocol = this._options.nativeProtocols[protocol]; - if (!nativeProtocol) { - this.emit("error", new Error("Unsupported protocol " + protocol)); - return; - } +exports = module.exports = debug; +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; - // If specified, use the agent corresponding to the protocol - // (HTTP and HTTPS use different types of agents) - if (this._options.agents) { - var scheme = protocol.substr(0, protocol.length - 1); - this._options.agent = this._options.agents[scheme]; - } +/** + * Colors. + */ - // Create the native request - var request = this._currentRequest = - nativeProtocol.request(this._options, this._onNativeResponse); - this._currentUrl = url.format(this._options); +exports.colors = [ 6, 2, 3, 4, 5, 1 ]; - // Set up event handlers - request._redirectable = this; - for (var event in eventHandlers) { - /* istanbul ignore else */ - if (event) { - request.on(event, eventHandlers[event]); - } +try { + var supportsColor = supportsColor_1; + if (supportsColor && supportsColor.level >= 2) { + exports.colors = [ + 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, + 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, + 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 214, 215, 220, 221 + ]; } +} catch (err) { + // swallow - we only care if `supports-color` is available; it doesn't have to be. +} - // End a redirected request - // (The first request must be ended explicitly with RedirectableRequest#end) - if (this._isRedirect) { - // Write the request entity and end. - var i = 0; - var buffers = this._requestBodyBuffers; - (function writeNext() { - if (i < buffers.length) { - var buffer = buffers[i++]; - request.write(buffer.data, buffer.encoding, writeNext); - } - else { - request.end(); - } - }()); - } -}; +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ -// Processes a response from the current native request -RedirectableRequest.prototype._processResponse = function (response) { - // Store the redirected response - if (this._options.trackRedirects) { - this._redirects.push({ - url: this._currentUrl, - headers: response.headers, - statusCode: response.statusCode, - }); - } +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // camel-case + var prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); - // RFC7231§6.4: The 3xx (Redirection) class of status code indicates - // that further action needs to be taken by the user agent in order to - // fulfill the request. If a Location header field is provided, - // the user agent MAY automatically redirect its request to the URI - // referenced by the Location field value, - // even if the specific status code is not understood. - var location = response.headers.location; - if (location && this._options.followRedirects !== false && - response.statusCode >= 300 && response.statusCode < 400) { - // RFC7231§6.4: A client SHOULD detect and intervene - // in cyclical redirections (i.e., "infinite" redirection loops). - if (++this._redirectCount > this._options.maxRedirects) { - this.emit("error", new Error("Max redirects exceeded.")); - return; - } + // coerce string value into JS value + var val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) val = true; + else if (/^(no|off|false|disabled)$/i.test(val)) val = false; + else if (val === 'null') val = null; + else val = Number(val); - // RFC7231§6.4: Automatic redirection needs to done with - // care for methods not known to be safe […], - // since the user might not wish to redirect an unsafe request. - // RFC7231§6.4.7: The 307 (Temporary Redirect) status code indicates - // that the target resource resides temporarily under a different URI - // and the user agent MUST NOT change the request method - // if it performs an automatic redirection to that URI. - var header; - var headers = this._options.headers; - if (response.statusCode !== 307 && !(this._options.method in SAFE_METHODS)) { - this._options.method = "GET"; - // Drop a possible entity and headers related to it - this._requestBodyBuffers = []; - for (header in headers) { - if (/^content-/i.test(header)) { - delete headers[header]; - } - } - } + obj[prop] = val; + return obj; +}, {}); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ - // Drop the Host header, as the redirect might lead to a different host - if (!this._isRedirect) { - for (header in headers) { - if (/^host$/i.test(header)) { - delete headers[header]; - } - } - } +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(process.stderr.fd); +} - // Perform the redirected request - var redirectUrl = url.resolve(this._currentUrl, location); - debug$1("redirecting to", redirectUrl); - Object.assign(this._options, url.parse(redirectUrl)); - this._isRedirect = true; - this._performRequest(); +/** + * Map %o to `util.inspect()`, all on a single line. + */ - // Discard the remainder of the response to avoid waiting for data - response.destroy(); - } - else { - // The response is not a redirect; return it as-is - response.responseUrl = this._currentUrl; - response.redirects = this._redirects; - this.emit("response", response); +exports.formatters.o = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .split('\n').map(function(str) { + return str.trim() + }).join(' '); +}; - // Clean up - this._requestBodyBuffers = []; - } +/** + * Map %o to `util.inspect()`, allowing multiple lines if needed. + */ + +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); }; -// Wraps the key/value object of protocols with redirect functionality -function wrap(protocols) { - // Default settings - var exports = { - maxRedirects: 21, - maxBodyLength: 10 * 1024 * 1024, - }; +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ - // Wrap each protocol - var nativeProtocols = {}; - Object.keys(protocols).forEach(function (scheme) { - var protocol = scheme + ":"; - var nativeProtocol = nativeProtocols[protocol] = protocols[scheme]; - var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol); +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; - // Executes a request, following redirects - wrappedProtocol.request = function (options, callback) { - if (typeof options === "string") { - options = url.parse(options); - options.maxRedirects = exports.maxRedirects; - } - else { - options = Object.assign({ - protocol: protocol, - maxRedirects: exports.maxRedirects, - maxBodyLength: exports.maxBodyLength, - }, options); - } - options.nativeProtocols = nativeProtocols; - assert.equal(options.protocol, protocol, "protocol mismatch"); - debug$1("options", options); - return new RedirectableRequest(options, callback); - }; + if (useColors) { + var c = this.color; + var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); + var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; - // Executes a GET request, following redirects - wrappedProtocol.get = function (options, callback) { - var request = wrappedProtocol.request(options, callback); - request.end(); - return request; - }; - }); - return exports; + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); + } else { + args[0] = getDate() + name + ' ' + args[0]; + } } -// Exports -var followRedirects = wrap({ http: http$1, https: https }); -var wrap_1 = wrap; -followRedirects.wrap = wrap_1; +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } else { + return new Date().toISOString() + ' '; + } +} -var name = "axios"; -var version = "0.19.0"; -var description = "Promise based HTTP client for the browser and node.js"; -var main = "index.js"; -var scripts = { - test: "grunt test && bundlesize", - start: "node ./sandbox/server.js", - build: "NODE_ENV=production grunt build", - preversion: "npm test", - version: "npm run build && grunt version && git add -A dist && git add CHANGELOG.md bower.json package.json", - postversion: "git push && git push --tags", - examples: "node ./examples/server.js", - coveralls: "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", - fix: "eslint --fix lib/**/*.js" -}; -var repository = { - type: "git", - url: "https://github.com/axios/axios.git" -}; -var keywords = [ - "xhr", - "http", - "ajax", - "promise", - "node" -]; -var author = "Matt Zabriskie"; -var license = "MIT"; -var bugs = { - url: "https://github.com/axios/axios/issues" -}; -var homepage = "https://github.com/axios/axios"; -var devDependencies = { - bundlesize: "^0.17.0", - coveralls: "^3.0.0", - "es6-promise": "^4.2.4", - grunt: "^1.0.2", - "grunt-banner": "^0.6.0", - "grunt-cli": "^1.2.0", - "grunt-contrib-clean": "^1.1.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-eslint": "^20.1.0", - "grunt-karma": "^2.0.0", - "grunt-mocha-test": "^0.13.3", - "grunt-ts": "^6.0.0-beta.19", - "grunt-webpack": "^1.0.18", - "istanbul-instrumenter-loader": "^1.0.0", - "jasmine-core": "^2.4.1", - karma: "^1.3.0", - "karma-chrome-launcher": "^2.2.0", - "karma-coverage": "^1.1.1", - "karma-firefox-launcher": "^1.1.0", - "karma-jasmine": "^1.1.1", - "karma-jasmine-ajax": "^0.1.13", - "karma-opera-launcher": "^1.0.0", - "karma-safari-launcher": "^1.0.0", - "karma-sauce-launcher": "^1.2.0", - "karma-sinon": "^1.0.5", - "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^1.7.0", - "load-grunt-tasks": "^3.5.2", - minimist: "^1.2.0", - mocha: "^5.2.0", - sinon: "^4.5.0", - typescript: "^2.8.1", - "url-search-params": "^0.10.0", - webpack: "^1.13.1", - "webpack-dev-server": "^1.14.1" -}; -var browser$1 = { - "./lib/adapters/http.js": "./lib/adapters/xhr.js" -}; -var typings = "./index.d.ts"; -var dependencies = { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" -}; -var bundlesize = [ - { - path: "./dist/axios.min.js", - threshold: "5kB" - } -]; -var _package = { - name: name, - version: version, - description: description, - main: main, - scripts: scripts, - repository: repository, - keywords: keywords, - author: author, - license: license, - bugs: bugs, - homepage: homepage, - devDependencies: devDependencies, - browser: browser$1, - typings: typings, - dependencies: dependencies, - bundlesize: bundlesize -}; +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ -var _package$1 = /*#__PURE__*/Object.freeze({ - __proto__: null, - name: name, - version: version, - description: description, - main: main, - scripts: scripts, - repository: repository, - keywords: keywords, - author: author, - license: license, - bugs: bugs, - homepage: homepage, - devDependencies: devDependencies, - browser: browser$1, - typings: typings, - dependencies: dependencies, - bundlesize: bundlesize, - 'default': _package -}); +function log() { + return process.stderr.write(util.format.apply(util, arguments) + '\n'); +} -var pkg = getCjsExportFromNamespace(_package$1); +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ -var httpFollow = followRedirects.http; -var httpsFollow = followRedirects.https; +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ +function load() { + return process.env.DEBUG; +} +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ +function init (debug) { + debug.inspectOpts = {}; + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} -var isHttps = /https:?/; +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ -/*eslint consistent-return:0*/ -var http_1$1 = function httpAdapter(config) { - return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { - var timer; - var resolve = function resolve(value) { - clearTimeout(timer); - resolvePromise(value); - }; - var reject = function reject(value) { - clearTimeout(timer); - rejectPromise(value); - }; - var data = config.data; - var headers = config.headers; +exports.enable(load()); +}); +var node_1 = node.init; +var node_2 = node.log; +var node_3 = node.formatArgs; +var node_4 = node.save; +var node_5 = node.load; +var node_6 = node.useColors; +var node_7 = node.colors; +var node_8 = node.inspectOpts; - // Set User-Agent (required by some servers) - // Only set header if it hasn't been set in config - // See https://github.com/axios/axios/issues/69 - if (!headers['User-Agent'] && !headers['user-agent']) { - headers['User-Agent'] = 'axios/' + pkg.version; - } +var src = createCommonjsModule(function (module) { +/** + * Detect Electron renderer process, which is node, but we should + * treat as a browser. + */ - if (data && !utils.isStream(data)) { - if (Buffer.isBuffer(data)) ; else if (utils.isArrayBuffer(data)) { - data = Buffer.from(new Uint8Array(data)); - } else if (utils.isString(data)) { - data = Buffer.from(data, 'utf-8'); - } else { - return reject(createError( - 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', - config - )); - } +if (typeof process === 'undefined' || process.type === 'renderer') { + module.exports = browser; +} else { + module.exports = node; +} +}); - // Add Content-Length header if data exists - headers['Content-Length'] = data.length; - } +var Writable = stream.Writable; +var debug$1 = src("follow-redirects"); - // HTTP basic authentication - var auth = undefined; - if (config.auth) { - var username = config.auth.username || ''; - var password = config.auth.password || ''; - auth = username + ':' + password; - } +// RFC7231§4.2.1: Of the request methods defined by this specification, +// the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe. +var SAFE_METHODS = { GET: true, HEAD: true, OPTIONS: true, TRACE: true }; - // Parse url - var parsed = url.parse(config.url); - var protocol = parsed.protocol || 'http:'; +// Create handlers that pass events from native requests +var eventHandlers = Object.create(null); +["abort", "aborted", "error", "socket", "timeout"].forEach(function (event) { + eventHandlers[event] = function (arg) { + this._redirectable.emit(event, arg); + }; +}); - if (!auth && parsed.auth) { - var urlAuth = parsed.auth.split(':'); - var urlUsername = urlAuth[0] || ''; - var urlPassword = urlAuth[1] || ''; - auth = urlUsername + ':' + urlPassword; - } +// An HTTP(S) request that can be redirected +function RedirectableRequest(options, responseCallback) { + // Initialize the request + Writable.call(this); + options.headers = options.headers || {}; + this._options = options; + this._redirectCount = 0; + this._redirects = []; + this._requestBodyLength = 0; + this._requestBodyBuffers = []; - if (auth) { - delete headers.Authorization; + // Since http.request treats host as an alias of hostname, + // but the url module interprets host as hostname plus port, + // eliminate the host property to avoid confusion. + if (options.host) { + // Use hostname if set, because it has precedence + if (!options.hostname) { + options.hostname = options.host; } + delete options.host; + } - var isHttpsRequest = isHttps.test(protocol); - var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; + // Attach a callback if passed + if (responseCallback) { + this.on("response", responseCallback); + } - var options = { - path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''), - method: config.method.toUpperCase(), - headers: headers, - agent: agent, - auth: auth - }; + // React to responses of native requests + var self = this; + this._onNativeResponse = function (response) { + self._processResponse(response); + }; - if (config.socketPath) { - options.socketPath = config.socketPath; - } else { - options.hostname = parsed.hostname; - options.port = parsed.port; + // Complete the URL object when necessary + if (!options.pathname && options.path) { + var searchPos = options.path.indexOf("?"); + if (searchPos < 0) { + options.pathname = options.path; } - - var proxy = config.proxy; - if (!proxy && proxy !== false) { - var proxyEnv = protocol.slice(0, -1) + '_proxy'; - var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; - if (proxyUrl) { - var parsedProxyUrl = url.parse(proxyUrl); - var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY; - var shouldProxy = true; - - if (noProxyEnv) { - var noProxy = noProxyEnv.split(',').map(function trim(s) { - return s.trim(); - }); - - shouldProxy = !noProxy.some(function proxyMatch(proxyElement) { - if (!proxyElement) { - return false; - } - if (proxyElement === '*') { - return true; - } - if (proxyElement[0] === '.' && - parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement && - proxyElement.match(/\./g).length === parsed.hostname.match(/\./g).length) { - return true; - } - - return parsed.hostname === proxyElement; - }); - } - - - if (shouldProxy) { - proxy = { - host: parsedProxyUrl.hostname, - port: parsedProxyUrl.port - }; - - if (parsedProxyUrl.auth) { - var proxyUrlAuth = parsedProxyUrl.auth.split(':'); - proxy.auth = { - username: proxyUrlAuth[0], - password: proxyUrlAuth[1] - }; - } - } - } + else { + options.pathname = options.path.substring(0, searchPos); + options.search = options.path.substring(searchPos); } + } - if (proxy) { - options.hostname = proxy.host; - options.host = proxy.host; - options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : ''); - options.port = proxy.port; - options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path; + // Perform the first request + this._performRequest(); +} +RedirectableRequest.prototype = Object.create(Writable.prototype); - // Basic proxy authorization - if (proxy.auth) { - var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64'); - options.headers['Proxy-Authorization'] = 'Basic ' + base64; - } - } +// Writes buffered data to the current native request +RedirectableRequest.prototype.write = function (data, encoding, callback) { + // Validate input and shift parameters if necessary + if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) { + throw new Error("data should be a string, Buffer or Uint8Array"); + } + if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } - var transport; - var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true); - if (config.transport) { - transport = config.transport; - } else if (config.maxRedirects === 0) { - transport = isHttpsProxy ? https : http$1; - } else { - if (config.maxRedirects) { - options.maxRedirects = config.maxRedirects; - } - transport = isHttpsProxy ? httpsFollow : httpFollow; + // Ignore empty buffers, since writing them doesn't invoke the callback + // https://github.com/nodejs/node/issues/22066 + if (data.length === 0) { + if (callback) { + callback(); } + return; + } + // Only write when we don't exceed the maximum body length + if (this._requestBodyLength + data.length <= this._options.maxBodyLength) { + this._requestBodyLength += data.length; + this._requestBodyBuffers.push({ data: data, encoding: encoding }); + this._currentRequest.write(data, encoding, callback); + } + // Error when we exceed the maximum body length + else { + this.emit("error", new Error("Request body larger than maxBodyLength limit")); + this.abort(); + } +}; - if (config.maxContentLength && config.maxContentLength > -1) { - options.maxBodyLength = config.maxContentLength; - } +// Ends the current native request +RedirectableRequest.prototype.end = function (data, encoding, callback) { + // Shift parameters if necessary + if (typeof data === "function") { + callback = data; + data = encoding = null; + } + else if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } - // Create the request - var req = transport.request(options, function handleResponse(res) { - if (req.aborted) return; + // Write data and end + var currentRequest = this._currentRequest; + this.write(data || "", encoding, function () { + currentRequest.end(null, null, callback); + }); +}; - // uncompress the response body transparently if required - var stream = res; - switch (res.headers['content-encoding']) { - /*eslint default-case:0*/ - case 'gzip': - case 'compress': - case 'deflate': - // add the unzipper to the body stream processing pipeline - stream = (res.statusCode === 204) ? stream : stream.pipe(zlib.createUnzip()); +// Sets a header value on the current native request +RedirectableRequest.prototype.setHeader = function (name, value) { + this._options.headers[name] = value; + this._currentRequest.setHeader(name, value); +}; - // remove the content-encoding in order to not confuse downstream operations - delete res.headers['content-encoding']; - break; - } +// Clears a header value on the current native request +RedirectableRequest.prototype.removeHeader = function (name) { + delete this._options.headers[name]; + this._currentRequest.removeHeader(name); +}; - // return the last request in case of redirects - var lastRequest = res.req || req; +// Proxy all other public ClientRequest methods +[ + "abort", "flushHeaders", "getHeader", + "setNoDelay", "setSocketKeepAlive", "setTimeout", +].forEach(function (method) { + RedirectableRequest.prototype[method] = function (a, b) { + return this._currentRequest[method](a, b); + }; +}); - var response = { - status: res.statusCode, - statusText: res.statusMessage, - headers: res.headers, - config: config, - request: lastRequest - }; +// Proxy all public ClientRequest properties +["aborted", "connection", "socket"].forEach(function (property) { + Object.defineProperty(RedirectableRequest.prototype, property, { + get: function () { return this._currentRequest[property]; }, + }); +}); - if (config.responseType === 'stream') { - response.data = stream; - settle(resolve, reject, response); - } else { - var responseBuffer = []; - stream.on('data', function handleStreamData(chunk) { - responseBuffer.push(chunk); +// Executes the next native request (initial or redirect) +RedirectableRequest.prototype._performRequest = function () { + // Load the native protocol + var protocol = this._options.protocol; + var nativeProtocol = this._options.nativeProtocols[protocol]; + if (!nativeProtocol) { + this.emit("error", new Error("Unsupported protocol " + protocol)); + return; + } - // make sure the content length is not over the maxContentLength if specified - if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) { - stream.destroy(); - reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', - config, null, lastRequest)); - } - }); + // If specified, use the agent corresponding to the protocol + // (HTTP and HTTPS use different types of agents) + if (this._options.agents) { + var scheme = protocol.substr(0, protocol.length - 1); + this._options.agent = this._options.agents[scheme]; + } - stream.on('error', function handleStreamError(err) { - if (req.aborted) return; - reject(enhanceError(err, config, null, lastRequest)); - }); + // Create the native request + var request = this._currentRequest = + nativeProtocol.request(this._options, this._onNativeResponse); + this._currentUrl = url.format(this._options); - stream.on('end', function handleStreamEnd() { - var responseData = Buffer.concat(responseBuffer); - if (config.responseType !== 'arraybuffer') { - responseData = responseData.toString(config.responseEncoding); - } + // Set up event handlers + request._redirectable = this; + for (var event in eventHandlers) { + /* istanbul ignore else */ + if (event) { + request.on(event, eventHandlers[event]); + } + } - response.data = responseData; - settle(resolve, reject, response); - }); + // End a redirected request + // (The first request must be ended explicitly with RedirectableRequest#end) + if (this._isRedirect) { + // Write the request entity and end. + var i = 0; + var buffers = this._requestBodyBuffers; + (function writeNext() { + if (i < buffers.length) { + var buffer = buffers[i++]; + request.write(buffer.data, buffer.encoding, writeNext); } - }); + else { + request.end(); + } + }()); + } +}; - // Handle errors - req.on('error', function handleRequestError(err) { - if (req.aborted) return; - reject(enhanceError(err, config, null, req)); +// Processes a response from the current native request +RedirectableRequest.prototype._processResponse = function (response) { + // Store the redirected response + if (this._options.trackRedirects) { + this._redirects.push({ + url: this._currentUrl, + headers: response.headers, + statusCode: response.statusCode, }); + } - // Handle request timeout - if (config.timeout) { - timer = setTimeout(function handleRequestTimeout() { - req.abort(); - reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); - }, config.timeout); - } - - if (config.cancelToken) { - // Handle cancellation - config.cancelToken.promise.then(function onCanceled(cancel) { - if (req.aborted) return; - - req.abort(); - reject(cancel); - }); + // RFC7231§6.4: The 3xx (Redirection) class of status code indicates + // that further action needs to be taken by the user agent in order to + // fulfill the request. If a Location header field is provided, + // the user agent MAY automatically redirect its request to the URI + // referenced by the Location field value, + // even if the specific status code is not understood. + var location = response.headers.location; + if (location && this._options.followRedirects !== false && + response.statusCode >= 300 && response.statusCode < 400) { + // RFC7231§6.4: A client SHOULD detect and intervene + // in cyclical redirections (i.e., "infinite" redirection loops). + if (++this._redirectCount > this._options.maxRedirects) { + this.emit("error", new Error("Max redirects exceeded.")); + return; } - // Send the request - if (utils.isStream(data)) { - data.on('error', function handleStreamError(err) { - reject(enhanceError(err, config, null, req)); - }).pipe(req); - } else { - req.end(data); + // RFC7231§6.4: Automatic redirection needs to done with + // care for methods not known to be safe […], + // since the user might not wish to redirect an unsafe request. + // RFC7231§6.4.7: The 307 (Temporary Redirect) status code indicates + // that the target resource resides temporarily under a different URI + // and the user agent MUST NOT change the request method + // if it performs an automatic redirection to that URI. + var header; + var headers = this._options.headers; + if (response.statusCode !== 307 && !(this._options.method in SAFE_METHODS)) { + this._options.method = "GET"; + // Drop a possible entity and headers related to it + this._requestBodyBuffers = []; + for (header in headers) { + if (/^content-/i.test(header)) { + delete headers[header]; + } + } } - }); -}; - -// Headers whose duplicates are ignored by node -// c.f. https://nodejs.org/api/http.html#http_message_headers -var ignoreDuplicateOf = [ - 'age', 'authorization', 'content-length', 'content-type', 'etag', - 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', - 'last-modified', 'location', 'max-forwards', 'proxy-authorization', - 'referer', 'retry-after', 'user-agent' -]; - -/** - * Parse headers into an object - * - * ``` - * Date: Wed, 27 Aug 2014 08:58:49 GMT - * Content-Type: application/json - * Connection: keep-alive - * Transfer-Encoding: chunked - * ``` - * - * @param {String} headers Headers needing to be parsed - * @returns {Object} Headers parsed into an object - */ -var parseHeaders = function parseHeaders(headers) { - var parsed = {}; - var key; - var val; - var i; - if (!headers) { return parsed; } - - utils.forEach(headers.split('\n'), function parser(line) { - i = line.indexOf(':'); - key = utils.trim(line.substr(0, i)).toLowerCase(); - val = utils.trim(line.substr(i + 1)); - - if (key) { - if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { - return; - } - if (key === 'set-cookie') { - parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); - } else { - parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + // Drop the Host header, as the redirect might lead to a different host + if (!this._isRedirect) { + for (header in headers) { + if (/^host$/i.test(header)) { + delete headers[header]; + } } } - }); - - return parsed; -}; -var isURLSameOrigin = ( - utils.isStandardBrowserEnv() ? + // Perform the redirected request + var redirectUrl = url.resolve(this._currentUrl, location); + debug$1("redirecting to", redirectUrl); + Object.assign(this._options, url.parse(redirectUrl)); + this._isRedirect = true; + this._performRequest(); - // Standard browser envs have full support of the APIs needed to test - // whether the request URL is of the same origin as current location. - (function standardBrowserEnv() { - var msie = /(msie|trident)/i.test(navigator.userAgent); - var urlParsingNode = document.createElement('a'); - var originURL; + // Discard the remainder of the response to avoid waiting for data + response.destroy(); + } + else { + // The response is not a redirect; return it as-is + response.responseUrl = this._currentUrl; + response.redirects = this._redirects; + this.emit("response", response); - /** - * Parse a URL to discover it's components - * - * @param {String} url The URL to be parsed - * @returns {Object} - */ - function resolveURL(url) { - var href = url; + // Clean up + this._requestBodyBuffers = []; + } +}; - if (msie) { - // IE needs attribute set twice to normalize properties - urlParsingNode.setAttribute('href', href); - href = urlParsingNode.href; - } +// Wraps the key/value object of protocols with redirect functionality +function wrap(protocols) { + // Default settings + var exports = { + maxRedirects: 21, + maxBodyLength: 10 * 1024 * 1024, + }; - urlParsingNode.setAttribute('href', href); + // Wrap each protocol + var nativeProtocols = {}; + Object.keys(protocols).forEach(function (scheme) { + var protocol = scheme + ":"; + var nativeProtocol = nativeProtocols[protocol] = protocols[scheme]; + var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol); - // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils - return { - href: urlParsingNode.href, - protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', - host: urlParsingNode.host, - search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', - hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', - hostname: urlParsingNode.hostname, - port: urlParsingNode.port, - pathname: (urlParsingNode.pathname.charAt(0) === '/') ? - urlParsingNode.pathname : - '/' + urlParsingNode.pathname - }; + // Executes a request, following redirects + wrappedProtocol.request = function (options, callback) { + if (typeof options === "string") { + options = url.parse(options); + options.maxRedirects = exports.maxRedirects; } + else { + options = Object.assign({ + protocol: protocol, + maxRedirects: exports.maxRedirects, + maxBodyLength: exports.maxBodyLength, + }, options); + } + options.nativeProtocols = nativeProtocols; + assert.equal(options.protocol, protocol, "protocol mismatch"); + debug$1("options", options); + return new RedirectableRequest(options, callback); + }; - originURL = resolveURL(window.location.href); - - /** - * Determine if a URL shares the same origin as the current location - * - * @param {String} requestURL The URL to test - * @returns {boolean} True if URL shares the same origin, otherwise false - */ - return function isURLSameOrigin(requestURL) { - var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; - return (parsed.protocol === originURL.protocol && - parsed.host === originURL.host); - }; - })() : + // Executes a GET request, following redirects + wrappedProtocol.get = function (options, callback) { + var request = wrappedProtocol.request(options, callback); + request.end(); + return request; + }; + }); + return exports; +} - // Non standard browser envs (web workers, react-native) lack needed support. - (function nonStandardBrowserEnv() { - return function isURLSameOrigin() { - return true; - }; - })() -); +// Exports +var followRedirects = wrap({ http: http$1, https: https }); +var wrap_1 = wrap; +followRedirects.wrap = wrap_1; -var cookies = ( - utils.isStandardBrowserEnv() ? +var name = "axios"; +var version = "0.19.2"; +var description = "Promise based HTTP client for the browser and node.js"; +var main = "index.js"; +var scripts = { + test: "grunt test && bundlesize", + start: "node ./sandbox/server.js", + build: "NODE_ENV=production grunt build", + preversion: "npm test", + version: "npm run build && grunt version && git add -A dist && git add CHANGELOG.md bower.json package.json", + postversion: "git push && git push --tags", + examples: "node ./examples/server.js", + coveralls: "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", + fix: "eslint --fix lib/**/*.js" +}; +var repository = { + type: "git", + url: "https://github.com/axios/axios.git" +}; +var keywords = [ + "xhr", + "http", + "ajax", + "promise", + "node" +]; +var author = "Matt Zabriskie"; +var license = "MIT"; +var bugs = { + url: "https://github.com/axios/axios/issues" +}; +var homepage = "https://github.com/axios/axios"; +var devDependencies = { + bundlesize: "^0.17.0", + coveralls: "^3.0.0", + "es6-promise": "^4.2.4", + grunt: "^1.0.2", + "grunt-banner": "^0.6.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.1.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-eslint": "^20.1.0", + "grunt-karma": "^2.0.0", + "grunt-mocha-test": "^0.13.3", + "grunt-ts": "^6.0.0-beta.19", + "grunt-webpack": "^1.0.18", + "istanbul-instrumenter-loader": "^1.0.0", + "jasmine-core": "^2.4.1", + karma: "^1.3.0", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.1", + "karma-firefox-launcher": "^1.1.0", + "karma-jasmine": "^1.1.1", + "karma-jasmine-ajax": "^0.1.13", + "karma-opera-launcher": "^1.0.0", + "karma-safari-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.2.0", + "karma-sinon": "^1.0.5", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^1.7.0", + "load-grunt-tasks": "^3.5.2", + minimist: "^1.2.0", + mocha: "^5.2.0", + sinon: "^4.5.0", + typescript: "^2.8.1", + "url-search-params": "^0.10.0", + webpack: "^1.13.1", + "webpack-dev-server": "^1.14.1" +}; +var browser$1 = { + "./lib/adapters/http.js": "./lib/adapters/xhr.js" +}; +var typings = "./index.d.ts"; +var dependencies = { + "follow-redirects": "1.5.10" +}; +var bundlesize = [ + { + path: "./dist/axios.min.js", + threshold: "5kB" + } +]; +var _package = { + name: name, + version: version, + description: description, + main: main, + scripts: scripts, + repository: repository, + keywords: keywords, + author: author, + license: license, + bugs: bugs, + homepage: homepage, + devDependencies: devDependencies, + browser: browser$1, + typings: typings, + dependencies: dependencies, + bundlesize: bundlesize +}; - // Standard browser envs support document.cookie - (function standardBrowserEnv() { - return { - write: function write(name, value, expires, path, domain, secure) { - var cookie = []; - cookie.push(name + '=' + encodeURIComponent(value)); +var _package$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + name: name, + version: version, + description: description, + main: main, + scripts: scripts, + repository: repository, + keywords: keywords, + author: author, + license: license, + bugs: bugs, + homepage: homepage, + devDependencies: devDependencies, + browser: browser$1, + typings: typings, + dependencies: dependencies, + bundlesize: bundlesize, + 'default': _package +}); - if (utils.isNumber(expires)) { - cookie.push('expires=' + new Date(expires).toGMTString()); - } +var pkg = getCjsExportFromNamespace(_package$1); - if (utils.isString(path)) { - cookie.push('path=' + path); - } +var httpFollow = followRedirects.http; +var httpsFollow = followRedirects.https; - if (utils.isString(domain)) { - cookie.push('domain=' + domain); - } - if (secure === true) { - cookie.push('secure'); - } - document.cookie = cookie.join('; '); - }, - read: function read(name) { - var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); - return (match ? decodeURIComponent(match[3]) : null); - }, - remove: function remove(name) { - this.write(name, '', Date.now() - 86400000); - } - }; - })() : - // Non standard browser env (web workers, react-native) lack needed support. - (function nonStandardBrowserEnv() { - return { - write: function write() {}, - read: function read() { return null; }, - remove: function remove() {} - }; - })() -); +var isHttps = /https:?/; -var xhr = function xhrAdapter(config) { - return new Promise(function dispatchXhrRequest(resolve, reject) { - var requestData = config.data; - var requestHeaders = config.headers; +/*eslint consistent-return:0*/ +var http_1$1 = function httpAdapter(config) { + return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { + var resolve = function resolve(value) { + resolvePromise(value); + }; + var reject = function reject(value) { + rejectPromise(value); + }; + var data = config.data; + var headers = config.headers; - if (utils.isFormData(requestData)) { - delete requestHeaders['Content-Type']; // Let the browser set it + // Set User-Agent (required by some servers) + // Only set header if it hasn't been set in config + // See https://github.com/axios/axios/issues/69 + if (!headers['User-Agent'] && !headers['user-agent']) { + headers['User-Agent'] = 'axios/' + pkg.version; } - var request = new XMLHttpRequest(); + if (data && !utils.isStream(data)) { + if (Buffer.isBuffer(data)) ; else if (utils.isArrayBuffer(data)) { + data = Buffer.from(new Uint8Array(data)); + } else if (utils.isString(data)) { + data = Buffer.from(data, 'utf-8'); + } else { + return reject(createError( + 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', + config + )); + } + + // Add Content-Length header if data exists + headers['Content-Length'] = data.length; + } // HTTP basic authentication + var auth = undefined; if (config.auth) { var username = config.auth.username || ''; var password = config.auth.password || ''; - requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); + auth = username + ':' + password; } - request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); + // Parse url + var fullPath = buildFullPath(config.baseURL, config.url); + var parsed = url.parse(fullPath); + var protocol = parsed.protocol || 'http:'; - // Set the request timeout in MS - request.timeout = config.timeout; + if (!auth && parsed.auth) { + var urlAuth = parsed.auth.split(':'); + var urlUsername = urlAuth[0] || ''; + var urlPassword = urlAuth[1] || ''; + auth = urlUsername + ':' + urlPassword; + } - // Listen for ready state - request.onreadystatechange = function handleLoad() { - if (!request || request.readyState !== 4) { - return; - } + if (auth) { + delete headers.Authorization; + } - // The request errored out and we didn't get a response, this will be - // handled by onerror instead - // With one exception: request that using file: protocol, most browsers - // will return status as 0 even though it's a successful request - if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { - return; - } + var isHttpsRequest = isHttps.test(protocol); + var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; - // Prepare the response - var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; - var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; - var response = { - data: responseData, - status: request.status, - statusText: request.statusText, - headers: responseHeaders, - config: config, - request: request - }; + var options = { + path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''), + method: config.method.toUpperCase(), + headers: headers, + agent: agent, + agents: { http: config.httpAgent, https: config.httpsAgent }, + auth: auth + }; - settle(resolve, reject, response); + if (config.socketPath) { + options.socketPath = config.socketPath; + } else { + options.hostname = parsed.hostname; + options.port = parsed.port; + } - // Clean up request - request = null; - }; + var proxy = config.proxy; + if (!proxy && proxy !== false) { + var proxyEnv = protocol.slice(0, -1) + '_proxy'; + var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; + if (proxyUrl) { + var parsedProxyUrl = url.parse(proxyUrl); + var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY; + var shouldProxy = true; - // Handle browser request cancellation (as opposed to a manual cancellation) - request.onabort = function handleAbort() { - if (!request) { - return; + if (noProxyEnv) { + var noProxy = noProxyEnv.split(',').map(function trim(s) { + return s.trim(); + }); + + shouldProxy = !noProxy.some(function proxyMatch(proxyElement) { + if (!proxyElement) { + return false; + } + if (proxyElement === '*') { + return true; + } + if (proxyElement[0] === '.' && + parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) { + return true; + } + + return parsed.hostname === proxyElement; + }); + } + + + if (shouldProxy) { + proxy = { + host: parsedProxyUrl.hostname, + port: parsedProxyUrl.port + }; + + if (parsedProxyUrl.auth) { + var proxyUrlAuth = parsedProxyUrl.auth.split(':'); + proxy.auth = { + username: proxyUrlAuth[0], + password: proxyUrlAuth[1] + }; + } + } } + } - reject(createError('Request aborted', config, 'ECONNABORTED', request)); + if (proxy) { + options.hostname = proxy.host; + options.host = proxy.host; + options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : ''); + options.port = proxy.port; + options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path; - // Clean up request - request = null; - }; + // Basic proxy authorization + if (proxy.auth) { + var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64'); + options.headers['Proxy-Authorization'] = 'Basic ' + base64; + } + } - // Handle low level network errors - request.onerror = function handleError() { - // Real errors are hidden from us by the browser - // onerror should only fire if it's a network error - reject(createError('Network Error', config, null, request)); + var transport; + var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true); + if (config.transport) { + transport = config.transport; + } else if (config.maxRedirects === 0) { + transport = isHttpsProxy ? https : http$1; + } else { + if (config.maxRedirects) { + options.maxRedirects = config.maxRedirects; + } + transport = isHttpsProxy ? httpsFollow : httpFollow; + } - // Clean up request - request = null; - }; + if (config.maxContentLength && config.maxContentLength > -1) { + options.maxBodyLength = config.maxContentLength; + } - // Handle timeout - request.ontimeout = function handleTimeout() { - reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', - request)); + // Create the request + var req = transport.request(options, function handleResponse(res) { + if (req.aborted) return; - // Clean up request - request = null; - }; + // uncompress the response body transparently if required + var stream = res; + switch (res.headers['content-encoding']) { + /*eslint default-case:0*/ + case 'gzip': + case 'compress': + case 'deflate': + // add the unzipper to the body stream processing pipeline + stream = (res.statusCode === 204) ? stream : stream.pipe(zlib.createUnzip()); - // Add xsrf header - // This is only done if running in a standard browser environment. - // Specifically not if we're in a web worker, or react-native. - if (utils.isStandardBrowserEnv()) { - var cookies$1 = cookies; + // remove the content-encoding in order to not confuse downstream operations + delete res.headers['content-encoding']; + break; + } - // Add xsrf header - var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ? - cookies$1.read(config.xsrfCookieName) : - undefined; + // return the last request in case of redirects + var lastRequest = res.req || req; + + var response = { + status: res.statusCode, + statusText: res.statusMessage, + headers: res.headers, + config: config, + request: lastRequest + }; + + if (config.responseType === 'stream') { + response.data = stream; + settle(resolve, reject, response); + } else { + var responseBuffer = []; + stream.on('data', function handleStreamData(chunk) { + responseBuffer.push(chunk); - if (xsrfValue) { - requestHeaders[config.xsrfHeaderName] = xsrfValue; - } - } + // make sure the content length is not over the maxContentLength if specified + if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) { + stream.destroy(); + reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', + config, null, lastRequest)); + } + }); - // Add headers to the request - if ('setRequestHeader' in request) { - utils.forEach(requestHeaders, function setRequestHeader(val, key) { - if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { - // Remove Content-Type if data is undefined - delete requestHeaders[key]; - } else { - // Otherwise add header to the request - request.setRequestHeader(key, val); - } - }); - } + stream.on('error', function handleStreamError(err) { + if (req.aborted) return; + reject(enhanceError(err, config, null, lastRequest)); + }); - // Add withCredentials to request if needed - if (config.withCredentials) { - request.withCredentials = true; - } + stream.on('end', function handleStreamEnd() { + var responseData = Buffer.concat(responseBuffer); + if (config.responseType !== 'arraybuffer') { + responseData = responseData.toString(config.responseEncoding); + } - // Add responseType to request if needed - if (config.responseType) { - try { - request.responseType = config.responseType; - } catch (e) { - // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. - // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. - if (config.responseType !== 'json') { - throw e; - } + response.data = responseData; + settle(resolve, reject, response); + }); } - } + }); - // Handle progress if needed - if (typeof config.onDownloadProgress === 'function') { - request.addEventListener('progress', config.onDownloadProgress); - } + // Handle errors + req.on('error', function handleRequestError(err) { + if (req.aborted) return; + reject(enhanceError(err, config, null, req)); + }); - // Not all browsers support upload events - if (typeof config.onUploadProgress === 'function' && request.upload) { - request.upload.addEventListener('progress', config.onUploadProgress); + // Handle request timeout + if (config.timeout) { + // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system. + // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET. + // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up. + // And then these socket which be hang up will devoring CPU little by little. + // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect. + req.setTimeout(config.timeout, function handleRequestTimeout() { + req.abort(); + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); + }); } if (config.cancelToken) { // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { - if (!request) { - return; - } + if (req.aborted) return; - request.abort(); + req.abort(); reject(cancel); - // Clean up request - request = null; }); } - if (requestData === undefined) { - requestData = null; - } - // Send the request - request.send(requestData); + if (utils.isStream(data)) { + data.on('error', function handleStreamError(err) { + reject(enhanceError(err, config, null, req)); + }).pipe(req); + } else { + req.end(data); + } }); }; @@ -22412,13 +22470,12 @@ function setContentTypeIfUnset(headers, value) { function getDefaultAdapter() { var adapter; - // Only Node.JS has a process variable that is of [[Class]] process - if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { - // For node use HTTP adapter - adapter = http_1$1; - } else if (typeof XMLHttpRequest !== 'undefined') { + if (typeof XMLHttpRequest !== 'undefined') { // For browsers use XHR adapter adapter = xhr; + } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { + // For node use HTTP adapter + adapter = http_1$1; } return adapter; } @@ -22494,32 +22551,6 @@ utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { var defaults_1 = defaults; -/** - * Determines whether the specified URL is absolute - * - * @param {string} url The URL to test - * @returns {boolean} True if the specified URL is absolute, otherwise false - */ -var isAbsoluteURL = function isAbsoluteURL(url) { - // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). - // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed - // by any combination of letters, digits, plus, period, or hyphen. - return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); -}; - -/** - * Creates a new URL by combining the specified URLs - * - * @param {string} baseURL The base URL - * @param {string} relativeURL The relative URL - * @returns {string} The combined URL - */ -var combineURLs = function combineURLs(baseURL, relativeURL) { - return relativeURL - ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') - : baseURL; -}; - /** * Throws a `Cancel` if cancellation has been requested. */ @@ -22538,11 +22569,6 @@ function throwIfCancellationRequested(config) { var dispatchRequest = function dispatchRequest(config) { throwIfCancellationRequested(config); - // Support baseURL config - if (config.baseURL && !isAbsoluteURL(config.url)) { - config.url = combineURLs(config.baseURL, config.url); - } - // Ensure headers exist config.headers = config.headers || {}; @@ -22557,7 +22583,7 @@ var dispatchRequest = function dispatchRequest(config) { config.headers = utils.merge( config.headers.common || {}, config.headers[config.method] || {}, - config.headers || {} + config.headers ); utils.forEach( @@ -22611,13 +22637,23 @@ var mergeConfig = function mergeConfig(config1, config2) { config2 = config2 || {}; var config = {}; - utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) { + var valueFromConfig2Keys = ['url', 'method', 'params', 'data']; + var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy']; + var defaultToConfig2Keys = [ + 'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', + 'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent', + 'httpsAgent', 'cancelToken', 'socketPath' + ]; + + utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) { if (typeof config2[prop] !== 'undefined') { config[prop] = config2[prop]; } }); - utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) { + utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) { if (utils.isObject(config2[prop])) { config[prop] = utils.deepMerge(config1[prop], config2[prop]); } else if (typeof config2[prop] !== 'undefined') { @@ -22629,13 +22665,25 @@ var mergeConfig = function mergeConfig(config1, config2) { } }); - utils.forEach([ - 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', - 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', - 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength', - 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken', - 'socketPath' - ], function defaultToConfig2(prop) { + utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) { + if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } else if (typeof config1[prop] !== 'undefined') { + config[prop] = config1[prop]; + } + }); + + var axiosKeys = valueFromConfig2Keys + .concat(mergeDeepPropertiesKeys) + .concat(defaultToConfig2Keys); + + var otherKeys = Object + .keys(config2) + .filter(function filterAxiosKeys(key) { + return axiosKeys.indexOf(key) === -1; + }); + + utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) { if (typeof config2[prop] !== 'undefined') { config[prop] = config2[prop]; } else if (typeof config1[prop] !== 'undefined') { @@ -22675,7 +22723,15 @@ Axios.prototype.request = function request(config) { } config = mergeConfig(this.defaults, config); - config.method = config.method ? config.method.toLowerCase() : 'get'; + + // Set config.method + if (config.method) { + config.method = config.method.toLowerCase(); + } else if (this.defaults.method) { + config.method = this.defaults.method.toLowerCase(); + } else { + config.method = 'get'; + } // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; @@ -22888,19 +22944,8 @@ var bank = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * Helper functions to deal with the GNU Taler demo bank. * @@ -22909,8 +22954,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); /** * Imports. */ -const axios_1 = __importDefault(axios$1); - +const axios_1 = tslib_1.__importDefault(axios$1); +/** + * Generate a random alphanumeric ID. Does *not* use cryptographically + * secure randomness. + */ function makeId(length) { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; @@ -22919,17 +22967,23 @@ function makeId(length) { } return result; } +/** + * Helper function to generate the "Authorization" HTTP header. + */ function makeAuth(username, password) { const auth = `${username}:${password}`; const authEncoded = Buffer.from(auth).toString("base64"); return `Basic ${authEncoded}`; } +/** + * Client for the Taler bank access API. + */ class Bank { constructor(bankBaseUrl) { this.bankBaseUrl = bankBaseUrl; } generateWithdrawUri(bankUser, amount) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const body = { amount, }; @@ -22940,7 +22994,7 @@ class Bank { data: body, responseType: "json", headers: { - "Authorization": makeAuth(bankUser.username, bankUser.password), + Authorization: makeAuth(bankUser.username, bankUser.password), }, }); if (resp.status != 200) { @@ -22954,14 +23008,13 @@ class Bank { }); } createReserve(bankUser, amount, reservePub, exchangePaytoUri) { - return __awaiter(this, void 0, void 0, function* () { - const reqUrl = new URL("api/withdraw-headless", this.bankBaseUrl).href; + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reqUrl = new URL("testing/withdraw", this.bankBaseUrl).href; const body = { - auth: { type: "basic" }, username: bankUser, amount, reserve_pub: reservePub, - exchange_wire_detail: exchangePaytoUri, + exchange_payto_uri: exchangePaytoUri, }; const resp = yield axios_1.default({ method: "post", @@ -22969,7 +23022,7 @@ class Bank { data: body, responseType: "json", headers: { - "Authorization": makeAuth(bankUser.username, bankUser.password), + Authorization: makeAuth(bankUser.username, bankUser.password), }, }); if (resp.status != 200) { @@ -22978,8 +23031,8 @@ class Bank { }); } registerRandomUser() { - return __awaiter(this, void 0, void 0, function* () { - const reqUrl = new URL("api/register", this.bankBaseUrl).href; + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const reqUrl = new URL("testing/register", this.bankBaseUrl).href; const randId = makeId(8); const bankUser = { username: `testuser-${randId}`, @@ -22988,7 +23041,7 @@ class Bank { const resp = yield axios_1.default({ method: "post", url: reqUrl, - data: querystring.stringify(bankUser), + data: bankUser, responseType: "json", }); if (resp.status != 200) { @@ -23008,7 +23061,7 @@ var bank_1 = bank.Bank; var cryptoImplementation = createCommonjsModule(function (module, exports) { /* This file is part of GNU Taler - (C) 2019 GNUnet e.V. + (C) 2019-2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23021,16 +23074,10 @@ var cryptoImplementation = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const Amounts = __importStar(amounts); -const timer$1 = __importStar(timer); + + +const timer$1 = tslib_1.__importStar(timer); @@ -23045,8 +23092,10 @@ var SignaturePurpose; SignaturePurpose[SignaturePurpose["WALLET_COIN_MELT"] = 1202] = "WALLET_COIN_MELT"; SignaturePurpose[SignaturePurpose["TEST"] = 4242] = "TEST"; SignaturePurpose[SignaturePurpose["MERCHANT_PAYMENT_OK"] = 1104] = "MERCHANT_PAYMENT_OK"; - SignaturePurpose[SignaturePurpose["WALLET_COIN_PAYBACK"] = 1203] = "WALLET_COIN_PAYBACK"; + SignaturePurpose[SignaturePurpose["WALLET_COIN_RECOUP"] = 1203] = "WALLET_COIN_RECOUP"; SignaturePurpose[SignaturePurpose["WALLET_COIN_LINK"] = 1204] = "WALLET_COIN_LINK"; + SignaturePurpose[SignaturePurpose["EXCHANGE_CONFIRM_RECOUP"] = 1039] = "EXCHANGE_CONFIRM_RECOUP"; + SignaturePurpose[SignaturePurpose["EXCHANGE_CONFIRM_RECOUP_REFRESH"] = 1041] = "EXCHANGE_CONFIRM_RECOUP_REFRESH"; })(SignaturePurpose || (SignaturePurpose = {})); function amountToBuffer(amount) { const buffer = new ArrayBuffer(8 + 4 + 12); @@ -23059,10 +23108,11 @@ function amountToBuffer(amount) { u8buf.set(curr, 8 + 4); return u8buf; } -function timestampToBuffer(ts) { +function timestampRoundedToBuffer(ts) { const b = new ArrayBuffer(8); const v = new DataView(b); - const s = BigInt(ts.t_ms) * BigInt(1000); + const tsRounded = time.timestampTruncateToSecond(ts); + const s = BigInt(tsRounded.t_ms) * BigInt(1000); v.setBigUint64(0, s); return new Uint8Array(b); } @@ -23077,13 +23127,13 @@ class SignaturePurposeBuilder { } build() { let payloadLen = 0; - for (let c of this.chunks) { + for (const c of this.chunks) { payloadLen += c.byteLength; } const buf = new ArrayBuffer(4 + 4 + payloadLen); const u8buf = new Uint8Array(buf); let p = 8; - for (let c of this.chunks) { + for (const c of this.chunks) { u8buf.set(c, p); p += c.byteLength; } @@ -23097,8 +23147,6 @@ function buildSigPS(purposeNum) { return new SignaturePurposeBuilder(purposeNum); } class CryptoImplementation { - constructor() { - } /** * Create a pre-coin of the given denomination to be withdrawn from then given * reserve. @@ -23111,7 +23159,7 @@ class CryptoImplementation { const blindingFactor = talerCrypto.createBlindingKeySecret(); const coinPubHash = talerCrypto.hash(coinKeyPair.eddsaPub); const ev = talerCrypto.rsaBlind(coinPubHash, blindingFactor, denomPub); - const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount; + const amountWithFee = amounts.Amounts.add(req.value, req.feeWithdraw).amount; const denomPubHash = talerCrypto.hash(denomPub); const evHash = talerCrypto.hash(ev); const withdrawRequest = buildSigPS(SignaturePurpose.RESERVE_WITHDRAW) @@ -23132,6 +23180,7 @@ class CryptoImplementation { denomPubHash: talerCrypto.encodeCrock(denomPubHash), reservePub: talerCrypto.encodeCrock(reservePub), withdrawSig: talerCrypto.encodeCrock(sig), + coinEvHash: talerCrypto.encodeCrock(evHash), }; return planchet; } @@ -23156,10 +23205,10 @@ class CryptoImplementation { return tipPlanchet; } /** - * Create and sign a message to request payback for a coin. + * Create and sign a message to recoup a coin. */ - createPaybackRequest(coin) { - const p = buildSigPS(SignaturePurpose.WALLET_COIN_PAYBACK) + createRecoupRequest(coin) { + const p = buildSigPS(SignaturePurpose.WALLET_COIN_RECOUP) .put(talerCrypto.decodeCrock(coin.coinPub)) .put(talerCrypto.decodeCrock(coin.denomPubHash)) .put(talerCrypto.decodeCrock(coin.blindingKey)) @@ -23170,8 +23219,9 @@ class CryptoImplementation { coin_blind_key_secret: coin.blindingKey, coin_pub: coin.coinPub, coin_sig: talerCrypto.encodeCrock(coinSig), - denom_pub: coin.denomPub, + denom_pub_hash: coin.denomPubHash, denom_sig: coin.denomSig, + refreshed: coin.coinSource.type === "refresh" /* Refresh */, }; return paybackRequest; } @@ -23192,8 +23242,8 @@ class CryptoImplementation { isValidWireFee(type, wf, masterPub) { const p = buildSigPS(SignaturePurpose.MASTER_WIRE_FEES) .put(talerCrypto.hash(talerCrypto.stringToBytes(type + "\0"))) - .put(timestampToBuffer(wf.startStamp)) - .put(timestampToBuffer(wf.endStamp)) + .put(timestampRoundedToBuffer(wf.startStamp)) + .put(timestampRoundedToBuffer(wf.endStamp)) .put(amountToBuffer(wf.wireFee)) .put(amountToBuffer(wf.closingFee)) .build(); @@ -23207,10 +23257,10 @@ class CryptoImplementation { isValidDenom(denom, masterPub) { const p = buildSigPS(SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) .put(talerCrypto.decodeCrock(masterPub)) - .put(timestampToBuffer(denom.stampStart)) - .put(timestampToBuffer(denom.stampExpireWithdraw)) - .put(timestampToBuffer(denom.stampExpireDeposit)) - .put(timestampToBuffer(denom.stampExpireLegal)) + .put(timestampRoundedToBuffer(denom.stampStart)) + .put(timestampRoundedToBuffer(denom.stampExpireWithdraw)) + .put(timestampRoundedToBuffer(denom.stampExpireDeposit)) + .put(timestampRoundedToBuffer(denom.stampExpireLegal)) .put(amountToBuffer(denom.value)) .put(amountToBuffer(denom.feeWithdraw)) .put(amountToBuffer(denom.feeDeposit)) @@ -23224,9 +23274,7 @@ class CryptoImplementation { } isValidWireAccount(paytoUri, sig, masterPub) { const h = kdf_1.kdf(64, talerCrypto.stringToBytes("exchange-wire-signature"), talerCrypto.stringToBytes(paytoUri + "\0"), new Uint8Array(0)); - const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS) - .put(h) - .build(); + const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS).put(h).build(); return talerCrypto.eddsaVerify(p, talerCrypto.decodeCrock(sig), talerCrypto.decodeCrock(masterPub)); } /** @@ -23260,8 +23308,8 @@ class CryptoImplementation { const d = buildSigPS(SignaturePurpose.WALLET_COIN_DEPOSIT) .put(talerCrypto.decodeCrock(depositInfo.contractTermsHash)) .put(talerCrypto.decodeCrock(depositInfo.wireInfoHash)) - .put(timestampToBuffer(depositInfo.timestamp)) - .put(timestampToBuffer(depositInfo.refundDeadline)) + .put(timestampRoundedToBuffer(depositInfo.timestamp)) + .put(timestampRoundedToBuffer(depositInfo.refundDeadline)) .put(amountToBuffer(depositInfo.spendAmount)) .put(amountToBuffer(depositInfo.feeDeposit)) .put(talerCrypto.decodeCrock(depositInfo.merchantPub)) @@ -23271,7 +23319,7 @@ class CryptoImplementation { const s = { coin_pub: depositInfo.coinPub, coin_sig: talerCrypto.encodeCrock(coinSig), - contribution: Amounts.toString(depositInfo.spendAmount), + contribution: amounts.Amounts.stringify(depositInfo.spendAmount), denom_pub: depositInfo.denomPub, exchange_url: depositInfo.exchangeBaseUrl, ub_sig: depositInfo.denomSig, @@ -23282,13 +23330,13 @@ class CryptoImplementation { * Create a new refresh session. */ createRefreshSession(exchangeBaseUrl, kappa, meltCoin, newCoinDenoms, meltFee) { - let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency); + let valueWithFee = amounts.Amounts.getZero(newCoinDenoms[0].value.currency); for (const ncd of newCoinDenoms) { - valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw) + valueWithFee = amounts.Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw) .amount; } // melt fee - valueWithFee = Amounts.add(valueWithFee, meltFee).amount; + valueWithFee = amounts.Amounts.add(valueWithFee, meltFee).amount; const sessionHc = talerCrypto.createHashContext(); const transferPubs = []; const transferPrivs = []; @@ -23337,17 +23385,17 @@ class CryptoImplementation { .put(talerCrypto.decodeCrock(meltCoin.coinPub)) .build(); const confirmSig = talerCrypto.eddsaSign(confirmData, talerCrypto.decodeCrock(meltCoin.coinPriv)); - let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency); + let valueOutput = amounts.Amounts.getZero(newCoinDenoms[0].value.currency); for (const denom of newCoinDenoms) { - valueOutput = Amounts.add(valueOutput, denom.value).amount; + valueOutput = amounts.Amounts.add(valueOutput, denom.value).amount; } const refreshSession = { confirmSig: talerCrypto.encodeCrock(confirmSig), exchangeBaseUrl, hash: talerCrypto.encodeCrock(sessionHash), meltCoinPub: meltCoin.coinPub, - newDenomHashes: newCoinDenoms.map(d => d.denomPubHash), - newDenoms: newCoinDenoms.map(d => d.denomPub), + newDenomHashes: newCoinDenoms.map((d) => d.denomPubHash), + newDenoms: newCoinDenoms.map((d) => d.denomPub), norevealIndex: undefined, planchetsForGammas: planchetsForGammas, transferPrivs, @@ -23369,10 +23417,10 @@ class CryptoImplementation { return talerCrypto.encodeCrock(talerCrypto.hash(b)); } /** - * Hash a denomination public key. + * Hash a crockford encoded value. */ - hashDenomPub(denomPub) { - return talerCrypto.encodeCrock(talerCrypto.hash(talerCrypto.decodeCrock(denomPub))); + hashEncoded(encodedBytes) { + return talerCrypto.encodeCrock(talerCrypto.hash(talerCrypto.decodeCrock(encodedBytes))); } signCoinLink(oldCoinPriv, newDenomHash, oldCoinPub, transferPub, coinEv) { const coinEvHash = talerCrypto.hash(talerCrypto.decodeCrock(coinEv)); @@ -23403,7 +23451,7 @@ class CryptoImplementation { let time_eddsa_create = 0; for (let i = 0; i < repetitions; i++) { const start = timer$1.performanceNow(); - const pair = talerCrypto.createEddsaKeyPair(); + talerCrypto.createEddsaKeyPair(); time_eddsa_create += timer$1.performanceNow() - start; } let time_eddsa_sign = 0; @@ -23442,17 +23490,24 @@ unwrapExports(cryptoImplementation); var cryptoImplementation_1 = cryptoImplementation.CryptoImplementation; var nodeThreadWorker = createCommonjsModule(function (module, exports) { -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; +/* + This file is part of TALER + (C) 2016 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ Object.defineProperty(exports, "__esModule", { value: true }); +const os_1 = tslib_1.__importDefault(os); const f = __filename; const workerCode = ` @@ -23501,7 +23556,7 @@ function handleWorkerMessage(msg) { console.error("RPC operation must be string"); return; } - const handleRequest = () => __awaiter(this, void 0, void 0, function* () { + const handleRequest = () => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const impl = new cryptoImplementation.CryptoImplementation(); if (!(operation in impl)) { @@ -23510,6 +23565,7 @@ function handleWorkerMessage(msg) { } try { const result = impl[operation](...args); + // eslint-disable-next-line @typescript-eslint/no-var-requires const worker_threads$1 = worker_threads; const p = worker_threads$1.parentPort; (_a = worker_threads$1.parentPort) === null || _a === void 0 ? void 0 : _a.postMessage; @@ -23525,7 +23581,7 @@ function handleWorkerMessage(msg) { return; } }); - handleRequest().catch(e => { + handleRequest().catch((e) => { console.error("error in node worker", e); }); } @@ -23542,7 +23598,7 @@ class NodeThreadCryptoWorkerFactory { return new NodeThreadCryptoWorker(); } getConcurrency() { - return Math.max(1, os.cpus().length - 1); + return Math.max(1, os_1.default.cpus().length - 1); } } exports.NodeThreadCryptoWorkerFactory = NodeThreadCryptoWorkerFactory; @@ -23551,6 +23607,7 @@ exports.NodeThreadCryptoWorkerFactory = NodeThreadCryptoWorkerFactory; */ class NodeThreadCryptoWorker { constructor() { + // eslint-disable-next-line @typescript-eslint/no-var-requires const worker_threads$1 = worker_threads; this.nodeWorker = new worker_threads$1.Worker(workerCode, { eval: true }); this.nodeWorker.on("error", (err) => { @@ -23653,9 +23710,9 @@ class OriginState { throw Error("assertion failed"); } const d_s = d.d_ms / 1000; - this.tokensSecond = Math.min(MAX_PER_SECOND, this.tokensSecond + (d_s / 1000)); - this.tokensMinute = Math.min(MAX_PER_MINUTE, this.tokensMinute + (d_s / 1000 * 60)); - this.tokensHour = Math.min(MAX_PER_HOUR, this.tokensHour + (d_s / 1000 * 60 * 60)); + this.tokensSecond = Math.min(MAX_PER_SECOND, this.tokensSecond + d_s / 1000); + this.tokensMinute = Math.min(MAX_PER_MINUTE, this.tokensMinute + (d_s / 1000) * 60); + this.tokensHour = Math.min(MAX_PER_HOUR, this.tokensHour + (d_s / 1000) * 60 * 60); this.lastUpdate = now; } /** @@ -23701,7 +23758,7 @@ class RequestThrottler { if (s) { return s; } - const ns = this.perOriginInfo[origin] = new OriginState(); + const ns = (this.perOriginInfo[origin] = new OriginState()); return ns; } /** @@ -23739,22 +23796,14 @@ var NodeHttpLib_1 = createCommonjsModule(function (module, exports) { SPDX-License-Identifier: AGPL3.0-or-later */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Imports. + */ + -const axios_1 = __importDefault(axios$1); +const axios_1 = tslib_1.__importDefault(axios$1); /** * Implementation of the HTTP request library interface for node. */ @@ -23770,31 +23819,24 @@ class NodeHttpLib { this.throttlingEnabled = enabled; } req(method, url, body, opt) { - var _a; - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { if (this.throttlingEnabled && this.throttle.applyThrottle(url)) { throw Error("request throttled"); } - let resp; - try { - resp = yield axios_1.default({ - method, - url: url, - responseType: "text", - headers: (_a = opt) === null || _a === void 0 ? void 0 : _a.headers, - validateStatus: () => true, - transformResponse: (x) => x, - data: body, - }); - } - catch (e) { - throw e; - } + const resp = yield axios_1.default({ + method, + url: url, + responseType: "text", + headers: opt === null || opt === void 0 ? void 0 : opt.headers, + validateStatus: () => true, + transformResponse: (x) => x, + data: body, + }); const respText = resp.data; if (typeof respText !== "string") { throw Error("unexpected response type"); } - const makeJson = () => __awaiter(this, void 0, void 0, function* () { + const makeJson = () => tslib_1.__awaiter(this, void 0, void 0, function* () { let responseJson; try { responseJson = JSON.parse(respText); @@ -23814,18 +23856,18 @@ class NodeHttpLib { return { headers, status: resp.status, - text: () => __awaiter(this, void 0, void 0, function* () { return resp.data; }), + text: () => tslib_1.__awaiter(this, void 0, void 0, function* () { return resp.data; }), json: makeJson, }; }); } get(url, opt) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.req("get", url, undefined, opt); }); } postJson(url, body, opt) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.req("post", url, body, opt); }); } @@ -23853,17 +23895,9 @@ var synchronousWorker = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * The synchronous crypto worker produced by this factory doesn't run in the * background, but actually blocks the caller until the operation is done. @@ -23908,7 +23942,7 @@ class SynchronousCryptoWorker { } } handleRequest(operation, id, args) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const impl = new cryptoImplementation.CryptoImplementation(); if (!(operation in impl)) { console.error(`crypto operation '${operation}' not found`); @@ -23923,7 +23957,7 @@ class SynchronousCryptoWorker { return; } try { - setImmediate(() => this.dispatchMessage({ result, id })); + setTimeout(() => this.dispatchMessage({ result, id }), 0); } catch (e) { console.log("got error during dispatch", e); @@ -23949,7 +23983,7 @@ class SynchronousCryptoWorker { console.error("RPC operation must be string"); return; } - this.handleRequest(operation, id, args).catch(e => { + this.handleRequest(operation, id, args).catch((e) => { console.error("Error while handling crypto request:", e); }); } @@ -23984,23 +24018,8 @@ var helpers$1 = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); + /** * Helpers to create headless wallets. * @author Florian Dold @@ -24011,9 +24030,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); -const amounts$1 = __importStar(amounts); - +const amounts$1 = tslib_1.__importStar(amounts); +const fs_1 = tslib_1.__importDefault(fs); @@ -24024,14 +24043,14 @@ const logger = new logging.Logger("helpers.ts"); * Get a wallet instance with default settings for node. */ function getDefaultNodeWallet(args = {}) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { build.BridgeIDBFactory.enableTracing = false; const myBackend = new build.MemoryBackend(); myBackend.enableTracing = false; const storagePath = args.persistentStoragePath; if (storagePath) { try { - const dbContentStr = fs.readFileSync(storagePath, { + const dbContentStr = fs_1.default.readFileSync(storagePath, { encoding: "utf-8", }); const dbContent = JSON.parse(dbContentStr); @@ -24040,13 +24059,13 @@ function getDefaultNodeWallet(args = {}) { catch (e) { console.error("could not read wallet file"); } - myBackend.afterCommitCallback = () => __awaiter(this, void 0, void 0, function* () { + myBackend.afterCommitCallback = () => tslib_1.__awaiter(this, void 0, void 0, function* () { // Allow caller to stop persisting the wallet. if (args.persistentStoragePath === undefined) { return; } const dbContent = myBackend.exportDump(); - fs.writeFileSync(storagePath, JSON.stringify(dbContent, undefined, 2), { + fs_1.default.writeFileSync(storagePath, JSON.stringify(dbContent, undefined, 2), { encoding: "utf-8", }); }); @@ -24087,7 +24106,7 @@ function getDefaultNodeWallet(args = {}) { } exports.getDefaultNodeWallet = getDefaultNodeWallet; function withdrawTestBalance(myWallet, amount = "TESTKUDOS:10", bankBaseUrl = "https://bank.test.taler.net/", exchangeBaseUrl = "https://exchange.test.taler.net/") { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { const reserveResponse = yield myWallet.createReserve({ amount: amounts$1.parseOrThrow(amount), exchange: exchangeBaseUrl, @@ -24101,9 +24120,13 @@ function withdrawTestBalance(myWallet, amount = "TESTKUDOS:10", bankBaseUrl = "h "x-taler-bank", ]); const donePromise = new Promise((resolve, reject) => { - myWallet.addNotificationListener(n => { - if (n.type === "reserve-depleted" /* ReserveDepleted */ && - n.reservePub === reservePub) { + myWallet.runRetryLoop().catch((x) => { + reject(x); + }); + myWallet.addNotificationListener((n) => { + if (n.type === "withdraw-group-finished" /* WithdrawGroupFinished */ && + n.withdrawalSource.type === "reserve" /* Reserve */ && + n.withdrawalSource.reservePub === reservePub) { resolve(); } }); @@ -24137,19 +24160,11 @@ var android = createCommonjsModule(function (module, exports) { You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", { value: true }); +const fs_1 = tslib_1.__importDefault(fs); // @ts-ignore: special built-in module @@ -24213,8 +24228,8 @@ class AndroidHttpLib { const resp = { headers, status: msg.status, - json: () => __awaiter(this, void 0, void 0, function* () { return JSON.parse(msg.responseText); }), - text: () => __awaiter(this, void 0, void 0, function* () { return msg.responseText; }), + json: () => tslib_1.__awaiter(this, void 0, void 0, function* () { return JSON.parse(msg.responseText); }), + text: () => tslib_1.__awaiter(this, void 0, void 0, function* () { return msg.responseText; }), }; p.resolve(resp); } @@ -24238,19 +24253,19 @@ class AndroidWalletMessageHandler { * Handle a request from the Android wallet. */ handleMessage(operation, id, args) { - return __awaiter(this, void 0, void 0, function* () { + return tslib_1.__awaiter(this, void 0, void 0, function* () { switch (operation) { case "init": { this.walletArgs = { - notifyHandler: () => __awaiter(this, void 0, void 0, function* () { - sendAkonoMessage(JSON.stringify({ type: "notification" })); + notifyHandler: (notification) => tslib_1.__awaiter(this, void 0, void 0, function* () { + sendAkonoMessage(JSON.stringify({ type: "notification", payload: notification })); }), persistentStoragePath: args.persistentStoragePath, httpLib: this.httpLib, }; const w = yield helpers$1.getDefaultNodeWallet(this.walletArgs); this.maybeWallet = w; - w.runRetryLoop().catch(e => { + w.runRetryLoop().catch((e) => { console.error("Error during wallet retry loop", e); }); this.wp.resolve(w); @@ -24314,6 +24329,10 @@ class AndroidWalletMessageHandler { const wallet = yield this.wp.promise; return yield wallet.getWithdrawDetailsForUri(args.talerWithdrawUri, args.selectedExchange); } + case "applyRefund": { + const wallet = yield this.wp.promise; + return yield wallet.applyRefund(args.talerRefundUri); + } case "acceptExchangeTermsOfService": { const wallet = yield this.wp.promise; return yield wallet.acceptExchangeTermsOfService(args.exchangeBaseUrl, args.etag); @@ -24327,7 +24346,7 @@ class AndroidWalletMessageHandler { this.walletArgs = Object.assign({}, oldArgs); if (oldArgs && oldArgs.persistentStoragePath) { try { - fs.unlinkSync(oldArgs.persistentStoragePath); + fs_1.default.unlinkSync(oldArgs.persistentStoragePath); } catch (e) { console.error("Error while deleting the wallet db:", e); @@ -24341,7 +24360,7 @@ class AndroidWalletMessageHandler { this.maybeWallet = undefined; const w = yield helpers$1.getDefaultNodeWallet(this.walletArgs); this.maybeWallet = w; - w.runRetryLoop().catch(e => { + w.runRetryLoop().catch((e) => { console.error("Error during wallet retry loop", e); }); this.wp.resolve(w); @@ -24362,7 +24381,7 @@ function installAndroidWalletListener() { throw new Error(errMsg); } const handler = new AndroidWalletMessageHandler(); - const onMessage = (msgStr) => __awaiter(this, void 0, void 0, function* () { + const onMessage = (msgStr) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (typeof msgStr !== "string") { console.error("expected string as message"); return; @@ -24378,7 +24397,13 @@ function installAndroidWalletListener() { try { const result = yield handler.handleMessage(operation, id, msg.args); console.log(`android listener: sending success response for ${operation} (${id})`); - const respMsg = { type: "response", id, operation, isError: false, result }; + const respMsg = { + type: "response", + id, + operation, + isError: false, + result, + }; sendMessage(JSON.stringify(respMsg)); } catch (e) { -- cgit v1.2.3